mirror of
https://github.com/arduino/Arduino.git
synced 2024-12-01 12:24:14 +01:00
Merge pull request #9158 from joew46167/master
Make update boards and libraries startup dialog accessible -
This commit is contained in:
commit
140f8e345c
@ -29,32 +29,34 @@
|
|||||||
|
|
||||||
package cc.arduino.contributions;
|
package cc.arduino.contributions;
|
||||||
|
|
||||||
|
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
|
||||||
import cc.arduino.contributions.libraries.LibraryInstaller;
|
import cc.arduino.contributions.libraries.LibraryInstaller;
|
||||||
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
|
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
|
||||||
import cc.arduino.contributions.packages.ContributionInstaller;
|
import cc.arduino.contributions.packages.ContributionInstaller;
|
||||||
import cc.arduino.contributions.packages.filters.UpdatablePlatformPredicate;
|
import cc.arduino.contributions.packages.filters.UpdatablePlatformPredicate;
|
||||||
import cc.arduino.view.NotificationPopup;
|
import cc.arduino.view.NotificationPopup;
|
||||||
import processing.app.Base;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import processing.app.BaseNoGui;
|
import processing.app.*;
|
||||||
import processing.app.Editor;
|
|
||||||
import processing.app.I18n;
|
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.HyperlinkListener;
|
import javax.swing.event.HyperlinkListener;
|
||||||
|
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.awt.event.WindowFocusListener;
|
import java.awt.event.WindowFocusListener;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
import static processing.app.I18n.tr;
|
import static processing.app.I18n.tr;
|
||||||
|
|
||||||
public class ContributionsSelfCheck extends TimerTask {
|
public class ContributionsSelfCheck extends TimerTask implements NotificationPopup.OptionalButtonCallbacks {
|
||||||
|
|
||||||
private final Base base;
|
private final Base base;
|
||||||
private final HyperlinkListener hyperlinkListener;
|
private final HyperlinkListener hyperlinkListener;
|
||||||
private final ContributionInstaller contributionInstaller;
|
private final ContributionInstaller contributionInstaller;
|
||||||
private final LibraryInstaller libraryInstaller;
|
private final LibraryInstaller libraryInstaller;
|
||||||
private final ProgressListener progressListener;
|
private final ProgressListener progressListener;
|
||||||
|
private final String boardsManagerURL = "http://boardsmanager/DropdownUpdatableCoresItem";
|
||||||
|
private final String libraryManagerURL = "http://librarymanager/DropdownUpdatableLibrariesItem";
|
||||||
|
|
||||||
private volatile boolean cancelled;
|
private volatile boolean cancelled;
|
||||||
private volatile NotificationPopup notificationPopup;
|
private volatile NotificationPopup notificationPopup;
|
||||||
@ -81,13 +83,41 @@ public class ContributionsSelfCheck extends TimerTask {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String text;
|
boolean setAccessible = PreferencesData.getBoolean("ide.accessible");
|
||||||
|
final String text;
|
||||||
|
final String button1Name;
|
||||||
|
final String button2Name;
|
||||||
|
String openAnchorBoards = "<a href=\"" + boardsManagerURL + "\">";
|
||||||
|
String closeAnchorBoards = "</a>";
|
||||||
|
String openAnchorLibraries = "<a href=\"" + libraryManagerURL + "\">";
|
||||||
|
String closeAnchorLibraries = "</a>";
|
||||||
|
|
||||||
|
// if accessibility mode and board updates are available set the button name and clear the anchors
|
||||||
|
if(setAccessible && updatablePlatforms) {
|
||||||
|
button1Name = tr("Boards");
|
||||||
|
openAnchorBoards = "";
|
||||||
|
closeAnchorBoards = "";
|
||||||
|
}
|
||||||
|
else { // when not accessibility mode or no boards to update no button is needed
|
||||||
|
button1Name = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if accessibility mode and libraries updates are available set the button name and clear the anchors
|
||||||
|
if (setAccessible && updatableLibraries) {
|
||||||
|
button2Name = tr("Libraries");
|
||||||
|
openAnchorLibraries = "";
|
||||||
|
closeAnchorLibraries = "";
|
||||||
|
}
|
||||||
|
else { // when not accessibility mode or no libraries to update no button is needed
|
||||||
|
button2Name = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (updatableLibraries && !updatablePlatforms) {
|
if (updatableLibraries && !updatablePlatforms) {
|
||||||
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
|
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), openAnchorLibraries, closeAnchorLibraries);
|
||||||
} else if (!updatableLibraries && updatablePlatforms) {
|
} else if (!updatableLibraries && updatablePlatforms) {
|
||||||
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>");
|
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), openAnchorBoards, closeAnchorBoards);
|
||||||
} else {
|
} else {
|
||||||
text = I18n.format(tr("Updates available for some of your {0}boards{1} and {2}libraries{3}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>", "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
|
text = I18n.format(tr("Updates available for some of your {0}libraries{1} and {2}libraries{3}"), openAnchorBoards, closeAnchorBoards, openAnchorLibraries, closeAnchorLibraries);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
@ -96,7 +126,13 @@ public class ContributionsSelfCheck extends TimerTask {
|
|||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
SwingUtilities.invokeLater(() -> {
|
||||||
Editor ed = base.getActiveEditor();
|
Editor ed = base.getActiveEditor();
|
||||||
|
boolean accessibleIde = PreferencesData.getBoolean("ide.accessible");
|
||||||
|
if (accessibleIde) {
|
||||||
|
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text, false, this, button1Name, button2Name);
|
||||||
|
}
|
||||||
|
else { // if not accessible view leave it the same
|
||||||
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
|
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
|
||||||
|
}
|
||||||
if (ed.isFocused()) {
|
if (ed.isFocused()) {
|
||||||
notificationPopup.begin();
|
notificationPopup.begin();
|
||||||
return;
|
return;
|
||||||
@ -122,6 +158,24 @@ public class ContributionsSelfCheck extends TimerTask {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void goToManager(String link) {
|
||||||
|
try {
|
||||||
|
((UpdatableBoardsLibsFakeURLsHandler) hyperlinkListener).openBoardLibManager(new URL(link));
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
LogManager.getLogger(ContributionsSelfCheck.class).warn("Exception while attempting to go to board manager", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// callback for boards button
|
||||||
|
public void onOptionalButton1Callback() {
|
||||||
|
goToManager(boardsManagerURL);
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback for libraries button
|
||||||
|
public void onOptionalButton2Callback() {
|
||||||
|
goToManager(libraryManagerURL);
|
||||||
|
}
|
||||||
|
|
||||||
static boolean checkForUpdatablePlatforms() {
|
static boolean checkForUpdatablePlatforms() {
|
||||||
return BaseNoGui.indexer.getPackages().stream()
|
return BaseNoGui.indexer.getPackages().stream()
|
||||||
.flatMap(pack -> pack.getPlatforms().stream())
|
.flatMap(pack -> pack.getPlatforms().stream())
|
||||||
|
@ -36,12 +36,7 @@ import java.awt.FlowLayout;
|
|||||||
import java.awt.Frame;
|
import java.awt.Frame;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
import java.awt.Point;
|
import java.awt.Point;
|
||||||
import java.awt.event.ComponentAdapter;
|
import java.awt.event.*;
|
||||||
import java.awt.event.ComponentEvent;
|
|
||||||
import java.awt.event.MouseAdapter;
|
|
||||||
import java.awt.event.MouseEvent;
|
|
||||||
import java.awt.event.WindowAdapter;
|
|
||||||
import java.awt.event.WindowEvent;
|
|
||||||
import java.util.Timer;
|
import java.util.Timer;
|
||||||
import java.util.TimerTask;
|
import java.util.TimerTask;
|
||||||
|
|
||||||
@ -55,22 +50,46 @@ import javax.swing.border.LineBorder;
|
|||||||
import javax.swing.event.HyperlinkListener;
|
import javax.swing.event.HyperlinkListener;
|
||||||
|
|
||||||
import cc.arduino.Constants;
|
import cc.arduino.Constants;
|
||||||
|
import processing.app.PreferencesData;
|
||||||
import processing.app.Theme;
|
import processing.app.Theme;
|
||||||
|
|
||||||
public class NotificationPopup extends JDialog {
|
import java.awt.event.KeyEvent;
|
||||||
|
|
||||||
|
import static processing.app.I18n.tr;
|
||||||
|
|
||||||
|
public class NotificationPopup extends JDialog {
|
||||||
private Timer autoCloseTimer = new Timer(false);
|
private Timer autoCloseTimer = new Timer(false);
|
||||||
private boolean autoClose = true;
|
private boolean autoClose = true;
|
||||||
|
private OptionalButtonCallbacks optionalButtonCallbacks;
|
||||||
|
|
||||||
|
public interface OptionalButtonCallbacks {
|
||||||
|
void onOptionalButton1Callback();
|
||||||
|
void onOptionalButton2Callback();
|
||||||
|
}
|
||||||
|
|
||||||
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
||||||
String message) {
|
String message) {
|
||||||
this(parent, hyperlinkListener, message, true);
|
this(parent, hyperlinkListener, message, true, null, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
||||||
String message, boolean _autoClose) {
|
String message, boolean _autoClose) {
|
||||||
|
this(parent, hyperlinkListener, message, _autoClose, null, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
|
||||||
|
String message, boolean _autoClose, OptionalButtonCallbacks listener, String button1Name, String button2Name) {
|
||||||
super(parent, false);
|
super(parent, false);
|
||||||
|
|
||||||
|
if (!PreferencesData.getBoolean("ide.accessible")) {
|
||||||
|
// often auto-close is too fast for users of screen readers, so don't allow it.
|
||||||
autoClose = _autoClose;
|
autoClose = _autoClose;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
autoClose = false;
|
||||||
|
}
|
||||||
|
optionalButtonCallbacks = listener;
|
||||||
|
|
||||||
setLayout(new FlowLayout());
|
setLayout(new FlowLayout());
|
||||||
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
|
||||||
setUndecorated(true);
|
setUndecorated(true);
|
||||||
@ -90,6 +109,74 @@ public class NotificationPopup extends JDialog {
|
|||||||
text.addHyperlinkListener(hyperlinkListener);
|
text.addHyperlinkListener(hyperlinkListener);
|
||||||
add(text);
|
add(text);
|
||||||
|
|
||||||
|
if (button1Name != null) {
|
||||||
|
JButton optionalButton1 = new JButton(tr(button1Name));
|
||||||
|
MouseAdapter button1Action = new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (optionalButtonCallbacks != null) {
|
||||||
|
optionalButtonCallbacks.onOptionalButton1Callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
optionalButton1.addMouseListener(button1Action);
|
||||||
|
|
||||||
|
KeyListener button1Key = new KeyListener() {
|
||||||
|
// Ignore when the key is typed - only act once the key is released
|
||||||
|
public void keyTyped(KeyEvent e) {
|
||||||
|
// do nothing here, wait until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore when the key is pressed - only act once the key is released
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
// do nothing here, wait until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyReleased(KeyEvent e) {
|
||||||
|
int key = e.getKeyCode();
|
||||||
|
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
|
||||||
|
optionalButtonCallbacks.onOptionalButton1Callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
optionalButton1.addKeyListener(button1Key);
|
||||||
|
add(optionalButton1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button2Name != null) {
|
||||||
|
JButton optionalButton2 = new JButton(tr(button2Name));
|
||||||
|
MouseAdapter button2Action = new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
if (optionalButtonCallbacks != null) {
|
||||||
|
optionalButtonCallbacks.onOptionalButton2Callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
optionalButton2.addMouseListener(button2Action);
|
||||||
|
|
||||||
|
KeyListener button2Key = new KeyListener() {
|
||||||
|
// Ignore when the key is typed - only act once the key is released
|
||||||
|
public void keyTyped(KeyEvent e) {
|
||||||
|
// do nothing here, wait until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore when the key is pressed - only act once the key is released
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
// do nothing here, wait until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyReleased(KeyEvent e) {
|
||||||
|
int key = e.getKeyCode();
|
||||||
|
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
|
||||||
|
optionalButtonCallbacks.onOptionalButton2Callback();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
optionalButton2.addKeyListener(button2Key);
|
||||||
|
add(optionalButton2);
|
||||||
|
}
|
||||||
|
|
||||||
Image close = Theme.getThemeImage("close", this, scale(22), scale(22));
|
Image close = Theme.getThemeImage("close", this, scale(22), scale(22));
|
||||||
JButton closeButton = new JButton(new ImageIcon(close));
|
JButton closeButton = new JButton(new ImageIcon(close));
|
||||||
closeButton.setBorder(null);
|
closeButton.setBorder(null);
|
||||||
@ -97,6 +184,26 @@ public class NotificationPopup extends JDialog {
|
|||||||
closeButton.setHideActionText(true);
|
closeButton.setHideActionText(true);
|
||||||
closeButton.setOpaque(false);
|
closeButton.setOpaque(false);
|
||||||
closeButton.setBackground(new Color(0, 0, 0, 0));
|
closeButton.setBackground(new Color(0, 0, 0, 0));
|
||||||
|
closeButton.getAccessibleContext().setAccessibleDescription(tr("Close"));
|
||||||
|
KeyListener closeKey = new KeyListener() {
|
||||||
|
// Ignore when the key is typed - only act once the key is released
|
||||||
|
public void keyTyped(KeyEvent e) {
|
||||||
|
// do nothing here, wait until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore when the key is pressed - only act once the key is released
|
||||||
|
public void keyPressed(KeyEvent e) {
|
||||||
|
// do nothing here, wait until the key is released
|
||||||
|
}
|
||||||
|
|
||||||
|
public void keyReleased(KeyEvent e) {
|
||||||
|
int key = e.getKeyCode();
|
||||||
|
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
closeButton.addKeyListener(closeKey);
|
||||||
add(closeButton);
|
add(closeButton);
|
||||||
|
|
||||||
MouseAdapter closeOnClick = new MouseAdapter() {
|
MouseAdapter closeOnClick = new MouseAdapter() {
|
||||||
@ -158,5 +265,9 @@ public class NotificationPopup extends JDialog {
|
|||||||
}, Constants.NOTIFICATION_POPUP_AUTOCLOSE_DELAY);
|
}, Constants.NOTIFICATION_POPUP_AUTOCLOSE_DELAY);
|
||||||
}
|
}
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
|
if (PreferencesData.getBoolean("ide.accessible")) {
|
||||||
|
requestFocus();
|
||||||
|
setModal(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ public class Preferences extends javax.swing.JDialog {
|
|||||||
externalEditorBox = new javax.swing.JCheckBox();
|
externalEditorBox = new javax.swing.JCheckBox();
|
||||||
checkUpdatesBox = new javax.swing.JCheckBox();
|
checkUpdatesBox = new javax.swing.JCheckBox();
|
||||||
saveVerifyUploadBox = new javax.swing.JCheckBox();
|
saveVerifyUploadBox = new javax.swing.JCheckBox();
|
||||||
|
accessibleIDEBox = new javax.swing.JCheckBox();
|
||||||
jLabel1 = new javax.swing.JLabel();
|
jLabel1 = new javax.swing.JLabel();
|
||||||
jLabel2 = new javax.swing.JLabel();
|
jLabel2 = new javax.swing.JLabel();
|
||||||
scaleSpinner = new javax.swing.JSpinner();
|
scaleSpinner = new javax.swing.JSpinner();
|
||||||
@ -281,6 +282,9 @@ public class Preferences extends javax.swing.JDialog {
|
|||||||
saveVerifyUploadBox.setText(tr("Save when verifying or uploading"));
|
saveVerifyUploadBox.setText(tr("Save when verifying or uploading"));
|
||||||
checkboxesContainer.add(saveVerifyUploadBox);
|
checkboxesContainer.add(saveVerifyUploadBox);
|
||||||
|
|
||||||
|
accessibleIDEBox.setText(tr("Use accessibility features"));
|
||||||
|
checkboxesContainer.add(accessibleIDEBox);
|
||||||
|
|
||||||
jLabel1.setText(tr("Interface scale:"));
|
jLabel1.setText(tr("Interface scale:"));
|
||||||
|
|
||||||
jLabel2.setText(tr(" (requires restart of Arduino)"));
|
jLabel2.setText(tr(" (requires restart of Arduino)"));
|
||||||
@ -713,6 +717,7 @@ public class Preferences extends javax.swing.JDialog {
|
|||||||
private javax.swing.JCheckBox autoScaleCheckBox;
|
private javax.swing.JCheckBox autoScaleCheckBox;
|
||||||
private javax.swing.JButton browseButton;
|
private javax.swing.JButton browseButton;
|
||||||
private javax.swing.JCheckBox checkUpdatesBox;
|
private javax.swing.JCheckBox checkUpdatesBox;
|
||||||
|
private javax.swing.JCheckBox accessibleIDEBox;
|
||||||
private javax.swing.JPanel checkboxesContainer;
|
private javax.swing.JPanel checkboxesContainer;
|
||||||
private javax.swing.JComboBox comboLanguage;
|
private javax.swing.JComboBox comboLanguage;
|
||||||
private javax.swing.JLabel comboLanguageLabel;
|
private javax.swing.JLabel comboLanguageLabel;
|
||||||
@ -826,7 +831,7 @@ public class Preferences extends javax.swing.JDialog {
|
|||||||
|
|
||||||
PreferencesData.setBoolean("update.check", checkUpdatesBox.isSelected());
|
PreferencesData.setBoolean("update.check", checkUpdatesBox.isSelected());
|
||||||
|
|
||||||
PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected());
|
PreferencesData.setBoolean("ide.accessible", accessibleIDEBox.isSelected());
|
||||||
|
|
||||||
PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ","));
|
PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ","));
|
||||||
|
|
||||||
@ -902,6 +907,8 @@ public class Preferences extends javax.swing.JDialog {
|
|||||||
PreferencesData.setBoolean("editor.update_extension", true);
|
PreferencesData.setBoolean("editor.update_extension", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accessibleIDEBox.setSelected(PreferencesData.getBoolean("ide.accessible"));
|
||||||
|
|
||||||
saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify"));
|
saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify"));
|
||||||
|
|
||||||
additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls"));
|
additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls"));
|
||||||
|
Loading…
Reference in New Issue
Block a user