X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=blobdiff_plain;f=bundles%2Forg.simantics.scl.ui%2Fsrc%2Forg%2Fsimantics%2Fscl%2Fui%2Fconsole%2FAbstractCommandConsole.java;h=ab9e033d020cca6d2537c83a35020c976419a8a7;hp=b4260b47b4a07ea01fc2ee2d94a363ce3f22bdea;hb=1dfeb7d5c49b1391cd9d877e1eddab18995cb151;hpb=969bd23cab98a79ca9101af33334000879fb60c5 diff --git a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/AbstractCommandConsole.java b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/AbstractCommandConsole.java old mode 100755 new mode 100644 index b4260b47b..ab9e033d0 --- a/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/AbstractCommandConsole.java +++ b/bundles/org.simantics.scl.ui/src/org/simantics/scl/ui/console/AbstractCommandConsole.java @@ -11,20 +11,22 @@ import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.preference.IPersistentPreferenceStore; import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.resource.FontDescriptor; +import org.eclipse.jface.resource.FontRegistry; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; +import org.eclipse.jface.util.IPropertyChangeListener; +import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.jface.window.DefaultToolTip; import org.eclipse.jface.window.ToolTip; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; -import org.eclipse.swt.custom.VerifyKeyListener; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.events.VerifyEvent; import org.eclipse.swt.events.VerifyListener; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; @@ -33,11 +35,15 @@ import org.eclipse.swt.layout.FormAttachment; import org.eclipse.swt.layout.FormData; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Sash; +import org.eclipse.ui.PlatformUI; import org.eclipse.ui.preferences.ScopedPreferenceStore; +import org.simantics.scl.runtime.tuple.Tuple2; +import org.slf4j.Logger; /** * A console with input and output area that can be embedded @@ -46,6 +52,11 @@ import org.eclipse.ui.preferences.ScopedPreferenceStore; */ public abstract class AbstractCommandConsole extends Composite { + /** + * Use this option mask to hide and disable the console input field. + */ + public static final int HIDE_INPUT = 1 << 0; + public static final String PLUGIN_ID = "org.simantics.scl.ui"; public static final int COMMAND_HISTORY_SIZE = 50; @@ -53,9 +64,12 @@ public abstract class AbstractCommandConsole extends Composite { public static final int SASH_HEIGHT = 3; LocalResourceManager resourceManager; - + + protected final int options; + StyledText output; Sash sash; + StyledText deco; protected StyledText input; int userInputHeight=0; @@ -63,7 +77,11 @@ public abstract class AbstractCommandConsole extends Composite { protected Color greenColor; protected Color redColor; - + + FontRegistry fontRegistry; + FontDescriptor textFontDescriptor; + Font textFont; + ArrayList commandHistory = new ArrayList(); int commandHistoryPos = 0; @@ -73,29 +91,38 @@ public abstract class AbstractCommandConsole extends Composite { Shell tip = null; Label label = null; */ - - public AbstractCommandConsole(Composite parent, int style) { - super(parent, style); + + public AbstractCommandConsole(Composite parent, int style, int options) { + super(parent, style); + this.options = options; createControl(); } - + @Override public boolean setFocus() { - return input.setFocus(); + return input != null ? input.setFocus() : output.setFocus(); } - + protected boolean canExecuteCommand() { return true; } + protected boolean hasOption(int mask) { + return (options & mask) != 0; + } + private void createControl() { resourceManager = new LocalResourceManager(JFaceResources.getResources(), this); greenColor = resourceManager.createColor(new RGB(0, 128, 0)); redColor = resourceManager.createColor(new RGB(172, 0, 0)); - + + // Initialize current text font + fontRegistry = PlatformUI.getWorkbench().getThemeManager().getCurrentTheme().getFontRegistry(); + fontRegistry.addListener(fontRegistryListener); + FontDescriptor font = FontDescriptor.createFrom( fontRegistry.getFontData("org.simantics.scl.consolefont") ); + setTextFont(font); + setLayout(new FormLayout()); - - Font textFont = new Font(getDisplay(),"Courier New",12,SWT.NONE); // Sash sash = new Sash(this, /*SWT.BORDER |*/ SWT.HORIZONTAL); @@ -115,121 +142,112 @@ public abstract class AbstractCommandConsole extends Composite { // Upper output = new StyledText(this, SWT.MULTI /*| SWT.READ_ONLY*/ | SWT.V_SCROLL | SWT.H_SCROLL); output.setFont(textFont); - { - FormData formData = new FormData(); - formData.top = new FormAttachment(0); - formData.bottom = new FormAttachment(sash); - formData.left = new FormAttachment(0); - formData.right = new FormAttachment(100); - output.setLayoutData(formData); - } + output.setLayoutData( formData(0, sash, 0, 100) ); output.addVerifyListener(new VerifyListener() { @Override public void verifyText(VerifyEvent e) { if(outputModiLock) return; - input.append(e.text); - input.setFocus(); - input.setCaretOffset(input.getText().length()); + if (input != null) { + input.append(e.text); + input.setFocus(); + input.setCaretOffset(input.getText().length()); + } e.doit = false; } }); - // Deco - StyledText deco = new StyledText(this, SWT.MULTI | SWT.READ_ONLY); + if (hasOption(HIDE_INPUT)) { + sash.setLayoutData( formData(new Tuple2(100, 0), null, 0, 100, 0) ); + layout(true); + } else { + createInputArea(); + } + + readPreferences(); + + addListener(SWT.Dispose, event -> { + if (fontRegistry != null) + fontRegistry.removeListener(fontRegistryListener); + try { + writePreferences(); + } catch (IOException e) { + e.printStackTrace(); + } + }); + } + + protected void createInputArea() { + deco = new StyledText(this, SWT.MULTI | SWT.READ_ONLY); deco.setFont(textFont); deco.setEnabled(false); GC gc = new GC(deco); int inputLeftPos = gc.getFontMetrics().getAverageCharWidth()*2; + gc.dispose(); deco.setText(">"); - { - FormData formData = new FormData(); - formData.top = new FormAttachment(sash); - formData.bottom = new FormAttachment(100); - formData.left = new FormAttachment(0); - formData.right = new FormAttachment(0, inputLeftPos); - deco.setLayoutData(formData); - } - - // Input area - input = new StyledText(this, SWT.MULTI); + deco.setLayoutData( formData(sash, 100, 0, new Tuple2(0, inputLeftPos)) ); + + // Input area + input = new StyledText(this, SWT.MULTI); input.setFont(textFont); - { - FormData formData = new FormData(); - formData.top = new FormAttachment(sash); - formData.bottom = new FormAttachment(100); - formData.left = new FormAttachment(0, inputLeftPos); - formData.right = new FormAttachment(100); - input.setLayoutData(formData); - } + input.setLayoutData( formData(sash, 100, new Tuple2(0, inputLeftPos), 100) ); adjustInputSize(""); - input.addVerifyKeyListener(new VerifyKeyListener() { - - @Override - public void verifyKey(VerifyEvent event) { - switch(event.keyCode) { - case SWT.KEYPAD_CR: - case SWT.CR: - if((event.stateMask & SWT.CTRL) == 0) { - if(canExecuteCommand()) - execute(); - event.doit = false; + input.addVerifyKeyListener(event -> { + switch(event.keyCode) { + case SWT.KEYPAD_CR: + case SWT.CR: + if((event.stateMask & SWT.CTRL) == 0) { + if(canExecuteCommand()) + execute(); + event.doit = false; + } + break; + case SWT.ARROW_UP: + case SWT.ARROW_DOWN: + if((event.stateMask & SWT.CTRL) != 0) { + int targetHistoryPos = commandHistoryPos; + if(event.keyCode == SWT.ARROW_UP) { + if(commandHistoryPos <= 0) + return; + --targetHistoryPos; } - break; - case SWT.ARROW_UP: - case SWT.ARROW_DOWN: - if((event.stateMask & SWT.CTRL) != 0) { - int targetHistoryPos = commandHistoryPos; - if(event.keyCode == SWT.ARROW_UP) { - if(commandHistoryPos <= 0) - return; - --targetHistoryPos; - } - else { - if(commandHistoryPos >= commandHistory.size()-1) - return; - ++targetHistoryPos; - } - setInputText(commandHistory.get(targetHistoryPos)); - commandHistoryPos = targetHistoryPos; - event.doit = false; + else { + if(commandHistoryPos >= commandHistory.size()-1) + return; + ++targetHistoryPos; } - break; -// case SWT.ESC: -// setInputText(""); -// commandHistoryPos = commandHistory.size(); -// break; - } - } - - }); - input.addVerifyListener(new VerifyListener() { - @Override - public void verifyText(VerifyEvent e) { - if(e.text.contains("\n")) { - int lineId = input.getLineAtOffset(e.start); - int lineOffset = input.getOffsetAtLine(lineId); - int indentAmount; - for(indentAmount=0; - lineOffset+indentAmount < input.getCharCount() && - input.getTextRange(lineOffset+indentAmount, 1).equals(" "); - ++indentAmount); - StringBuilder indent = new StringBuilder(); - indent.append('\n'); - for(int i=0;i { + if(e.text.contains("\n")) { + int lineId = input.getLineAtOffset(e.start); + int lineOffset = input.getOffsetAtLine(lineId); + int indentAmount; + for(indentAmount=0; + lineOffset+indentAmount < input.getCharCount() && + input.getTextRange(lineOffset+indentAmount, 1).equals(" "); + ++indentAmount); + StringBuilder indent = new StringBuilder(); + indent.append('\n'); + for(int i=0;i { + adjustInputSize(input.getText()); + commandHistoryPos = commandHistory.size(); + //asyncValidate(); + }); Listener hoverListener = new Listener() { DefaultToolTip toolTip = new DefaultToolTip(input, ToolTip.RECREATE, true); @@ -285,30 +303,42 @@ public abstract class AbstractCommandConsole extends Composite { toolTipVisible = false; } return; - } - } + } + } }; input.addListener(SWT.MouseHover, hoverListener); input.addListener(SWT.MouseMove, hoverListener); input.addListener(SWT.MouseExit, hoverListener); - - readPreferences(); - - addListener(SWT.Dispose, new Listener() { - - @Override - public void handleEvent(Event event) { - try { - writePreferences(); - } catch (IOException e) { - e.printStackTrace(); - } - } - - }); - } - + + private FormData formData(Object top, Object bottom, Object left, Object right) { + return formData(top, bottom, left, right, null); + } + + private FormData formData(Object top, Object bottom, Object left, Object right, Integer height) { + FormData d = new FormData(); + d.top = formAttachment(top); + d.bottom = formAttachment(bottom); + d.left = formAttachment(left); + d.right = formAttachment(right); + d.height = height != null ? (Integer) height : SWT.DEFAULT; + return d; + } + + private FormAttachment formAttachment(Object o) { + if (o == null) + return null; + if (o instanceof Control) + return new FormAttachment((Control) o); + if (o instanceof Integer) + return new FormAttachment((Integer) o); + if (o instanceof Tuple2) { + Tuple2 t = (Tuple2) o; + return new FormAttachment((Integer) t.c0, (Integer) t.c1); + } + throw new IllegalArgumentException("argument not supported: " + o); + } + private int getOffsetInInput(int x, int y) { int offset; try { @@ -327,6 +357,8 @@ public abstract class AbstractCommandConsole extends Composite { } public void setInputText(String text) { + if (input == null) + return; input.setText(text); input.setCaretOffset(text.length()); adjustInputSize(text); @@ -345,20 +377,17 @@ public abstract class AbstractCommandConsole extends Composite { }; - Job preValidationJob = new Job("SCL input validation") { + Job preValidationJob = new Job("SCL input validation") { @Override protected IStatus run(IProgressMonitor monitor) { if(!input.isDisposed()) { - input.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - if(!input.isDisposed()) { - validatedText = input.getText(); - validationJob.setPriority(Job.BUILD); - validationJob.schedule(); - } - } - }); + input.getDisplay().asyncExec(() -> { + if(!input.isDisposed()) { + validatedText = input.getText(); + validationJob.setPriority(Job.BUILD); + validationJob.schedule(); + } + }); } return Status.OK_STATUS; @@ -391,45 +420,65 @@ public abstract class AbstractCommandConsole extends Composite { } private void setInputHeight(int inputHeight) { - FormData formData = new FormData(); - formData.top = new FormAttachment(100, -inputHeight); - formData.left = new FormAttachment(0); - formData.right = new FormAttachment(100); - formData.height = SASH_HEIGHT; - sash.setLayoutData(formData); + sash.setLayoutData( formData(new Tuple2(100, -inputHeight), null, 0, 100, SASH_HEIGHT) ); AbstractCommandConsole.this.layout(true); } + private StringBuilder outputBuffer = new StringBuilder(); + private ArrayList styleRanges = new ArrayList(); + private volatile boolean outputScheduled = false; + public void appendOutput(final String text, final Color foreground, final Color background) { - final Display display = Display.getDefault(); - if(display.isDisposed()) return; - display.asyncExec(new Runnable() { - @Override - public void run() { + synchronized (outputBuffer) { + styleRanges.add(new StyleRange(outputBuffer.length(), text.length(), foreground, background)); + outputBuffer.append(text); + } + if(!outputScheduled) { + outputScheduled = true; + final Display display = Display.getDefault(); + if(display.isDisposed()) return; + display.asyncExec(() -> { if(output.isDisposed()) return; + String outputText; + StyleRange[] styleRangeArray; + synchronized(outputBuffer) { + outputScheduled = false; + + outputText = outputBuffer.toString(); + outputBuffer = new StringBuilder(); + + styleRangeArray = styleRanges.toArray(new StyleRange[styleRanges.size()]); + styleRanges.clear(); + } int pos = output.getCharCount(); + outputModiLock = true; - output.replaceTextRange(pos, 0, text); + output.replaceTextRange(pos, 0, outputText); outputModiLock = false; - output.setStyleRange(new StyleRange(pos, text.length(), - foreground, background)); + + for(StyleRange styleRange : styleRangeArray) { + styleRange.start += pos; + output.setStyleRange(styleRange); + } + output.setCaretOffset(output.getCharCount()); output.showSelection(); - } - - }); + }); + } } - - private void execute() { + + private void execute() { String command = input.getText().trim(); if(command.isEmpty()) return; // Add command to command history - commandHistory.add(command); - if(commandHistory.size() > COMMAND_HISTORY_SIZE*2) - commandHistory = new ArrayList( - commandHistory.subList(COMMAND_HISTORY_SIZE, COMMAND_HISTORY_SIZE*2)); + if(commandHistory.isEmpty() || !commandHistory.get(commandHistory.size()-1).equals(command)) { + commandHistory.add(command); + if(commandHistory.size() > COMMAND_HISTORY_SIZE*2) + commandHistory = new ArrayList( + commandHistory.subList(COMMAND_HISTORY_SIZE, COMMAND_HISTORY_SIZE*2)); + } commandHistoryPos = commandHistory.size(); // Print it into output area @@ -471,8 +520,7 @@ public abstract class AbstractCommandConsole extends Composite { range.start = 0; range.length = 1; input.setStyleRange(range); - System.err.println("The following error message didn't have a proper location:"); - System.err.println(annotation.description); + getLogger().error("The following error message didn't have a proper location: {}", annotation.description, e); } } } @@ -480,15 +528,12 @@ public abstract class AbstractCommandConsole extends Composite { private void asyncSetErrorAnnotations(final String forCommand, final ErrorAnnotation[] annotations) { if(input.isDisposed()) return; - input.getDisplay().asyncExec(new Runnable() { - @Override - public void run() { - if(input.isDisposed()) - return; - if(!input.getText().equals(forCommand)) - return; - syncSetErrorAnnotations(forCommand, annotations); - } + input.getDisplay().asyncExec(() -> { + if(input.isDisposed()) + return; + if(!input.getText().equals(forCommand)) + return; + syncSetErrorAnnotations(forCommand, annotations); }); } @@ -529,4 +574,34 @@ public abstract class AbstractCommandConsole extends Composite { return output; } + IPropertyChangeListener fontRegistryListener = new IPropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent event) { + setTextFont( FontDescriptor.createFrom((FontData[]) event.getNewValue()) ); + } + }; + + private void setTextFont(FontDescriptor font) { + FontDescriptor oldFontDesc = textFontDescriptor; + textFont = resourceManager.createFont(font); + textFontDescriptor = font; + applyTextFont(textFont); + + // Only destroy old font after the new font has been set! + if (oldFontDesc != null) + resourceManager.destroyFont(oldFontDesc); + } + + private void applyTextFont(Font font) { + if (output != null) + output.setFont(font); + if (deco != null) + deco.setFont(font); + if (input != null) { + input.setFont(font); + adjustInputSize(input.getText()); + } + } + + public abstract Logger getLogger(); }