Skip to content

Commit 140f8e3

Browse files
authored
Merge pull request #9158 from joew46167/master
Make update boards and libraries startup dialog accessible -
2 parents 5bb9f87 + 710667d commit 140f8e3

File tree

3 files changed

+192
-20
lines changed

3 files changed

+192
-20
lines changed

Diff for: app/src/cc/arduino/contributions/ContributionsSelfCheck.java

+64-10
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,34 @@
2929

3030
package cc.arduino.contributions;
3131

32+
import cc.arduino.UpdatableBoardsLibsFakeURLsHandler;
3233
import cc.arduino.contributions.libraries.LibraryInstaller;
3334
import cc.arduino.contributions.libraries.filters.UpdatableLibraryPredicate;
3435
import cc.arduino.contributions.packages.ContributionInstaller;
3536
import cc.arduino.contributions.packages.filters.UpdatablePlatformPredicate;
3637
import cc.arduino.view.NotificationPopup;
37-
import processing.app.Base;
38-
import processing.app.BaseNoGui;
39-
import processing.app.Editor;
40-
import processing.app.I18n;
38+
import org.apache.logging.log4j.LogManager;
39+
import processing.app.*;
4140

4241
import javax.swing.*;
4342
import javax.swing.event.HyperlinkListener;
4443

4544
import java.awt.event.WindowEvent;
4645
import java.awt.event.WindowFocusListener;
46+
import java.net.URL;
4747
import java.util.TimerTask;
4848

4949
import static processing.app.I18n.tr;
5050

51-
public class ContributionsSelfCheck extends TimerTask {
51+
public class ContributionsSelfCheck extends TimerTask implements NotificationPopup.OptionalButtonCallbacks {
5252

5353
private final Base base;
5454
private final HyperlinkListener hyperlinkListener;
5555
private final ContributionInstaller contributionInstaller;
5656
private final LibraryInstaller libraryInstaller;
5757
private final ProgressListener progressListener;
58+
private final String boardsManagerURL = "http://boardsmanager/DropdownUpdatableCoresItem";
59+
private final String libraryManagerURL = "http://librarymanager/DropdownUpdatableLibrariesItem";
5860

5961
private volatile boolean cancelled;
6062
private volatile NotificationPopup notificationPopup;
@@ -81,13 +83,41 @@ public void run() {
8183
return;
8284
}
8385

84-
String text;
86+
boolean setAccessible = PreferencesData.getBoolean("ide.accessible");
87+
final String text;
88+
final String button1Name;
89+
final String button2Name;
90+
String openAnchorBoards = "<a href=\"" + boardsManagerURL + "\">";
91+
String closeAnchorBoards = "</a>";
92+
String openAnchorLibraries = "<a href=\"" + libraryManagerURL + "\">";
93+
String closeAnchorLibraries = "</a>";
94+
95+
// if accessibility mode and board updates are available set the button name and clear the anchors
96+
if(setAccessible && updatablePlatforms) {
97+
button1Name = tr("Boards");
98+
openAnchorBoards = "";
99+
closeAnchorBoards = "";
100+
}
101+
else { // when not accessibility mode or no boards to update no button is needed
102+
button1Name = null;
103+
}
104+
105+
// if accessibility mode and libraries updates are available set the button name and clear the anchors
106+
if (setAccessible && updatableLibraries) {
107+
button2Name = tr("Libraries");
108+
openAnchorLibraries = "";
109+
closeAnchorLibraries = "";
110+
}
111+
else { // when not accessibility mode or no libraries to update no button is needed
112+
button2Name = null;
113+
}
114+
85115
if (updatableLibraries && !updatablePlatforms) {
86-
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
116+
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), openAnchorLibraries, closeAnchorLibraries);
87117
} else if (!updatableLibraries && updatablePlatforms) {
88-
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>");
118+
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), openAnchorBoards, closeAnchorBoards);
89119
} else {
90-
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>");
120+
text = I18n.format(tr("Updates available for some of your {0}libraries{1} and {2}libraries{3}"), openAnchorBoards, closeAnchorBoards, openAnchorLibraries, closeAnchorLibraries);
91121
}
92122

93123
if (cancelled) {
@@ -96,7 +126,13 @@ public void run() {
96126

97127
SwingUtilities.invokeLater(() -> {
98128
Editor ed = base.getActiveEditor();
99-
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
129+
boolean accessibleIde = PreferencesData.getBoolean("ide.accessible");
130+
if (accessibleIde) {
131+
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text, false, this, button1Name, button2Name);
132+
}
133+
else { // if not accessible view leave it the same
134+
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
135+
}
100136
if (ed.isFocused()) {
101137
notificationPopup.begin();
102138
return;
@@ -122,6 +158,24 @@ public void windowGainedFocus(WindowEvent evt) {
122158
});
123159
}
124160

161+
private void goToManager(String link) {
162+
try {
163+
((UpdatableBoardsLibsFakeURLsHandler) hyperlinkListener).openBoardLibManager(new URL(link));
164+
}
165+
catch (Exception e){
166+
LogManager.getLogger(ContributionsSelfCheck.class).warn("Exception while attempting to go to board manager", e);
167+
}
168+
}
169+
// callback for boards button
170+
public void onOptionalButton1Callback() {
171+
goToManager(boardsManagerURL);
172+
}
173+
174+
// callback for libraries button
175+
public void onOptionalButton2Callback() {
176+
goToManager(libraryManagerURL);
177+
}
178+
125179
static boolean checkForUpdatablePlatforms() {
126180
return BaseNoGui.indexer.getPackages().stream()
127181
.flatMap(pack -> pack.getPlatforms().stream())

Diff for: app/src/cc/arduino/view/NotificationPopup.java

+120-9
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,7 @@
3636
import java.awt.Frame;
3737
import java.awt.Image;
3838
import java.awt.Point;
39-
import java.awt.event.ComponentAdapter;
40-
import java.awt.event.ComponentEvent;
41-
import java.awt.event.MouseAdapter;
42-
import java.awt.event.MouseEvent;
43-
import java.awt.event.WindowAdapter;
44-
import java.awt.event.WindowEvent;
39+
import java.awt.event.*;
4540
import java.util.Timer;
4641
import java.util.TimerTask;
4742

@@ -55,22 +50,46 @@
5550
import javax.swing.event.HyperlinkListener;
5651

5752
import cc.arduino.Constants;
53+
import processing.app.PreferencesData;
5854
import processing.app.Theme;
5955

60-
public class NotificationPopup extends JDialog {
56+
import java.awt.event.KeyEvent;
57+
58+
import static processing.app.I18n.tr;
6159

60+
public class NotificationPopup extends JDialog {
6261
private Timer autoCloseTimer = new Timer(false);
6362
private boolean autoClose = true;
63+
private OptionalButtonCallbacks optionalButtonCallbacks;
64+
65+
public interface OptionalButtonCallbacks {
66+
void onOptionalButton1Callback();
67+
void onOptionalButton2Callback();
68+
}
6469

6570
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
6671
String message) {
67-
this(parent, hyperlinkListener, message, true);
72+
this(parent, hyperlinkListener, message, true, null, null, null);
6873
}
6974

7075
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
7176
String message, boolean _autoClose) {
77+
this(parent, hyperlinkListener, message, _autoClose, null, null, null);
78+
}
79+
80+
public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
81+
String message, boolean _autoClose, OptionalButtonCallbacks listener, String button1Name, String button2Name) {
7282
super(parent, false);
73-
autoClose = _autoClose;
83+
84+
if (!PreferencesData.getBoolean("ide.accessible")) {
85+
// often auto-close is too fast for users of screen readers, so don't allow it.
86+
autoClose = _autoClose;
87+
}
88+
else {
89+
autoClose = false;
90+
}
91+
optionalButtonCallbacks = listener;
92+
7493
setLayout(new FlowLayout());
7594
setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
7695
setUndecorated(true);
@@ -90,13 +109,101 @@ public NotificationPopup(Frame parent, HyperlinkListener hyperlinkListener,
90109
text.addHyperlinkListener(hyperlinkListener);
91110
add(text);
92111

112+
if (button1Name != null) {
113+
JButton optionalButton1 = new JButton(tr(button1Name));
114+
MouseAdapter button1Action = new MouseAdapter() {
115+
@Override
116+
public void mouseClicked(MouseEvent e) {
117+
if (optionalButtonCallbacks != null) {
118+
optionalButtonCallbacks.onOptionalButton1Callback();
119+
}
120+
}
121+
};
122+
optionalButton1.addMouseListener(button1Action);
123+
124+
KeyListener button1Key = new KeyListener() {
125+
// Ignore when the key is typed - only act once the key is released
126+
public void keyTyped(KeyEvent e) {
127+
// do nothing here, wait until the key is released
128+
}
129+
130+
// Ignore when the key is pressed - only act once the key is released
131+
public void keyPressed(KeyEvent e) {
132+
// do nothing here, wait until the key is released
133+
}
134+
135+
public void keyReleased(KeyEvent e) {
136+
int key = e.getKeyCode();
137+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
138+
optionalButtonCallbacks.onOptionalButton1Callback();
139+
}
140+
}
141+
};
142+
optionalButton1.addKeyListener(button1Key);
143+
add(optionalButton1);
144+
}
145+
146+
if (button2Name != null) {
147+
JButton optionalButton2 = new JButton(tr(button2Name));
148+
MouseAdapter button2Action = new MouseAdapter() {
149+
@Override
150+
public void mouseClicked(MouseEvent e) {
151+
if (optionalButtonCallbacks != null) {
152+
optionalButtonCallbacks.onOptionalButton2Callback();
153+
}
154+
}
155+
};
156+
optionalButton2.addMouseListener(button2Action);
157+
158+
KeyListener button2Key = new KeyListener() {
159+
// Ignore when the key is typed - only act once the key is released
160+
public void keyTyped(KeyEvent e) {
161+
// do nothing here, wait until the key is released
162+
}
163+
164+
// Ignore when the key is pressed - only act once the key is released
165+
public void keyPressed(KeyEvent e) {
166+
// do nothing here, wait until the key is released
167+
}
168+
169+
public void keyReleased(KeyEvent e) {
170+
int key = e.getKeyCode();
171+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
172+
optionalButtonCallbacks.onOptionalButton2Callback();
173+
}
174+
}
175+
};
176+
optionalButton2.addKeyListener(button2Key);
177+
add(optionalButton2);
178+
}
179+
93180
Image close = Theme.getThemeImage("close", this, scale(22), scale(22));
94181
JButton closeButton = new JButton(new ImageIcon(close));
95182
closeButton.setBorder(null);
96183
closeButton.setBorderPainted(false);
97184
closeButton.setHideActionText(true);
98185
closeButton.setOpaque(false);
99186
closeButton.setBackground(new Color(0, 0, 0, 0));
187+
closeButton.getAccessibleContext().setAccessibleDescription(tr("Close"));
188+
KeyListener closeKey = new KeyListener() {
189+
// Ignore when the key is typed - only act once the key is released
190+
public void keyTyped(KeyEvent e) {
191+
// do nothing here, wait until the key is released
192+
}
193+
194+
// Ignore when the key is pressed - only act once the key is released
195+
public void keyPressed(KeyEvent e) {
196+
// do nothing here, wait until the key is released
197+
}
198+
199+
public void keyReleased(KeyEvent e) {
200+
int key = e.getKeyCode();
201+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
202+
close();
203+
}
204+
}
205+
};
206+
closeButton.addKeyListener(closeKey);
100207
add(closeButton);
101208

102209
MouseAdapter closeOnClick = new MouseAdapter() {
@@ -158,5 +265,9 @@ public void run() {
158265
}, Constants.NOTIFICATION_POPUP_AUTOCLOSE_DELAY);
159266
}
160267
setVisible(true);
268+
if (PreferencesData.getBoolean("ide.accessible")) {
269+
requestFocus();
270+
setModal(true);
271+
}
161272
}
162273
}

Diff for: app/src/cc/arduino/view/preferences/Preferences.java

+8-1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ private void initComponents() {
134134
externalEditorBox = new javax.swing.JCheckBox();
135135
checkUpdatesBox = new javax.swing.JCheckBox();
136136
saveVerifyUploadBox = new javax.swing.JCheckBox();
137+
accessibleIDEBox = new javax.swing.JCheckBox();
137138
jLabel1 = new javax.swing.JLabel();
138139
jLabel2 = new javax.swing.JLabel();
139140
scaleSpinner = new javax.swing.JSpinner();
@@ -281,6 +282,9 @@ public void mouseEntered(java.awt.event.MouseEvent evt) {
281282
saveVerifyUploadBox.setText(tr("Save when verifying or uploading"));
282283
checkboxesContainer.add(saveVerifyUploadBox);
283284

285+
accessibleIDEBox.setText(tr("Use accessibility features"));
286+
checkboxesContainer.add(accessibleIDEBox);
287+
284288
jLabel1.setText(tr("Interface scale:"));
285289

286290
jLabel2.setText(tr(" (requires restart of Arduino)"));
@@ -713,6 +717,7 @@ private void autoScaleCheckBoxItemStateChanged(java.awt.event.ItemEvent evt) {//
713717
private javax.swing.JCheckBox autoScaleCheckBox;
714718
private javax.swing.JButton browseButton;
715719
private javax.swing.JCheckBox checkUpdatesBox;
720+
private javax.swing.JCheckBox accessibleIDEBox;
716721
private javax.swing.JPanel checkboxesContainer;
717722
private javax.swing.JComboBox comboLanguage;
718723
private javax.swing.JLabel comboLanguageLabel;
@@ -826,7 +831,7 @@ private void savePreferencesData() {
826831

827832
PreferencesData.setBoolean("update.check", checkUpdatesBox.isSelected());
828833

829-
PreferencesData.setBoolean("editor.save_on_verify", saveVerifyUploadBox.isSelected());
834+
PreferencesData.setBoolean("ide.accessible", accessibleIDEBox.isSelected());
830835

831836
PreferencesData.set("boardsmanager.additional.urls", additionalBoardsManagerField.getText().replace("\r\n", "\n").replace("\r", "\n").replace("\n", ","));
832837

@@ -902,6 +907,8 @@ private void showPreferencesData() {
902907
PreferencesData.setBoolean("editor.update_extension", true);
903908
}
904909

910+
accessibleIDEBox.setSelected(PreferencesData.getBoolean("ide.accessible"));
911+
905912
saveVerifyUploadBox.setSelected(PreferencesData.getBoolean("editor.save_on_verify"));
906913

907914
additionalBoardsManagerField.setText(PreferencesData.get("boardsmanager.additional.urls"));

0 commit comments

Comments
 (0)