mirror of
https://github.com/JetBrains/JetBrainsRuntime.git
synced 2025-12-06 09:29:38 +01:00
JBR-6194 Fix VoiceOver reading old JComboBox value after changing it
1. Remove `value == nil` check in ComboBoxAccessiblity.accessibilityValue to fix the issue with not updated value of combo box. With `value == nil` check, the value was not reassigned on every get request of `accessibilityValue`, but only on get `accessibilitySelectedChildren`. When changing focus by Tab, only get `accessibilityValue` is called, and because `value` is already not nil, an old value was returned.
2. Set combo box role to NSAccessibilityPopUpButtonRole if it's not editable. Setting role to popup button fixes the bug when combo box value was not updated when using VO cursor navigation. Native MacOS non-editable combo boxes and non-editable HTML <select> elements also have the "popup button" role instead of "combo box", so the role should become more clear. Popup button role additionally enables opening the combo box menu with VO+Space shortcut, and changes VO instructions to be more appropriate when combo box is focused.
3. Add test for VoiceOver-specific issues of JComboBox.
(cherry picked from commit 8982db51d7)
This commit is contained in:
committed by
Nikita Provotorov
parent
4a8b300f2b
commit
556f48375a
@@ -59,6 +59,7 @@ import javax.accessibility.AccessibleTable;
|
||||
import javax.accessibility.AccessibleText;
|
||||
import javax.accessibility.AccessibleValue;
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JEditorPane;
|
||||
import javax.swing.JLabel;
|
||||
@@ -1138,4 +1139,18 @@ class CAccessibility implements PropertyChangeListener {
|
||||
}
|
||||
}, c);
|
||||
}
|
||||
|
||||
private static boolean isComboBoxEditable(Accessible a, Component c) {
|
||||
if (a == null) return false;
|
||||
|
||||
return invokeAndWait(new Callable<Boolean>() {
|
||||
public Boolean call() throws Exception {
|
||||
Accessible sa = CAccessible.getSwingAccessible(a);
|
||||
if (sa instanceof JComboBox<?> comboBox) {
|
||||
return comboBox.isEditable();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}, c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,18 @@ static jmethodID sjm_getAccessibleName = NULL;
|
||||
return value;
|
||||
}
|
||||
|
||||
- (BOOL)isEditable
|
||||
{
|
||||
JNIEnv* env = [ThreadUtilities getJNIEnv];
|
||||
GET_CACCESSIBILITY_CLASS_RETURN(NO);
|
||||
DECLARE_STATIC_METHOD_RETURN(sjm_isComboBoxEditable, sjc_CAccessibility, "isComboBoxEditable", "(Ljavax/accessibility/Accessible;Ljava/awt/Component;)Z", NO);
|
||||
|
||||
BOOL isEditable = (*env)->CallStaticBooleanMethod(env, sjc_CAccessibility, sjm_isComboBoxEditable, fAccessible, fComponent);
|
||||
CHECK_EXCEPTION();
|
||||
|
||||
return isEditable;
|
||||
}
|
||||
|
||||
// NSAccessibilityElement protocol methods
|
||||
|
||||
- (id)accessibilityValue
|
||||
@@ -66,12 +78,10 @@ static jmethodID sjm_getAccessibleName = NULL;
|
||||
if (expanded) {
|
||||
return nil;
|
||||
}
|
||||
if (!expanded &&
|
||||
(value == nil)) {
|
||||
[self accessibleSelection];
|
||||
}
|
||||
|
||||
return [value accessibilityLabel];
|
||||
[self accessibleSelection];
|
||||
|
||||
return value != nil ? [value accessibilityLabel] : nil;
|
||||
}
|
||||
|
||||
- (NSArray *)accessibilitySelectedChildren
|
||||
@@ -79,6 +89,13 @@ static jmethodID sjm_getAccessibleName = NULL;
|
||||
return [NSArray arrayWithObject:[self accessibleSelection]];
|
||||
}
|
||||
|
||||
- (NSAccessibilityRole)accessibilityRole
|
||||
{
|
||||
return [self isEditable]
|
||||
? NSAccessibilityComboBoxRole
|
||||
: NSAccessibilityPopUpButtonRole;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
if (value != nil) {
|
||||
|
||||
86
test/jdk/java/awt/a11y/AccessibleJComboBoxVoiceOverTest.java
Normal file
86
test/jdk/java/awt/a11y/AccessibleJComboBoxVoiceOverTest.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2023, JetBrains s.r.o.. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary Test for VoiceOver-specific issues of JComboBox
|
||||
* @author dmitry.drobotov@jetbrains.com
|
||||
* @run main/manual AccessibleJComboBoxVoiceOverTest
|
||||
* @requires (os.family == "mac")
|
||||
*/
|
||||
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.SwingUtilities;
|
||||
import java.awt.FlowLayout;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class AccessibleJComboBoxVoiceOverTest extends AccessibleComponentTest {
|
||||
|
||||
@java.lang.Override
|
||||
public CountDownLatch createCountDownLatch() {
|
||||
return new CountDownLatch(1);
|
||||
}
|
||||
|
||||
void createCombobox() {
|
||||
INSTRUCTIONS = """
|
||||
INSTRUCTIONS:
|
||||
Check VoiceOver-specific issues of JComboBox.
|
||||
|
||||
Turn VoiceOver on, and Tab to the combo box.
|
||||
|
||||
Use VO+Space shortcut to open the combo box and select a new item.
|
||||
Move keyboard focus away from the combo box using Shift+Tab and then back to the combo box by Tab.
|
||||
Repeat the same step with VoiceOver cursor navigation using VO+Left and VO+Right.
|
||||
|
||||
If in both cases VoiceOver reads the newly selected value, press PASS, otherwise press FAIL.""";
|
||||
|
||||
JPanel frame = new JPanel();
|
||||
|
||||
String[] NAMES = {"One", "Two", "Three", "Four", "Five"};
|
||||
JComboBox<String> combo = new JComboBox<>(NAMES);
|
||||
|
||||
JLabel label = new JLabel("This is combobox:");
|
||||
label.setLabelFor(combo);
|
||||
|
||||
frame.setLayout(new FlowLayout());
|
||||
frame.add(label);
|
||||
frame.add(combo);
|
||||
exceptionString = "AccessibleJComboBoxVoiceOver test failed!";
|
||||
super.createUI(frame, "AccessibleJComboBoxVoiceOverTest");
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
AccessibleJComboBoxVoiceOverTest test = new AccessibleJComboBoxVoiceOverTest();
|
||||
|
||||
countDownLatch = test.createCountDownLatch();
|
||||
SwingUtilities.invokeLater(test::createCombobox);
|
||||
countDownLatch.await();
|
||||
|
||||
if (!testResult) {
|
||||
throw new RuntimeException(a11yTest.exceptionString);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user