Skip to content

Commit cb6dbf0

Browse files
committed
Add font zoom functionality to console view
-Add key listener on text widget of console view which listens ctrl plus and control minus(including numpad +/-) -Zoom in/out of the console view text in steps based on the keys pressed. see #2578
1 parent 519a3b0 commit cb6dbf0

2 files changed

Lines changed: 251 additions & 0 deletions

File tree

debug/org.eclipse.ui.console/src/org/eclipse/ui/console/TextConsolePage.java

Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515

1616
package org.eclipse.ui.console;
1717

18+
import java.nio.charset.StandardCharsets;
1819
import java.util.ArrayList;
20+
import java.util.Base64;
1921
import java.util.HashMap;
2022
import java.util.Map;
2123
import java.util.Objects;
@@ -28,6 +30,7 @@
2830
import org.eclipse.jface.action.IToolBarManager;
2931
import org.eclipse.jface.action.MenuManager;
3032
import org.eclipse.jface.action.Separator;
33+
import org.eclipse.jface.preference.IPreferenceStore;
3134
import org.eclipse.jface.resource.JFaceResources;
3235
import org.eclipse.jface.text.IDocument;
3336
import org.eclipse.jface.text.IFindReplaceTarget;
@@ -36,6 +39,11 @@
3639
import org.eclipse.jface.util.IPropertyChangeListener;
3740
import org.eclipse.jface.util.PropertyChangeEvent;
3841
import org.eclipse.jface.viewers.ISelectionChangedListener;
42+
import org.eclipse.swt.SWT;
43+
import org.eclipse.swt.custom.StyledText;
44+
import org.eclipse.swt.events.KeyListener;
45+
import org.eclipse.swt.graphics.Font;
46+
import org.eclipse.swt.graphics.FontData;
3947
import org.eclipse.swt.widgets.Composite;
4048
import org.eclipse.swt.widgets.Control;
4149
import org.eclipse.swt.widgets.Menu;
@@ -80,9 +88,39 @@ public class TextConsolePage implements IPageBookViewPage, IPropertyChangeListen
8088
private final IConsoleView fConsoleView;
8189
private TextConsoleViewer fViewer;
8290
private MenuManager fMenuManager;
91+
// Font created from persisted preferences; disposed by this page to avoid leaks
92+
private Font fPersistedFont;
8393
protected Map<String, IAction> fGlobalActions = new HashMap<>();
8494
protected ArrayList<String> fSelectionActions = new ArrayList<>();
8595
protected ClearOutputAction fClearOutputAction;
96+
/**
97+
* Data key used to store a custom zoom font on a StyledText widget. The font
98+
* stored under this key is managed per-widget and disposed via a
99+
* DisposeListener on that widget.
100+
*/
101+
private static final String ZOOM_FONT_KEY = TextConsolePage.class.getName() + ".zoomFont"; //$NON-NLS-1$
102+
103+
/**
104+
* Minimum font size for console zoom.
105+
*/
106+
private static final int MIN_FONT_SIZE = 6;
107+
108+
/**
109+
* Maximum font size for console zoom.
110+
*/
111+
private static final int MAX_FONT_SIZE = 72;
112+
113+
/**
114+
* Font size change step for zoom in/out.
115+
*/
116+
private static final int FONT_SIZE_STEP = 1;
117+
118+
/**
119+
* Preference key prefix used to store per-console-type font settings.
120+
* The full key is: console.font.<consoleType>
121+
* The value format is: name|height|style
122+
*/
123+
private static final String FONT_PREF_KEY_PREFIX = "console.font."; //$NON-NLS-1$
86124

87125
// text selection listener, used to update selection dependent actions on selection changes
88126
private final ISelectionChangedListener selectionChangedListener = event -> updateSelectionDependentActions();
@@ -145,6 +183,13 @@ protected void updateSelectionDependentActions() {
145183
@Override
146184
public void createControl(Composite parent) {
147185
fViewer = createViewer(parent);
186+
// Restore font for this console type (if saved)
187+
Font prefFont = loadFontFromPreferences();
188+
if (prefFont != null) {
189+
// keep a reference so we can dispose it when the page is disposed
190+
fPersistedFont = prefFont;
191+
fViewer.setFont(prefFont);
192+
}
148193
fViewer.setConsoleWidth(fConsole.getConsoleWidth());
149194
fViewer.setTabWidth(fConsole.getTabWidth());
150195
fConsole.addPropertyChangeListener(this);
@@ -168,6 +213,12 @@ public void createControl(Composite parent) {
168213

169214
fViewer.getSelectionProvider().addSelectionChangedListener(selectionChangedListener);
170215
fViewer.addTextListener(textListener);
216+
217+
// Install font zoom key listener on the StyledText widget
218+
StyledText textWidget = fViewer.getTextWidget();
219+
if (textWidget != null) {
220+
installFontZoomKeyListener(textWidget);
221+
}
171222
}
172223

173224
@Override
@@ -185,6 +236,11 @@ public void dispose() {
185236
fViewer.getSelectionProvider().removeSelectionChangedListener(selectionChangedListener);
186237
fViewer.removeTextListener(textListener);
187238
fViewer = null;
239+
// Dispose any font created from persisted preferences to avoid resource leaks
240+
if (fPersistedFont != null && !fPersistedFont.isDisposed()) {
241+
fPersistedFont.dispose();
242+
fPersistedFont = null;
243+
}
188244
}
189245

190246
@Override
@@ -393,4 +449,198 @@ public TextConsoleViewer getViewer() {
393449
public void setViewer(TextConsoleViewer viewer) {
394450
this.fViewer = viewer;
395451
}
452+
453+
/**
454+
* Installs the font zoom key listener on the given control.
455+
*
456+
* @param control the control to install the key listener on
457+
*/
458+
private void installFontZoomKeyListener(Control control) {
459+
control.addKeyListener(KeyListener.keyPressedAdapter(event -> {
460+
// Check for Ctrl key first.
461+
if ((event.stateMask & SWT.MOD1) == 0) {
462+
return;
463+
}
464+
465+
// Check for + or - keys (including numpad)
466+
boolean isPlus = event.character == '=' || event.keyCode == SWT.KEYPAD_ADD
467+
|| (event.character == '+' && (event.stateMask & SWT.SHIFT) != 0);
468+
boolean isMinus = event.character == '-' || event.keyCode == SWT.KEYPAD_SUBTRACT;
469+
470+
if (isPlus) {
471+
increaseFontSize();
472+
event.doit = false;
473+
} else if (isMinus) {
474+
decreaseFontSize();
475+
event.doit = false;
476+
}
477+
}));
478+
}
479+
480+
/**
481+
* Increases the font size of the console by one step.
482+
*/
483+
private void increaseFontSize() {
484+
changeFontSize(FONT_SIZE_STEP);
485+
}
486+
487+
/**
488+
* Decreases the font size of the console by one step.
489+
*/
490+
private void decreaseFontSize() {
491+
changeFontSize(-FONT_SIZE_STEP);
492+
}
493+
494+
/**
495+
* Changes the font size of the console by the given delta.
496+
*
497+
* @param delta the amount to change the font size (positive to increase,
498+
* negative to decrease)
499+
*/
500+
private void changeFontSize(int delta) {
501+
StyledText styledText = fViewer.getTextWidget();
502+
if (styledText == null || styledText.isDisposed()) {
503+
return;
504+
}
505+
506+
Font currentFont = styledText.getFont();
507+
if (currentFont == null || currentFont.isDisposed()) {
508+
return;
509+
}
510+
511+
FontData[] fontData = currentFont.getFontData();
512+
if (fontData == null || fontData.length == 0) {
513+
return;
514+
}
515+
516+
int currentHeight = fontData[0].getHeight();
517+
int newHeight = Math.max(MIN_FONT_SIZE, Math.min(MAX_FONT_SIZE, currentHeight + delta));
518+
519+
if (newHeight == currentHeight) {
520+
return;
521+
}
522+
523+
// Copy the existing FontData array and set the new height on each element.
524+
FontData[] newFontData = fontData.clone();
525+
for (FontData fd : newFontData) {
526+
if (fd != null) {
527+
fd.setHeight(newHeight);
528+
}
529+
}
530+
531+
// Get any existing zoom font for this specific StyledText
532+
Font oldZoomFont = (Font) styledText.getData(ZOOM_FONT_KEY);
533+
534+
// Create and set the new font
535+
Font newZoomFont = new Font(styledText.getDisplay(), newFontData);
536+
styledText.setFont(newZoomFont);
537+
538+
// Store the new zoom font on this specific StyledText
539+
styledText.setData(ZOOM_FONT_KEY, newZoomFont);
540+
541+
// Install DisposeListener if this is the first zoom font for this widget
542+
if (oldZoomFont == null) {
543+
styledText.addDisposeListener(e -> {
544+
Font zoomFont = (Font) styledText.getData(ZOOM_FONT_KEY);
545+
if (zoomFont != null && !zoomFont.isDisposed()) {
546+
zoomFont.dispose();
547+
}
548+
});
549+
}
550+
551+
// Dispose the old zoom font for this widget after setting the new one
552+
if (oldZoomFont != null && !oldZoomFont.isDisposed()) {
553+
oldZoomFont.dispose();
554+
}
555+
556+
// Persist the changed font for this console type
557+
saveFontToPreferences(newZoomFont);
558+
}
559+
560+
/**
561+
* Returns the preference key for the font for this console's type.
562+
*/
563+
private String getFontPrefKey() {
564+
String type = getConsole().getType();
565+
if (type == null || type.isEmpty()) {
566+
return FONT_PREF_KEY_PREFIX + "default"; //$NON-NLS-1$
567+
}
568+
return FONT_PREF_KEY_PREFIX + type;
569+
}
570+
571+
/**
572+
* Loads a Font from the preference store for this console type, or null if none.
573+
*/
574+
private Font loadFontFromPreferences() {
575+
IPreferenceStore store = ConsolePlugin.getDefault().getPreferenceStore();
576+
String value = store.getString(getFontPrefKey());
577+
if (value == null || value.isEmpty()) {
578+
return null;
579+
}
580+
// Stored as a comma-separated list of Base64 encoded tokens.
581+
// Each token is either a platform FontData(String) (starting with "1|")
582+
// or our compact format: name|height|style|locale
583+
try {
584+
String[] parts = value.split(","); //$NON-NLS-1$
585+
FontData[] fds = new FontData[parts.length];
586+
for (int i = 0; i < parts.length; i++) {
587+
String decoded = new String(Base64.getDecoder().decode(parts[i]), StandardCharsets.UTF_8);
588+
589+
if (decoded.startsWith("1|")) { //$NON-NLS-1$
590+
// platform-native FontData string
591+
fds[i] = new FontData(decoded);
592+
} else {
593+
// our compact format: name|height|style|locale
594+
String[] fields = decoded.split("\\|", -1); //$NON-NLS-1$
595+
if (fields.length >= 4) {
596+
String name = fields[0];
597+
int height = Integer.parseInt(fields[1]);
598+
int style = Integer.parseInt(fields[2]);
599+
String locale = fields[3];
600+
FontData fd = new FontData(name, height, style);
601+
if (locale != null && !locale.isEmpty()) {
602+
fd.setLocale(locale);
603+
}
604+
fds[i] = fd;
605+
} else {
606+
fds[i] = new FontData(decoded);
607+
}
608+
}
609+
}
610+
return new Font(ConsolePlugin.getStandardDisplay(), fds);
611+
} catch (RuntimeException e) {
612+
return null;
613+
}
614+
}
615+
616+
/**
617+
* Saves the given font into the preference store for this console type.
618+
*/
619+
private void saveFontToPreferences(Font font) {
620+
if (font == null)
621+
return;
622+
FontData[] fds = font.getFontData();
623+
if (fds == null || fds.length == 0)
624+
return;
625+
// Persist only name, height, style and locale in a compact token and Base64-encode it.
626+
StringBuilder sb = new StringBuilder();
627+
for (int i = 0; i < fds.length; i++) {
628+
if (i > 0) {
629+
sb.append(',');
630+
}
631+
FontData fd = fds[i];
632+
String name = fd.getName();
633+
int height = fd.getHeight();
634+
int style = fd.getStyle();
635+
String locale = fd.getLocale();
636+
if (locale == null) {
637+
locale = ""; //$NON-NLS-1$
638+
}
639+
String token = name + "|" + height + "|" + style + "|" + locale; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
640+
String enc = Base64.getEncoder().encodeToString(token.getBytes(StandardCharsets.UTF_8));
641+
sb.append(enc);
642+
}
643+
IPreferenceStore store = ConsolePlugin.getDefault().getPreferenceStore();
644+
store.setValue(getFontPrefKey(), sb.toString());
645+
}
396646
}

debug/org.eclipse.ui.console/src/org/eclipse/ui/internal/console/ConsoleView.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,7 @@ public void dispose() {
441441
localResManager.dispose();
442442
localResManager = null;
443443
}
444+
444445
fConsoleToPageParticipants.clear();
445446
fStack.clear();
446447
fConsoleToPart.clear();

0 commit comments

Comments
 (0)