From f005ca59c1264ea10eb3ed2162447275a2eba91d Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Mon, 13 Jul 2026 15:24:56 -0700 Subject: [PATCH 1/5] 8384483: Create implementation of NSAccessibilityTable protocol - Create first implementation; - Fix incorrect verbosity level when navigating to the table from other component; --- .../scene/control/skin/TableViewSkin.java | 4 + .../native-glass/mac/a11y/AccessibleBase.h | 2 + .../native-glass/mac/a11y/AccessibleBase.m | 31 ++++ .../mac/a11y/JFXTableAccessibility.h | 48 ++++++ .../mac/a11y/JFXTableAccessibility.m | 148 ++++++++++++++++++ 5 files changed, 233 insertions(+) create mode 100644 modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.h create mode 100644 modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java index d4f3dff7a01..8f6f9acb44d 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkin.java @@ -147,6 +147,10 @@ public void dispose() { /** {@inheritDoc} */ @Override public Object queryAccessibleAttribute(AccessibleAttribute attribute, Object... parameters) { switch (attribute) { + case ROW_AT_INDEX: { + final int rowIndex = (Integer)parameters[0]; + return rowIndex < 0 ? null : flow.getPrivateCell(rowIndex); + } case SELECTED_ITEMS: { List selection = new ArrayList<>(); TableViewSelectionModel sm = getSkinnable().getSelectionModel(); diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h index 084dda6e821..2c447fcca31 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h @@ -41,6 +41,8 @@ id jRole; - (id)requestNodeAttribute:(NSString *)attribute; - (id)requestNodeAttribute:(NSString *)attribute forParameter:(id)parameter; - (BOOL)isNodeAttributeSettable:(NSString *)attribute; +- (NSInteger)requestNodeArrayAttributeCount:(NSString *)attribute; +- (NSArray *)requestNodeArrayAttribute:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount; - (void)setNodeAttribute:(id)value forAttribute:(NSString *)attribute; - (NSString *)accessibilityPlaceholderValue; - (NSRect)accessibilityFrame; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m index 29d56239a1d..ad2a4c4d9be 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m @@ -29,6 +29,7 @@ #import "com_sun_glass_ui_mac_MacAccessible.h" #import "com_sun_glass_ui_mac_MacVariant.h" #import "common.h" +#include static NSMutableDictionary * rolesMap; @@ -50,6 +51,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"JFXImageAccessibility" forKey:@"IMAGE"]; [rolesMap setObject:@"JFXImageAccessibility" forKey:@"IMAGE_VIEW"]; [rolesMap setObject:@"JFXButtonAccessibility" forKey:@"INCREMENT_BUTTON"]; + [rolesMap setObject:@"JFXTableAccessibility" forKey:@"LIST_VIEW"]; [rolesMap setObject:@"JFXMenuItemAccessibility" forKey:@"MENU"]; [rolesMap setObject:@"JFXMenuBarAccessibility" forKey:@"MENU_BAR"]; [rolesMap setObject:@"JFXMenuItemAccessibility" forKey:@"MENU_ITEM"]; @@ -63,6 +65,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"JFXButtonAccessibility" forKey:@"SPLIT_MENU_BUTTON"]; [rolesMap setObject:@"JFXRadiobuttonAccessibility" forKey:@"TAB_ITEM"]; [rolesMap setObject:@"JFXTabGroupAccessibility" forKey:@"TAB_PANE"]; + [rolesMap setObject:@"JFXTableAccessibility" forKey:@"TABLE_VIEW"]; [rolesMap setObject:@"JFXStaticTextAccessibility" forKey:@"TEXT"]; [rolesMap setObject:@"JFXNavigableTextAccessibility" forKey:@"TEXT_AREA"]; [rolesMap setObject:@"JFXNavigableTextAccessibility" forKey:@"TEXT_FIELD"]; @@ -151,6 +154,34 @@ - (id)requestNodeAttribute:(NSString *)attribute forParameter:(id)parameter return variantToID(env, jresult); } +- (NSInteger)requestNodeArrayAttributeCount:(NSString *)attribute +{ + GET_MAIN_JENV; + if (env == NULL) { + return -1; + } + jint jresult = (*env)->CallIntMethod(env, [self getJAccessible], + jAccessibilityArrayAttributeCount, + (jlong)attribute); + GLASS_CHECK_EXCEPTION(env); + return jresult; +} + +- (NSArray *)requestNodeArrayAttribute:(NSString *)attribute index:(NSUInteger)index maxCount:(NSUInteger)maxCount +{ + GET_MAIN_JENV; + if (env == NULL) { + return nil; + } + jlongArray jresult = (jlongArray)(*env)->CallObjectMethod(env, [self getJAccessible], + jAccessibilityArrayAttributeValues, + (jlong)attribute, + (jint)index, + (jint)maxCount); + GLASS_CHECK_EXCEPTION(env); + return jArrayToNSArray(env, jresult, jLongToID); +} + /* * Check whether an accessibility attribute on the JavaFX Node can be set. */ diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.h b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.h new file mode 100644 index 00000000000..abf01bc40ca --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#import "AccessibleBase.h" +#import + +@interface JFXTableAccessibility : AccessibleBase { +}; + +- (NSAccessibilityRole)accessibilityRole; +- (NSString *)accessibilityLabel; +- (BOOL)isAccessibilityEnabled; +- (NSArray *)accessibilityChildren; +- (id)accessibilityParent; +- (NSRect)accessibilityFrame; +- (id)accessibilityHeader; +- (NSInteger)accessibilityColumnCount; +- (NSInteger)accessibilityRowCount; +- (NSArray *)accessibilityColumns; +- (NSArray *)accessibilityRows; +- (NSArray *)accessibilitySelectedRows; +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows; +- (NSArray *)accessibilitySelectedCells; +- (void)setAccessibilitySelectedCells:(NSArray *)selectedCells; +- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row; +@end diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m new file mode 100644 index 00000000000..aecc51c6e91 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#import "JFXTableAccessibility.h" + +@implementation JFXTableAccessibility +- (NSAccessibilityRole)accessibilityRole +{ + return NSAccessibilityTableRole; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel]; +} + +- (BOOL)isAccessibilityEnabled +{ + id retVal = [self requestNodeAttribute:@"AXEnabled"]; + if (retVal == NULL) { + return YES; + } + return [retVal boolValue]; +} + +- (NSArray *)accessibilityChildren +{ + return [super accessibilityChildren]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (id)accessibilityHeader +{ + return [self requestNodeAttribute:@"AXHeader"]; +} + +- (NSInteger)accessibilityColumnCount +{ + id retVal = [self requestNodeAttribute:@"AXColumnCount"]; + if (retVal == NULL) { + return [self accessibilityArrayCountForAttribute:@"AXColumns"]; + } + return [retVal integerValue]; +} + +- (NSInteger)accessibilityRowCount +{ + id retVal = [self requestNodeAttribute:@"AXRowCount"]; + if (retVal == NULL) { + return [self accessibilityArrayCountForAttribute:@"AXRows"]; + } + return [retVal integerValue]; +} + +- (NSArray *)accessibilityColumns +{ + return [self accessibilityArrayForAttribute:@"AXColumns"]; +} + +- (NSArray *)accessibilityRows +{ + return [self accessibilityArrayForAttribute:@"AXRows"]; +} + +- (NSArray *)accessibilitySelectedRows +{ + return [self requestNodeAttribute:@"AXSelectedRows"]; +} + +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows +{ + [self setNodeAttribute:selectedRows forAttribute:@"AXSelectedRows"]; +} + +- (NSArray *)accessibilitySelectedCells +{ + return [self requestNodeAttribute:@"AXSelectedCells"]; +} + +- (void)setAccessibilitySelectedCells:(NSArray *)selectedCells +{ + [self setNodeAttribute:selectedCells forAttribute:@"AXSelectedCells"]; +} + +- (id)accessibilityCellForColumn:(NSInteger)column row:(NSInteger)row +{ + NSArray *parameter = @[[NSNumber numberWithInteger:column], + [NSNumber numberWithInteger:row]]; + return [self requestNodeAttribute:@"AXCellForColumnAndRow" forParameter:parameter]; +} + +- (NSArray *)accessibilityArrayForAttribute:(NSString *)attribute +{ + NSInteger count = [self accessibilityArrayCountForAttribute:attribute]; + if (count < 0) { + return nil; + } + if (count == 0) { + return @[]; + } + NSArray *result = [self requestNodeArrayAttribute:attribute index:0 maxCount:(NSUInteger)count]; + if (result == nil) { + result = [super accessibilityArrayAttributeValues:attribute index:0 maxCount:(NSUInteger)count]; + } + return result; +} + +- (NSInteger)accessibilityArrayCountForAttribute:(NSString *)attribute +{ + NSInteger count = [self requestNodeArrayAttributeCount:attribute]; + if (count < 0) { + count = [super accessibilityArrayAttributeCount:attribute]; + } + return count; +} + +@end From 52d6cc5f61f751c76f0f9f772d3461248583b0cb Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Wed, 15 Jul 2026 12:30:30 -0700 Subject: [PATCH 2/5] - Fixing the incorrect value being returned for the NSAccessibilitySelectedRowsAttribute. We should select a set of rows, not a set of cells because that confuses VoiceOver and it falls back to reporting as first row is selected. --- .../com/sun/glass/ui/mac/MacAccessible.java | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java index 6651b10af80..b2cb72b8f87 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2026, Oracle and/or its affiliates. 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 @@ -27,7 +27,9 @@ import java.util.ArrayList; import java.util.Arrays; +import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; import java.util.function.Function; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -1300,6 +1302,31 @@ private MacVariant accessibilityAttributeValue(long attribute) { return null; } Object result = getAttribute(jfxAttr); + if (attr == MacAttribute.NSAccessibilitySelectedRowsAttribute + && role == AccessibleRole.TABLE_VIEW) { + @SuppressWarnings("unchecked") + ObservableList selectedCells = (ObservableList)result; + Set selectedRows = new LinkedHashSet<>(); + if (selectedCells != null) { + for (Node cell : selectedCells) { + Accessible cellAccessible = getAccessible(cell); + if (cellAccessible == null) { + continue; + } + + Integer rowIndex = (Integer)cellAccessible.getAttribute(ROW_INDEX); + if (rowIndex == null) { + continue; + } + + Node row = (Node)getAttribute(ROW_AT_INDEX, rowIndex); + if (row != null) { + selectedRows.add(row); + } + } + } + result = FXCollections.observableArrayList(selectedRows); + } if (result == null) { switch (attr) { case NSAccessibilityParentAttribute: break; From 99f9c70bf69e1cdcf249e4e72ad2dadbdbc197f7 Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Wed, 15 Jul 2026 15:43:11 -0700 Subject: [PATCH 3/5] - Fix VoiceOver navigation indose ListView by creating a separate native accessibility peer for Lists; - Expose the focused item trough the corresponding function in AccessibleBase; - Resolve issue with incorrect row selection in MacAccessible; --- .../com/sun/glass/ui/mac/MacAccessible.java | 5 +- .../native-glass/mac/a11y/AccessibleBase.h | 1 + .../native-glass/mac/a11y/AccessibleBase.m | 12 +- .../mac/a11y/JFXListAccessibility.h | 43 +++++++ .../mac/a11y/JFXListAccessibility.m | 119 ++++++++++++++++++ 5 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.h create mode 100644 modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java index b2cb72b8f87..3a3a04d5732 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java @@ -1723,12 +1723,15 @@ private void accessibilitySetValue(long value, long attribute) { if (variant != null && variant.longArray != null && variant.longArray.length > 0) { long[] ids = variant.longArray; ObservableList items = FXCollections.observableArrayList(); + AccessibleRole controlRole = (AccessibleRole)getAttribute(ROLE); for (long id : ids) { MacAccessible acc = GlassAccessibleToMacAccessible(id); if (acc != null) { Integer index = (Integer)acc.getAttribute(INDEX); if (index != null) { - Node cell = (Node)getAttribute(ROW_AT_INDEX, index); + AccessibleAttribute itemAttribute = controlRole == AccessibleRole.LIST_VIEW + ? ITEM_AT_INDEX : ROW_AT_INDEX; + Node cell = (Node)getAttribute(itemAttribute, index); if (cell != null) { items.add(cell); } diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h index 2c447fcca31..0f01329c63b 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.h @@ -47,6 +47,7 @@ id jRole; - (NSString *)accessibilityPlaceholderValue; - (NSRect)accessibilityFrame; - (id)accessibilityParent; +- (id)accessibilityFocusedUIElement; - (BOOL)isAccessibilityElement; - (BOOL)performAccessibleAction:(NSString*)actionId; + (void) initializeRolesMap; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m index ad2a4c4d9be..11f5822735e 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/AccessibleBase.m @@ -51,7 +51,7 @@ + (void) initializeRolesMap { [rolesMap setObject:@"JFXImageAccessibility" forKey:@"IMAGE"]; [rolesMap setObject:@"JFXImageAccessibility" forKey:@"IMAGE_VIEW"]; [rolesMap setObject:@"JFXButtonAccessibility" forKey:@"INCREMENT_BUTTON"]; - [rolesMap setObject:@"JFXTableAccessibility" forKey:@"LIST_VIEW"]; + [rolesMap setObject:@"JFXListAccessibility" forKey:@"LIST_VIEW"]; [rolesMap setObject:@"JFXMenuItemAccessibility" forKey:@"MENU"]; [rolesMap setObject:@"JFXMenuBarAccessibility" forKey:@"MENU_BAR"]; [rolesMap setObject:@"JFXMenuItemAccessibility" forKey:@"MENU_ITEM"]; @@ -258,6 +258,16 @@ - (NSArray *)accessibilityChildren return [self requestNodeAttribute:@"AXChildren"]; } +- (id)accessibilityFocusedUIElement +{ + GET_MAIN_JENV; + if (env == NULL) return NULL; + id result = (id)(*env)->CallLongMethod(env, self->jAccessible, + jAccessibilityFocusedUIElement); + GLASS_CHECK_EXCEPTION(env); + return result; +} + - (id)accessibilityRoleDescription { return [self requestNodeAttribute:@"AXRoleDescription"]; diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.h b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.h new file mode 100644 index 00000000000..33473d3b7a1 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#import "AccessibleBase.h" +#import + +@interface JFXListAccessibility : AccessibleBase { +}; + +- (NSAccessibilityRole)accessibilityRole; +- (NSString *)accessibilityLabel; +- (BOOL)isAccessibilityEnabled; +- (NSArray *)accessibilityChildren; +- (id)accessibilityParent; +- (NSRect)accessibilityFrame; +- (NSInteger)accessibilityColumnCount; +- (NSInteger)accessibilityRowCount; +- (NSArray *)accessibilityRows; +- (NSArray *)accessibilitySelectedRows; +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows; +@end diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m new file mode 100644 index 00000000000..c4f9acd3f07 --- /dev/null +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2026, Oracle and/or its affiliates. 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. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * 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. + */ + +#import "JFXListAccessibility.h" + +@implementation JFXListAccessibility +- (NSAccessibilityRole)accessibilityRole +{ + // This can change to NSAccessibilityListRole in the future. + return NSAccessibilityTableRole; +} + +- (NSString *)accessibilityLabel +{ + return [super accessibilityLabel]; +} + +- (BOOL)isAccessibilityEnabled +{ + id retVal = [self requestNodeAttribute:@"AXEnabled"]; + if (retVal == NULL) { + return YES; + } + return [retVal boolValue]; +} + +- (NSArray *)accessibilityChildren +{ + return [super accessibilityChildren]; +} + +- (id)accessibilityParent +{ + return [super accessibilityParent]; +} + +- (NSRect)accessibilityFrame +{ + return [super accessibilityFrame]; +} + +- (NSInteger)accessibilityColumnCount +{ + id retVal = [self requestNodeAttribute:@"AXColumnCount"]; + return retVal != NULL ? [retVal integerValue] : 1; +} + +- (NSInteger)accessibilityRowCount +{ + id retVal = [self requestNodeAttribute:@"AXRowCount"]; + if (retVal == NULL) { + return [self accessibilityArrayCountForAttribute:@"AXRows"]; + } + return [retVal integerValue]; +} + +- (NSArray *)accessibilityRows +{ + return [self accessibilityArrayForAttribute:@"AXRows"]; +} + +- (NSArray *)accessibilitySelectedRows +{ + return [self requestNodeAttribute:@"AXSelectedRows"]; +} + +- (void)setAccessibilitySelectedRows:(NSArray *)selectedRows +{ + [self setNodeAttribute:selectedRows forAttribute:@"AXSelectedRows"]; +} + +- (NSArray *)accessibilityArrayForAttribute:(NSString *)attribute +{ + NSInteger count = [self accessibilityArrayCountForAttribute:attribute]; + if (count < 0) { + return nil; + } + if (count == 0) { + return @[]; + } + NSArray *result = [self requestNodeArrayAttribute:attribute index:0 maxCount:(NSUInteger)count]; + if (result == nil) { + result = [super accessibilityArrayAttributeValues:attribute index:0 maxCount:(NSUInteger)count]; + } + return result; +} + +- (NSInteger)accessibilityArrayCountForAttribute:(NSString *)attribute +{ + NSInteger count = [self requestNodeArrayAttributeCount:attribute]; + if (count < 0) { + count = [super accessibilityArrayAttributeCount:attribute]; + } + return count; +} + +@end From e93291ebe42e74b8ce40a077e74d4f7ff15e6931 Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Thu, 6 Aug 2026 11:52:21 -0700 Subject: [PATCH 4/5] Fixing the performance on the large tables and lists. In order to do so: - Always return the NSArray of the appropriate size, otherwice VO reports the table size incorrectly; - To avoid querying of the entire content of the control fill in only visible rows +/- additional 10 to avoid incorrect reporting due to the VO caching when navigating with VO controls beyond the visible range; - To request the visible range from the control's skin a new a11y attribute added - VISIBLE_ITEM_RANGE. Since outside of the MacOS it is not used that will not create any problems on other platforms; --- .../scene/control/skin/ListViewSkin.java | 9 +++ .../scene/control/skin/TableViewSkinBase.java | 9 +++ .../com/sun/glass/ui/mac/MacAccessible.java | 1 + .../javafx/scene/AccessibleAttribute.java | 12 ++++ .../mac/a11y/JFXListAccessibility.m | 59 ++++++++++++++++++- .../mac/a11y/JFXTableAccessibility.m | 59 ++++++++++++++++++- 6 files changed, 147 insertions(+), 2 deletions(-) diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java index dd72252f675..79f28d13c08 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/ListViewSkin.java @@ -376,6 +376,15 @@ public ListViewSkin(final ListView control) { } return null; } + case VISIBLE_ITEM_RANGE: { + ListCell firstVisibleCell = flow.getFirstVisibleCellWithinViewport(); + ListCell lastVisibleCell = flow.getLastVisibleCellWithinViewport(); + if (firstVisibleCell == null || lastVisibleCell == null) { + return new int[] { 0, 0 }; + } + int firstIndex = firstVisibleCell.getIndex(); + return new int[] { firstIndex, lastVisibleCell.getIndex() - firstIndex + 1 }; + } case SELECTED_ITEMS: { MultipleSelectionModel sm = getSkinnable().getSelectionModel(); if (sm == null) { diff --git a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java index 5cd4bc7293b..657d5798cf0 100644 --- a/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java +++ b/modules/javafx.controls/src/main/java/javafx/scene/control/skin/TableViewSkinBase.java @@ -1066,6 +1066,15 @@ private boolean isCellFocused(int row) { * look for column headers */ return getTableHeaderRow(); } + case VISIBLE_ITEM_RANGE: { + I firstVisibleCell = flow.getFirstVisibleCellWithinViewport(); + I lastVisibleCell = flow.getLastVisibleCellWithinViewport(); + if (firstVisibleCell == null || lastVisibleCell == null) { + return new int[] { 0, 0 }; + } + int firstIndex = firstVisibleCell.getIndex(); + return new int[] { firstIndex, lastVisibleCell.getIndex() - firstIndex + 1 }; + } case VERTICAL_SCROLLBAR: return flow.getVbar(); case HORIZONTAL_SCROLLBAR: return flow.getHbar(); default: return super.queryAccessibleAttribute(attribute, parameters); diff --git a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java index 3a3a04d5732..0391c300f9a 100644 --- a/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java +++ b/modules/javafx.graphics/src/main/java/com/sun/glass/ui/mac/MacAccessible.java @@ -121,6 +121,7 @@ private static enum MacAttribute { AXMenuItemCmdGlyph(ACCELERATOR, MacVariant::createNSNumberForInt), AXMenuItemCmdModifiers(ACCELERATOR, MacVariant::createNSNumberForInt), AXMenuItemMarkChar(SELECTED, MacVariant::createNSString), + AXVisibleItemRange(VISIBLE_ITEM_RANGE, MacVariant::createNSValueForRange), AXDateTimeComponents(null, MacVariant::createNSNumberForInt), // NSAccessibilityMenuRole diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java b/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java index 95cc1900a34..841427f17c5 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java @@ -801,6 +801,18 @@ public enum AccessibleAttribute { */ VERTICAL_SCROLLBAR(Node.class), + /** + * Returns the range of visible items in an indexed control. + *
    + *
  • Used by: ListView and TableView
  • + *
  • Needs notify: no
  • + *
  • Return Type: {@code int[]} containing the first item index and the + * number of visible items
  • + *
  • Parameters:
  • + *
+ */ + VISIBLE_ITEM_RANGE(int[].class), + /** * Returns true if node is visible, otherwise false. *
    diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m index c4f9acd3f07..c807921b100 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXListAccessibility.m @@ -25,6 +25,32 @@ #import "JFXListAccessibility.h" +@interface JFXListRowPlaceholder : NSAccessibilityElement +@end + +@implementation JFXListRowPlaceholder + +- (jobject)getJAccessible +{ + return nil; +} + +@end + +static const NSInteger ACCESSIBILITY_ROW_WINDOW = 10; +static JFXListRowPlaceholder *rowPlaceholder; +static dispatch_once_t rowPlaceholderOnce; + +static id getRowPlaceholder(void) +{ + dispatch_once(&rowPlaceholderOnce, ^{ + rowPlaceholder = [[JFXListRowPlaceholder alloc] init]; + [rowPlaceholder setAccessibilityRole:NSAccessibilityUnknownRole]; + [rowPlaceholder setAccessibilityElement:NO]; + }); + return rowPlaceholder; +} + @implementation JFXListAccessibility - (NSAccessibilityRole)accessibilityRole { @@ -78,7 +104,38 @@ - (NSInteger)accessibilityRowCount - (NSArray *)accessibilityRows { - return [self accessibilityArrayForAttribute:@"AXRows"]; + NSInteger count = [self accessibilityArrayCountForAttribute:@"AXRows"]; + if (count < 0) { + return nil; + } + + NSMutableArray *rows = [NSMutableArray arrayWithCapacity:(NSUInteger)count]; + id placeholder = getRowPlaceholder(); + for (NSInteger index = 0; index < count; index++) { + [rows addObject:placeholder]; + } + + if (count > 0) { + NSInteger firstRequestedRow = 0; + NSInteger lastRequestedRow = MIN(count - 1, ACCESSIBILITY_ROW_WINDOW - 1); + NSRange visibleRange = [[self requestNodeAttribute:@"AXVisibleItemRange"] rangeValue]; + if (visibleRange.length > 0 && visibleRange.location < (NSUInteger)count) { + NSInteger firstVisibleRow = (NSInteger)visibleRange.location; + NSInteger lastVisibleRow = MIN(count - 1, + firstVisibleRow + (NSInteger)visibleRange.length - 1); + firstRequestedRow = MAX(0, firstVisibleRow - ACCESSIBILITY_ROW_WINDOW); + lastRequestedRow = MIN(count - 1, lastVisibleRow + ACCESSIBILITY_ROW_WINDOW); + } + NSUInteger requestedRowCount = (NSUInteger)(lastRequestedRow - firstRequestedRow + 1); + NSArray *requestedRows = [self requestNodeArrayAttribute:@"AXRows" + index:(NSUInteger)firstRequestedRow + maxCount:requestedRowCount]; + for (NSUInteger index = 0; index < requestedRows.count; index++) { + [rows replaceObjectAtIndex:(NSUInteger)firstRequestedRow + index + withObject:[requestedRows objectAtIndex:index]]; + } + } + return rows; } - (NSArray *)accessibilitySelectedRows diff --git a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m index aecc51c6e91..5b2a71a97fc 100644 --- a/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m +++ b/modules/javafx.graphics/src/main/native-glass/mac/a11y/JFXTableAccessibility.m @@ -25,6 +25,32 @@ #import "JFXTableAccessibility.h" +@interface JFXTableRowPlaceholder : NSAccessibilityElement +@end + +@implementation JFXTableRowPlaceholder + +- (jobject)getJAccessible +{ + return nil; +} + +@end + +static const NSInteger ACCESSIBILITY_ROW_WINDOW = 10; +static JFXTableRowPlaceholder *rowPlaceholder; +static dispatch_once_t rowPlaceholderOnce; + +static id getRowPlaceholder(void) +{ + dispatch_once(&rowPlaceholderOnce, ^{ + rowPlaceholder = [[JFXTableRowPlaceholder alloc] init]; + [rowPlaceholder setAccessibilityRole:NSAccessibilityUnknownRole]; + [rowPlaceholder setAccessibilityElement:NO]; + }); + return rowPlaceholder; +} + @implementation JFXTableAccessibility - (NSAccessibilityRole)accessibilityRole { @@ -90,7 +116,38 @@ - (NSArray *)accessibilityColumns - (NSArray *)accessibilityRows { - return [self accessibilityArrayForAttribute:@"AXRows"]; + NSInteger count = [self accessibilityArrayCountForAttribute:@"AXRows"]; + if (count < 0) { + return nil; + } + + NSMutableArray *rows = [NSMutableArray arrayWithCapacity:(NSUInteger)count]; + id placeholder = getRowPlaceholder(); + for (NSInteger index = 0; index < count; index++) { + [rows addObject:placeholder]; + } + + if (count > 0) { + NSInteger firstRequestedRow = 0; + NSInteger lastRequestedRow = MIN(count - 1, ACCESSIBILITY_ROW_WINDOW - 1); + NSRange visibleRange = [[self requestNodeAttribute:@"AXVisibleItemRange"] rangeValue]; + if (visibleRange.length > 0 && visibleRange.location < (NSUInteger)count) { + NSInteger firstVisibleRow = (NSInteger)visibleRange.location; + NSInteger lastVisibleRow = MIN(count - 1, + firstVisibleRow + (NSInteger)visibleRange.length - 1); + firstRequestedRow = MAX(0, firstVisibleRow - ACCESSIBILITY_ROW_WINDOW); + lastRequestedRow = MIN(count - 1, lastVisibleRow + ACCESSIBILITY_ROW_WINDOW); + } + NSUInteger requestedRowCount = (NSUInteger)(lastRequestedRow - firstRequestedRow + 1); + NSArray *requestedRows = [self requestNodeArrayAttribute:@"AXRows" + index:(NSUInteger)firstRequestedRow + maxCount:requestedRowCount]; + for (NSUInteger index = 0; index < requestedRows.count; index++) { + [rows replaceObjectAtIndex:(NSUInteger)firstRequestedRow + index + withObject:[requestedRows objectAtIndex:index]]; + } + } + return rows; } - (NSArray *)accessibilitySelectedRows From 8172963713b219c6cdadd382e5803404ffa215dc Mon Sep 17 00:00:00 2001 From: Alexander Zuev Date: Thu, 13 Aug 2026 12:22:18 -0700 Subject: [PATCH 5/5] Added @since annotation. --- .../src/main/java/javafx/scene/AccessibleAttribute.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java b/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java index 841427f17c5..af2ce016819 100644 --- a/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java +++ b/modules/javafx.graphics/src/main/java/javafx/scene/AccessibleAttribute.java @@ -810,6 +810,8 @@ public enum AccessibleAttribute { * number of visible items *
  • Parameters:
  • *
+ * + * @since 28 */ VISIBLE_ITEM_RANGE(int[].class),