macOS Accessibility: optimize table interface implementation

Store row and column in the QMacAccessibilityElement when creating it so
that we can avoid linearly looking for ourselves in the parent's data.

Row elements have their m_rowIndex set, Column elements the
m_columnIndex, and elements representing a cell have both set. Cells
are not managed by the table.

Task-number: QTBUG-34337
Pick-to: 6.5
Change-Id: I319fad1f1fda0cfa4c0b95e9e16c25c87df04351
Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io>
This commit is contained in:
Volker Hilsheimer 2023-04-07 18:44:30 +02:00
parent 11ae55e918
commit 042d753495
2 changed files with 57 additions and 43 deletions

View File

@ -117,7 +117,6 @@ static void populateRoleMap()
roleMap[QAccessible::ColumnHeader] = NSAccessibilityColumnRole; roleMap[QAccessible::ColumnHeader] = NSAccessibilityColumnRole;
roleMap[QAccessible::Row] = NSAccessibilityRowRole; roleMap[QAccessible::Row] = NSAccessibilityRowRole;
roleMap[QAccessible::RowHeader] = NSAccessibilityRowRole; roleMap[QAccessible::RowHeader] = NSAccessibilityRowRole;
roleMap[QAccessible::Cell] = NSAccessibilityTextFieldRole;
roleMap[QAccessible::Button] = NSAccessibilityButtonRole; roleMap[QAccessible::Button] = NSAccessibilityButtonRole;
roleMap[QAccessible::EditableText] = NSAccessibilityTextFieldRole; roleMap[QAccessible::EditableText] = NSAccessibilityTextFieldRole;
roleMap[QAccessible::Link] = NSAccessibilityLinkRole; roleMap[QAccessible::Link] = NSAccessibilityLinkRole;
@ -125,7 +124,7 @@ static void populateRoleMap()
roleMap[QAccessible::Splitter] = NSAccessibilitySplitGroupRole; roleMap[QAccessible::Splitter] = NSAccessibilitySplitGroupRole;
roleMap[QAccessible::List] = NSAccessibilityListRole; roleMap[QAccessible::List] = NSAccessibilityListRole;
roleMap[QAccessible::ListItem] = NSAccessibilityStaticTextRole; roleMap[QAccessible::ListItem] = NSAccessibilityStaticTextRole;
roleMap[QAccessible::Cell] = NSAccessibilityStaticTextRole; roleMap[QAccessible::Cell] = NSAccessibilityCellRole;
roleMap[QAccessible::Client] = NSAccessibilityGroupRole; roleMap[QAccessible::Client] = NSAccessibilityGroupRole;
roleMap[QAccessible::Paragraph] = NSAccessibilityGroupRole; roleMap[QAccessible::Paragraph] = NSAccessibilityGroupRole;
roleMap[QAccessible::Section] = NSAccessibilityGroupRole; roleMap[QAccessible::Section] = NSAccessibilityGroupRole;

View File

@ -80,6 +80,8 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
@implementation QMacAccessibilityElement { @implementation QMacAccessibilityElement {
QAccessible::Id axid; QAccessible::Id axid;
int m_rowIndex;
int m_columnIndex;
// used by NSAccessibilityTable // used by NSAccessibilityTable
NSMutableArray<QMacAccessibilityElement *> *rows; // corresponds to accessibilityRows NSMutableArray<QMacAccessibilityElement *> *rows; // corresponds to accessibilityRows
@ -105,12 +107,25 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
self = [super init]; self = [super init];
if (self) { if (self) {
axid = anId; axid = anId;
m_rowIndex = -1;
m_columnIndex = -1;
rows = nil; rows = nil;
columns = nil; columns = nil;
synthesizedRole = role; synthesizedRole = role;
QAccessibleInterface *iface = QAccessible::accessibleInterface(axid); // table: if this is not created as an element managed by the table, then
if (iface && iface->tableInterface() && !synthesizedRole) // it's either the table itself, or an element created for an already existing
[self updateTableModel]; // cell interface (or an element that's not at all related to a table).
if (!synthesizedRole) {
if (QAccessibleInterface *iface = QAccessible::accessibleInterface(axid)) {
if (iface->tableInterface()) {
[self updateTableModel];
} else if (const auto *cell = iface->tableCellInterface()) {
// if we create an element for a table cell, initialize it with row/column
m_rowIndex = cell->rowIndex();
m_columnIndex = cell->columnIndex();
}
}
}
} }
return self; return self;
@ -183,6 +198,10 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
QMacAccessibilityElement *element = QMacAccessibilityElement *element =
[[QMacAccessibilityElement alloc] initWithId:axid role:role]; [[QMacAccessibilityElement alloc] initWithId:axid role:role];
if (element) { if (element) {
if (role == NSAccessibilityRowRole)
element->m_rowIndex = n;
else if (role == NSAccessibilityColumnRole)
element->m_columnIndex = n;
[array addObject:element]; [array addObject:element];
[element release]; [element release];
} else { } else {
@ -320,25 +339,26 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
} else if (synthesizedRole == NSAccessibilityRowRole) { } else if (synthesizedRole == NSAccessibilityRowRole) {
// axid matches the parent table axid so that we can easily find the parent table // axid matches the parent table axid so that we can easily find the parent table
// children of row are cell/any items // children of row are cell/any items
QMacAccessibilityElement *tableElement = [QMacAccessibilityElement elementWithId:axid]; Q_ASSERT(m_rowIndex >= 0);
Q_ASSERT(tableElement->rows); if (!columns) {
NSUInteger rowIndex = [tableElement->rows indexOfObjectIdenticalTo:self]; const int numColumns = table->columnCount();
Q_ASSERT(rowIndex != NSNotFound); columns = [NSMutableArray<QMacAccessibilityElement *> arrayWithCapacity:numColumns];
int numColumns = table->columnCount(); for (int i = 0; i < numColumns; ++i) {
NSMutableArray<QMacAccessibilityElement *> *cells = QAccessibleInterface *cell = table->cellAt(m_rowIndex, i);
[NSMutableArray<QMacAccessibilityElement *> arrayWithCapacity:numColumns]; if (cell && cell->isValid()) {
for (int i = 0; i < numColumns; ++i) { QAccessible::Id cellId = QAccessible::uniqueId(cell);
QAccessibleInterface *cell = table->cellAt(rowIndex, i); QMacAccessibilityElement *element =
if (cell && cell->isValid()) { [QMacAccessibilityElement elementWithId:cellId];
QAccessible::Id cellId = QAccessible::uniqueId(cell); if (element) {
QMacAccessibilityElement *element = element->m_rowIndex = m_rowIndex;
[QMacAccessibilityElement elementWithId:cellId]; element->m_columnIndex = i;
if (element) { [columns insertObject:element atIndex:i];
[cells addObject:element]; }
} }
} }
[columns retain];
} }
return NSAccessibilityUnignoredChildren(cells); return NSAccessibilityUnignoredChildren(columns);
} }
} }
return QCocoaAccessible::unignoredChildren(iface); return QCocoaAccessible::unignoredChildren(iface);
@ -388,18 +408,19 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
if (QAccessibleInterface *parent = iface->parent()) { if (QAccessibleInterface *parent = iface->parent()) {
if (parent->tableInterface()) { if (parent->tableInterface()) {
if (QAccessibleTableCellInterface *cell = iface->tableCellInterface()) { QAccessible::Id parentId = QAccessible::uniqueId(parent);
// parent of cell should be row QMacAccessibilityElement *tableElement =
QAccessible::Id parentId = QAccessible::uniqueId(parent); [QMacAccessibilityElement elementWithId:parentId];
QMacAccessibilityElement *tableElement =
[QMacAccessibilityElement elementWithId:parentId];
const int rowIndex = cell->rowIndex(); // parent of cell should be row
if (tableElement->rows && int([tableElement->rows count]) > rowIndex) { int rowIndex = -1;
QMacAccessibilityElement *rowElement = tableElement->rows[rowIndex]; if (m_rowIndex >= 0 && m_columnIndex >= 0)
return NSAccessibilityUnignoredAncestor(rowElement); rowIndex = m_rowIndex;
} else if (QAccessibleTableCellInterface *cell = iface->tableCellInterface())
} rowIndex = cell->rowIndex();
Q_ASSERT(tableElement->rows && int([tableElement->rows count]) > rowIndex);
QMacAccessibilityElement *rowElement = tableElement->rows[rowIndex];
return NSAccessibilityUnignoredAncestor(rowElement);
} }
if (parent->role() != QAccessible::Application) { if (parent->role() != QAccessible::Application) {
QAccessible::Id parentId = QAccessible::uniqueId(parent); QAccessible::Id parentId = QAccessible::uniqueId(parent);
@ -436,10 +457,7 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
int &row = isRow ? cellPos.ry() : cellPos.rx(); int &row = isRow ? cellPos.ry() : cellPos.rx();
int &col = isRow ? cellPos.rx() : cellPos.ry(); int &col = isRow ? cellPos.rx() : cellPos.ry();
QMacAccessibilityElement *tableElement = NSUInteger trackIndex = self.accessibilityIndex;
[QMacAccessibilityElement elementWithId:axid];
NSArray *tracks = isRow ? tableElement->rows : tableElement->columns;
NSUInteger trackIndex = [tracks indexOfObjectIdenticalTo:self];
if (trackIndex != NSNotFound) { if (trackIndex != NSNotFound) {
row = int(trackIndex); row = int(trackIndex);
if (QAccessibleInterface *firstCell = table->cellAt(cellPos.y(), cellPos.x())) { if (QAccessibleInterface *firstCell = table->cellAt(cellPos.y(), cellPos.x())) {
@ -808,13 +826,10 @@ static void convertLineOffset(QAccessibleTextInterface *text, int *line, int *of
// axid matches the parent table axid so that we can easily find the parent table // axid matches the parent table axid so that we can easily find the parent table
// children of row are cell/any items // children of row are cell/any items
if (QAccessibleTableInterface *table = iface->tableInterface()) { if (QAccessibleTableInterface *table = iface->tableInterface()) {
QMacAccessibilityElement *tableElement = [QMacAccessibilityElement elementWithId:axid]; if (m_rowIndex >= 0)
NSArray *track = synthesizedRole == NSAccessibilityRowRole index = NSInteger(m_rowIndex);
? tableElement->rows : tableElement->columns; else if (m_columnIndex >= 0)
if (track) { index = NSInteger(m_columnIndex);
NSUInteger trackIndex = [track indexOfObjectIdenticalTo:self];
index = (NSInteger)trackIndex;
}
} }
} }
} }