Skip to content

Commit 30a20c8

Browse files
author
Joe Wegner
committed
Add Accessibility to Upadates Available Dialog
Add accessibile IDE option to preferences dialog (Preferences.java) When accessible use buttons instead of links in in Updates Available dialog (ContributionsSelfCheck.java) Handle buttons and prevent auto-close for accessible dialog box (NotificationPopup.java)
1 parent 6bcd052 commit 30a20c8

File tree

3 files changed

+188
-19
lines changed

3 files changed

+188
-19
lines changed

app/src/cc/arduino/contributions/ContributionsSelfCheck.java

Lines changed: 73 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,32 +29,33 @@
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 processing.app.*;
4139

4240
import javax.swing.*;
4341
import javax.swing.event.HyperlinkListener;
4442

4543
import java.awt.event.WindowEvent;
4644
import java.awt.event.WindowFocusListener;
45+
import java.net.URL;
4746
import java.util.TimerTask;
4847

4948
import static processing.app.I18n.tr;
5049

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

5352
private final Base base;
5453
private final HyperlinkListener hyperlinkListener;
5554
private final ContributionInstaller contributionInstaller;
5655
private final LibraryInstaller libraryInstaller;
5756
private final ProgressListener progressListener;
57+
private final String boardsManagerURL = "http://boardsmanager/DropdownUpdatableCoresItem";
58+
private final String libraryManagerURL = "http://librarymanager/DropdownUpdatableLibrariesItem";
5859

5960
private volatile boolean cancelled;
6061
private volatile NotificationPopup notificationPopup;
@@ -81,13 +82,52 @@ public void run() {
8182
return;
8283
}
8384

84-
String text;
85+
final String text;
86+
final String button1Name;
87+
final String button2Name;
88+
String openAnchor1 = "";
89+
String closeAnchor1 = "";
90+
String openAnchor2 = "";
91+
String closeAnchor2 = "";
92+
boolean setAccessible = PreferencesData.getBoolean("ide.accessible");
8593
if (updatableLibraries && !updatablePlatforms) {
86-
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), "<a href=\"http://librarymanager/DropdownUpdatableLibrariesItem\">", "</a>");
94+
if (setAccessible) {
95+
button1Name = null;
96+
button2Name = tr("libraries");
97+
}
98+
else {
99+
button1Name = null;
100+
button2Name = null;
101+
openAnchor1 = "<a href=\"" + libraryManagerURL + "\">";
102+
closeAnchor1 = "</a>";
103+
}
104+
text = I18n.format(tr("Updates available for some of your {0}libraries{1}"), openAnchor1, closeAnchor1);
87105
} else if (!updatableLibraries && updatablePlatforms) {
88-
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), "<a href=\"http://boardsmanager/DropdownUpdatableCoresItem\">", "</a>");
106+
if (setAccessible) {
107+
button1Name = tr("boards");
108+
button2Name = null;
109+
}
110+
else {
111+
button1Name = null;
112+
button2Name = null;
113+
openAnchor1 = "<a href=\"" + boardsManagerURL + "\">";
114+
closeAnchor1 = "</a>";
115+
}
116+
text = I18n.format(tr("Updates available for some of your {0}boards{1}"), openAnchor1, closeAnchor1);
89117
} 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>");
118+
if (setAccessible) {
119+
button1Name = tr("boards");
120+
button2Name = tr("libraries");
121+
}
122+
else {
123+
button1Name = null;
124+
button2Name = null;
125+
openAnchor1 = "<a href=\"" + boardsManagerURL + "\">";
126+
closeAnchor1 = "</a>";
127+
openAnchor2 = "<a href=\"" + libraryManagerURL + "\">";
128+
closeAnchor2 = "</a>";
129+
}
130+
text = I18n.format(tr("Updates available for some of your {0}libraries{1} and {2}libraries{3}"), openAnchor1, closeAnchor1, openAnchor2, closeAnchor2);
91131
}
92132

93133
if (cancelled) {
@@ -96,7 +136,13 @@ public void run() {
96136

97137
SwingUtilities.invokeLater(() -> {
98138
Editor ed = base.getActiveEditor();
99-
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
139+
boolean accessibleIde = PreferencesData.getBoolean("ide.accessible");
140+
if (accessibleIde) {
141+
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text, false, this, button1Name, button2Name);
142+
}
143+
else { // if not accessible view leave it the same
144+
notificationPopup = new NotificationPopup(ed, hyperlinkListener, text);
145+
}
100146
if (ed.isFocused()) {
101147
notificationPopup.begin();
102148
return;
@@ -122,6 +168,23 @@ public void windowGainedFocus(WindowEvent evt) {
122168
});
123169
}
124170

171+
private void goToManager(String link) {
172+
try {
173+
((UpdatableBoardsLibsFakeURLsHandler) hyperlinkListener).openBoardLibManager(new URL(link));
174+
}
175+
catch (Exception e){
176+
}
177+
}
178+
// callback for boards button
179+
public void onOptionalButton1Callback() {
180+
goToManager(boardsManagerURL);
181+
}
182+
183+
// callback for libraries button
184+
public void onOptionalButton2Callback() {
185+
goToManager(libraryManagerURL);
186+
}
187+
125188
static boolean checkForUpdatablePlatforms() {
126189
return BaseNoGui.indexer.getPackages().stream()
127190
.flatMap(pack -> pack.getPlatforms().stream())

app/src/cc/arduino/view/NotificationPopup.java

Lines changed: 107 additions & 8 deletions
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

56+
import java.awt.event.KeyEvent;
57+
58+
import static processing.app.I18n.tr;
59+
6060
public class NotificationPopup extends JDialog {
61+
public interface OptionalButtonCallbacks {
62+
void onOptionalButton1Callback();
63+
void onOptionalButton2Callback();
64+
}
6165

6266
private Timer autoCloseTimer = new Timer(false);
6367
private boolean autoClose = true;
68+
private OptionalButtonCallbacks optionalButtonCallbacks;
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,89 @@ 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+
public void keyTyped(KeyEvent e) {
126+
}
127+
128+
public void keyPressed(KeyEvent e) {
129+
}
130+
131+
public void keyReleased(KeyEvent e) {
132+
int key = e.getKeyCode();
133+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
134+
optionalButtonCallbacks.onOptionalButton1Callback();
135+
}
136+
}
137+
};
138+
optionalButton1.addKeyListener(button1Key);
139+
add(optionalButton1);
140+
}
141+
142+
if (button2Name != null) {
143+
JButton optionalButton2 = new JButton(tr(button2Name));
144+
MouseAdapter button2Action = new MouseAdapter() {
145+
@Override
146+
public void mouseClicked(MouseEvent e) {
147+
if (optionalButtonCallbacks != null) {
148+
optionalButtonCallbacks.onOptionalButton2Callback();
149+
}
150+
}
151+
};
152+
optionalButton2.addMouseListener(button2Action);
153+
154+
KeyListener button2Key = new KeyListener() {
155+
public void keyTyped(KeyEvent e) {
156+
}
157+
158+
public void keyPressed(KeyEvent e) {
159+
}
160+
161+
public void keyReleased(KeyEvent e) {
162+
int key = e.getKeyCode();
163+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
164+
optionalButtonCallbacks.onOptionalButton2Callback();
165+
}
166+
}
167+
};
168+
optionalButton2.addKeyListener(button2Key);
169+
add(optionalButton2);
170+
}
171+
93172
Image close = Theme.getThemeImage("close", this, scale(22), scale(22));
94173
JButton closeButton = new JButton(new ImageIcon(close));
95174
closeButton.setBorder(null);
96175
closeButton.setBorderPainted(false);
97176
closeButton.setHideActionText(true);
98177
closeButton.setOpaque(false);
99178
closeButton.setBackground(new Color(0, 0, 0, 0));
179+
closeButton.getAccessibleContext().setAccessibleDescription(tr("Close"));
180+
KeyListener closeKey = new KeyListener() {
181+
public void keyTyped(KeyEvent e) {
182+
}
183+
184+
public void keyPressed(KeyEvent e) {
185+
}
186+
187+
public void keyReleased(KeyEvent e) {
188+
int key = e.getKeyCode();
189+
if ((key == KeyEvent.VK_ENTER) || (key == KeyEvent.VK_SPACE)) {
190+
close();
191+
}
192+
}
193+
};
194+
closeButton.addKeyListener(closeKey);
100195
add(closeButton);
101196

102197
MouseAdapter closeOnClick = new MouseAdapter() {
@@ -158,5 +253,9 @@ public void run() {
158253
}, Constants.NOTIFICATION_POPUP_AUTOCLOSE_DELAY);
159254
}
160255
setVisible(true);
256+
if (PreferencesData.getBoolean("ide.accessible")) {
257+
requestFocus();
258+
setModal(true);
259+
}
161260
}
162261
}

app/src/cc/arduino/view/preferences/Preferences.java

Lines changed: 8 additions & 1 deletion
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)