Skip to content

Serial/network monitor and editor console dynamic font size hotkeys and improved updating #8704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 26, 2019
7 changes: 4 additions & 3 deletions app/src/cc/arduino/packages/MonitorFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,24 @@
package cc.arduino.packages;

import processing.app.AbstractMonitor;
import processing.app.Base;
import processing.app.NetworkMonitor;
import processing.app.SerialMonitor;

public class MonitorFactory {

public AbstractMonitor newMonitor(BoardPort port) {
public AbstractMonitor newMonitor(Base base, BoardPort port) {
if ("network".equals(port.getProtocol())) {
if ("yes".equals(port.getPrefs().get("ssh_upload"))) {
// the board is SSH capable
return new NetworkMonitor(port);
return new NetworkMonitor(base, port);
} else {
// SSH not supported, no monitor support
return null;
}
}

return new SerialMonitor(port);
return new SerialMonitor(base, port);
}

}
12 changes: 12 additions & 0 deletions app/src/processing/app/AbstractMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public AbstractMonitor(BoardPort boardPort) {
this.boardPort = boardPort;

addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
try {
closed = true;
Expand All @@ -41,6 +42,7 @@ public void windowClosing(WindowEvent event) {
KeyStroke wc = Editor.WINDOW_CLOSE_KEYSTROKE;
getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(wc, "close");
getRootPane().getActionMap().put("close", (new AbstractAction() {
@Override
public void actionPerformed(ActionEvent event) {
try {
close();
Expand Down Expand Up @@ -165,6 +167,7 @@ private synchronized String consumeUpdateBuffer() {
return s;
}

@Override
public void actionPerformed(ActionEvent e) {
String s = consumeUpdateBuffer();
if (s.isEmpty()) {
Expand All @@ -173,4 +176,13 @@ public void actionPerformed(ActionEvent e) {
message(s);
}
}

/**
* Read and apply new values from the preferences, either because
* the app is just starting up, or the user just finished messing
* with things in the Preferences window.
*/
public void applyPreferences() {
// Empty.
};
}
65 changes: 40 additions & 25 deletions app/src/processing/app/AbstractTextMonitor.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,25 +40,26 @@ public abstract class AbstractTextMonitor extends AbstractMonitor {
protected JButton clearButton;
protected JCheckBox autoscrollBox;
protected JCheckBox addTimeStampBox;
protected JComboBox lineEndings;
protected JComboBox serialRates;
protected JComboBox<String> lineEndings;
protected JComboBox<String> serialRates;

public AbstractTextMonitor(BoardPort boardPort) {
public AbstractTextMonitor(Base base, BoardPort boardPort) {
super(boardPort);

// Add font size adjustment listeners. This has to be done here due to
// super(boardPort) invoking onCreateWindow(...) before we can store base.
base.addEditorFontResizeListeners(textArea);
}

@Override
protected void onCreateWindow(Container mainPane) {
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
Font font = Theme.scale(new Font(consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize()));

mainPane.setLayout(new BorderLayout());

textArea = new TextAreaFIFO(8_000_000);
textArea.setRows(16);
textArea.setColumns(40);
textArea.setEditable(false);
textArea.setFont(font);

// don't automatically update the caret. that way we can manually decide
// whether or not to do so based on the autoscroll checkbox.
Expand All @@ -75,6 +76,7 @@ protected void onCreateWindow(Container mainPane) {
textField = new JTextField(40);
// textField is selected every time the window is focused
addWindowFocusListener(new WindowAdapter() {
@Override
public void windowGainedFocus(WindowEvent e) {
textField.requestFocusInWindow();
}
Expand Down Expand Up @@ -103,28 +105,17 @@ public void windowGainedFocus(WindowEvent e) {
minimumSize.setSize(minimumSize.getWidth() / 3, minimumSize.getHeight());
noLineEndingAlert.setMinimumSize(minimumSize);

lineEndings = new JComboBox(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
lineEndings.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
noLineEndingAlert.setForeground(pane.getBackground());
}
});
if (PreferencesData.get("serial.line_ending") != null) {
lineEndings.setSelectedIndex(PreferencesData.getInteger("serial.line_ending"));
}
if (PreferencesData.get("serial.show_timestamp") != null) {
addTimeStampBox.setSelected(PreferencesData.getBoolean("serial.show_timestamp"));
}
addTimeStampBox.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
PreferencesData.setBoolean("serial.show_timestamp", addTimeStampBox.isSelected());
}
lineEndings = new JComboBox<String>(new String[]{tr("No line ending"), tr("Newline"), tr("Carriage return"), tr("Both NL & CR")});
lineEndings.addActionListener((ActionEvent event) -> {
PreferencesData.setInteger("serial.line_ending", lineEndings.getSelectedIndex());
noLineEndingAlert.setForeground(pane.getBackground());
});
addTimeStampBox.addActionListener((ActionEvent event) ->
PreferencesData.setBoolean("serial.show_timestamp", addTimeStampBox.isSelected()));

lineEndings.setMaximumSize(lineEndings.getMinimumSize());

serialRates = new JComboBox();
serialRates = new JComboBox<String>();
for (String rate : serialRateStrings) {
serialRates.addItem(rate + " " + tr("baud"));
}
Expand All @@ -142,9 +133,12 @@ public void actionPerformed(ActionEvent e) {
pane.add(Box.createRigidArea(new Dimension(8, 0)));
pane.add(clearButton);

applyPreferences();

mainPane.add(pane, BorderLayout.SOUTH);
}

@Override
protected void onEnableWindow(boolean enable)
{
textArea.setEnabled(enable);
Expand All @@ -171,6 +165,7 @@ public void onSerialRateChange(ActionListener listener) {
serialRates.addActionListener(listener);
}

@Override
public void message(String msg) {
SwingUtilities.invokeLater(() -> updateTextArea(msg));
}
Expand All @@ -189,6 +184,26 @@ protected void updateTextArea(String msg) {
}
}

@Override
public void applyPreferences() {

// Apply font.
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
textArea.setFont(Theme.scale(new Font(
consoleFont.getName(), consoleFont.getStyle(), editorFont.getSize())));

// Apply line endings.
if (PreferencesData.get("serial.line_ending") != null) {
lineEndings.setSelectedIndex(PreferencesData.getInteger("serial.line_ending"));
}

// Apply timestamp visibility.
if (PreferencesData.get("serial.show_timestamp") != null) {
addTimeStampBox.setSelected(PreferencesData.getBoolean("serial.show_timestamp"));
}
}

private String addTimestamps(String text) {
String now = new SimpleDateFormat("HH:mm:ss.SSS -> ").format(new Date());
final StringBuilder sb = new StringBuilder(text.length() + now.length());
Expand Down
75 changes: 75 additions & 0 deletions app/src/processing/app/Base.java
Original file line number Diff line number Diff line change
Expand Up @@ -1877,6 +1877,81 @@ public void handleFontSizeChange(int change) {
getEditors().forEach(Editor::applyPreferences);
}

private MouseWheelListener editorFontResizeMouseWheelListener = null;
private KeyListener editorFontResizeKeyListener = null;

/**
* Adds a {@link MouseWheelListener} and {@link KeyListener} to the given
* component that will make "CTRL scroll" and "CTRL +/-"
* (with optional SHIFT for +) increase/decrease the editor text size.
* This method is equivalent to calling
* {@link #addEditorFontResizeMouseWheelListener(Component)} and
* {@link #addEditorFontResizeKeyListener(Component)} on the given component.
* Note that this also affects components that use the editor font settings.
* @param comp - The component to add the listener to.
*/
public void addEditorFontResizeListeners(Component comp) {
this.addEditorFontResizeMouseWheelListener(comp);
this.addEditorFontResizeKeyListener(comp);
}

/**
* Adds a {@link MouseWheelListener} to the given component that will
* make "CTRL scroll" increase/decrease the editor text size.
* When CTRL is not pressed while scrolling, mouse wheel events are passed
* on to the parent of the given component.
* Note that this also affects components that use the editor font settings.
* @param comp - The component to add the listener to.
*/
public void addEditorFontResizeMouseWheelListener(Component comp) {
if (this.editorFontResizeMouseWheelListener == null) {
this.editorFontResizeMouseWheelListener = (MouseWheelEvent e) -> {
if (e.isControlDown()) {
if (e.getWheelRotation() < 0) {
this.handleFontSizeChange(1);
} else {
this.handleFontSizeChange(-1);
}
} else {
e.getComponent().getParent().dispatchEvent(e);
}
};
}
comp.addMouseWheelListener(this.editorFontResizeMouseWheelListener);
}

/**
* Adds a {@link KeyListener} to the given component that will make "CTRL +/-"
* (with optional SHIFT for +) increase/decrease the editor text size.
* Note that this also affects components that use the editor font settings.
* @param comp - The component to add the listener to.
*/
public void addEditorFontResizeKeyListener(Component comp) {
if (this.editorFontResizeKeyListener == null) {
this.editorFontResizeKeyListener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getModifiersEx() == KeyEvent.CTRL_DOWN_MASK
|| e.getModifiersEx() == (KeyEvent.CTRL_DOWN_MASK
| KeyEvent.SHIFT_DOWN_MASK)) {
switch (e.getKeyCode()) {
case KeyEvent.VK_PLUS:
case KeyEvent.VK_EQUALS:
Base.this.handleFontSizeChange(1);
break;
case KeyEvent.VK_MINUS:
if (!e.isShiftDown()) {
Base.this.handleFontSizeChange(-1);
}
break;
}
}
}
};
}
comp.addKeyListener(this.editorFontResizeKeyListener);
}

public List<JMenu> getBoardsCustomMenus() {
return boardsCustomMenus;
}
Expand Down
15 changes: 10 additions & 5 deletions app/src/processing/app/Editor.java
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ public void windowDeactivated(WindowEvent e) {
status = new EditorStatus(this);
consolePanel.add(status, BorderLayout.NORTH);

console = new EditorConsole();
console = new EditorConsole(base);
console.setName("console");
// windows puts an ugly border on this guy
console.setBorder(null);
Expand Down Expand Up @@ -495,6 +495,9 @@ public void applyPreferences() {
tab.applyPreferences();
}
console.applyPreferences();
if (serialMonitor != null) {
serialMonitor.applyPreferences();
}
}


Expand Down Expand Up @@ -1270,12 +1273,14 @@ private JMenu buildEditMenu() {
JMenuItem increaseFontSizeItem = newJMenuItem(tr("Increase Font Size"), KeyEvent.VK_PLUS);
increaseFontSizeItem.addActionListener(event -> base.handleFontSizeChange(1));
menu.add(increaseFontSizeItem);
// Add alternative shortcut "CTRL SHIFT =" for keyboards that haven't the "+" key
// in the base layer. This workaround covers all the keyboards that have the "+"
// key available as "SHIFT =" that seems to be very common.
// Many keyboards have '+' and '=' on the same key. Allowing "CTRL +",
// "CTRL SHIFT +" and "CTRL =" covers the generally expected behavior.
KeyStroke ctrlShiftEq = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, SHORTCUT_KEY_MASK | ActionEvent.SHIFT_MASK);
menu.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ctrlShiftEq, "IncreaseFontSize");
KeyStroke ctrlEq = KeyStroke.getKeyStroke(KeyEvent.VK_EQUALS, SHORTCUT_KEY_MASK);
menu.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(ctrlEq, "IncreaseFontSize");
menu.getActionMap().put("IncreaseFontSize", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
base.handleFontSizeChange(1);
}
Expand Down Expand Up @@ -2209,7 +2214,7 @@ public void handleSerial() {
return;
}

serialMonitor = new MonitorFactory().newMonitor(port);
serialMonitor = new MonitorFactory().newMonitor(base, port);

if (serialMonitor == null) {
String board = port.getPrefs().get("board");
Expand Down
43 changes: 40 additions & 3 deletions app/src/processing/app/EditorConsole.java
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public static void setCurrentEditorConsole(EditorConsole console) {
private SimpleAttributeSet stdOutStyle;
private SimpleAttributeSet stdErrStyle;

public EditorConsole() {
public EditorConsole(Base base) {
document = new DefaultStyledDocument();

consoleTextPane = new JTextPane(document);
Expand Down Expand Up @@ -110,18 +110,55 @@ public EditorConsole() {
setMinimumSize(new Dimension(100, (height * lines)));

EditorConsole.init(stdOutStyle, System.out, stdErrStyle, System.err);

// Add font size adjustment listeners.
base.addEditorFontResizeListeners(consoleTextPane);
}

public void applyPreferences() {

// Update the console text pane font from the preferences.
Font consoleFont = Theme.getFont("console.font");
Font editorFont = PreferencesData.getFont("editor.font");
Font actualFont = new Font(consoleFont.getName(), consoleFont.getStyle(), scale(editorFont.getSize()));

AttributeSet stdOutStyleOld = stdOutStyle.copyAttributes();
AttributeSet stdErrStyleOld = stdErrStyle.copyAttributes();
StyleConstants.setFontSize(stdOutStyle, actualFont.getSize());
StyleConstants.setFontSize(stdErrStyle, actualFont.getSize());

out.setAttibutes(stdOutStyle);
err.setAttibutes(stdErrStyle);
// Re-insert console text with the new preferences if there were changes.
// This assumes that the document has single-child paragraphs (default).
if (!stdOutStyle.isEqual(stdOutStyleOld) || !stdErrStyle.isEqual(stdOutStyleOld)) {
out.setAttibutes(stdOutStyle);
err.setAttibutes(stdErrStyle);

int start;
for (int end = document.getLength() - 1; end >= 0; end = start - 1) {
Element elem = document.getParagraphElement(end);
start = elem.getStartOffset();
AttributeSet attrs = elem.getElement(0).getAttributes();
AttributeSet newAttrs;
if (attrs.isEqual(stdErrStyleOld)) {
newAttrs = stdErrStyle;
} else if (attrs.isEqual(stdOutStyleOld)) {
newAttrs = stdOutStyle;
} else {
continue;
}
try {
String text = document.getText(start, end - start);
document.remove(start, end - start);
document.insertString(start, text, newAttrs);
} catch (BadLocationException e) {
// Should only happen when text is async removed (through clear()).
// Accept this case, but throw an error when text could mess up.
if (document.getLength() != 0) {
throw new Error(e);
}
}
}
}
}

public void clear() {
Expand Down
Loading