5#include <AppKit/AppKit.h>
13#include <QtCore/qlogging.h>
14#include <QtGui/private/qaccessiblecache_p.h>
15#include <QtGui/private/qaccessiblebridgeutils_p.h>
16#include <QtGui/qaccessible.h>
20Q_STATIC_LOGGING_CATEGORY(lcAccessibilityTable,
"qt.accessibility.table")
22using namespace Qt::StringLiterals;
24#if QT_CONFIG(accessibility)
27
28
29
30
31
32
33
34
35
36
37
38
39static void convertLineOffset(QAccessibleTextInterface *text,
int *line,
int *offset, NSUInteger *start = 0, NSUInteger *end = 0)
41 Q_ASSERT(*line == -1 || *offset == -1);
42 Q_ASSERT(*line != -1 || *offset != -1);
43 Q_ASSERT(*offset <= text->characterCount());
46 int curStart = 0, curEnd = 0;
50 text->textAtOffset(curStart, QAccessible::LineBoundary, &curStart, &curEnd);
52 if (curStart == -1 || curEnd == -1) {
66 text->textAtOffset(curEnd, QAccessible::LineBoundary, &nextStart, &nextEnd);
67 if (nextEnd == curEnd)
70 }
while ((*line == -1 || curLine < *line) && (*offset == -1 || (curEnd <= *offset)) && curEnd <= text->characterCount());
72 curEnd = qMin(curEnd, text->characterCount());
79 Q_ASSERT(curStart >= 0);
80 Q_ASSERT(curEnd >= 0);
87@implementation QMacAccessibilityElement {
93 NSMutableArray<QMacAccessibilityElement *> *rows;
94 NSMutableArray<QMacAccessibilityElement *> *columns;
102 NSString *synthesizedRole;
105- (instancetype)initWithId:(QAccessible::Id)anId
107 return [self initWithId:anId role:nil];
110- (instancetype)initWithId:(QAccessible::Id)anId role:(NSAccessibilityRole)role
112 Q_ASSERT((
int)anId < 0);
120 synthesizedRole = role;
124 if (!synthesizedRole) {
125 if (QAccessibleInterface *iface = QAccessible::accessibleInterface(axid)) {
126 if (iface->tableInterface()) {
127 [self updateTableModel];
128 }
else if (
const auto *cell = iface->tableCellInterface()) {
131 m_rowIndex = cell->rowIndex();
132 m_columnIndex = cell->columnIndex();
133 QAccessibleInterface *table = cell->table();
135 QAccessibleTableInterface *tableInterface = table->tableInterface();
136 if (tableInterface) {
137 auto *tableElement = [QMacAccessibilityElement elementWithInterface:table];
138 Q_ASSERT(tableElement);
139 if (!tableElement->rows
140 ||
int(tableElement->rows.count) <= m_rowIndex
141 ||
int(tableElement->rows.count) != tableInterface->rowCount()) {
142 qCWarning(lcAccessibilityTable)
143 <<
"Cell requested for row" << m_rowIndex <<
"is out of"
144 <<
"bounds for table with" << (tableElement->rows ?
145 tableElement->rows.count : tableInterface->rowCount())
146 <<
"rows! Resizing table model.";
147 [tableElement updateTableModel];
150 Q_ASSERT(tableElement->rows);
151 Q_ASSERT(
int(tableElement->rows.count) > m_rowIndex);
153 auto *rowElement = tableElement->rows[m_rowIndex];
154 if (!rowElement->columns ||
int(rowElement->columns.count) != tableInterface->columnCount()) {
155 if (rowElement->columns) {
156 qCWarning(lcAccessibilityTable)
157 <<
"Table representation column count is out of sync:"
158 << rowElement->columns.count <<
"!=" << tableInterface->columnCount();
159 [rowElement->columns autorelease];
160 rowElement->columns = nil;
162 rowElement->columns = [rowElement populateTableRow:tableInterface->columnCount()];
163 [rowElement->columns retain];
166 qCDebug(lcAccessibilityTable) <<
"Creating cell representation for"
167 << m_rowIndex << m_columnIndex
169 << tableElement->rows.count <<
"rows and"
170 << rowElement->columns.count <<
"columns";
172 rowElement->columns[m_columnIndex] = self;
183
184
185
186
187
188
189
190
191+ (instancetype)elementWithId:(QAccessible::Id)anId
197 QAccessibleCache *cache = QAccessibleCache::instance();
199 QMacAccessibilityElement *element = cache->elementForId(anId);
201 Q_ASSERT(QAccessible::accessibleInterface(anId));
202 element = [[self alloc] initWithId:anId];
203 if (cache->insertElement(anId, element))
209+ (instancetype)elementWithInterface:(QAccessibleInterface *)iface
215 const QAccessible::Id anId = QAccessible::uniqueId(iface);
216 return [self elementWithId:anId];
219+ (
void)removeElementsFromCache:(NSArray *)array {
220 for (uint i = 0; i < array.count; ++i) {
221 QMacAccessibilityElement *cell = [array objectAtIndex:i];
223 QAccessibleCache::instance()->deleteInterface(cell->axid);
232 [QMacAccessibilityElement removeElementsFromCache:rows];
237 [QMacAccessibilityElement removeElementsFromCache:columns];
238 [columns autorelease];
241 synthesizedRole = nil;
243 NSAccessibilityPostNotification(self, NSAccessibilityUIElementDestroyedNotification);
247
248
249
250
251
252
253
254
255
256
259 [QMacAccessibilityElement removeElementsFromCache:rows];
263 [QMacAccessibilityElement removeElementsFromCache:columns];
266 QAccessibleCache::instance()->deleteInterface(axid);
270- (BOOL)isEqual:(id)object {
271 if ([object isKindOfClass:[QMacAccessibilityElement
class]]) {
272 QMacAccessibilityElement *other = object;
273 return other->axid == axid && other->synthesizedRole == synthesizedRole;
283- (BOOL)isManagedByParent {
284 return synthesizedRole != nil;
287- (NSMutableArray *)populateTableArray:(NSAccessibilityRole)role count:(
int)count
289 if (self.qtInterface) {
290 auto *array = [NSMutableArray<QMacAccessibilityElement *> arrayWithCapacity:count];
292 for (
int n = 0; n < count; ++n) {
294 QMacAccessibilityElement *element =
295 [[QMacAccessibilityElement alloc] initWithId:axid role:role];
297 if (role == NSAccessibilityRowRole)
298 element->m_rowIndex = n;
299 else if (role == NSAccessibilityColumnRole)
300 element->m_columnIndex = n;
301 [array addObject:element];
304 qWarning(
"QCocoaAccessibility: invalid child");
312- (NSMutableArray *)populateTableRow:(
int)count
314 Q_ASSERT(synthesizedRole == NSAccessibilityRowRole);
315 qCDebug(lcAccessibilityTable) <<
"Populating table row" << m_rowIndex
316 <<
"with" << count <<
"placeholder cells";
325 auto *array = [NSMutableArray<QMacAccessibilityElement *> arrayWithCapacity:count];
328 for (
int n = 0; n < count; ++n) {
330 QMacAccessibilityElement *cell =
331 [[QMacAccessibilityElement alloc] initWithId:axid role:NSAccessibilityCellRole];
333 cell->m_rowIndex = m_rowIndex;
334 cell->m_columnIndex = n;
335 [array addObject:cell];
342- (
void)updateTableModel
344 if (QAccessibleInterface *iface = self.qtInterface) {
345 if (QAccessibleTableInterface *table = iface->tableInterface()) {
346 Q_ASSERT(!self.isManagedByParent);
347 qCDebug(lcAccessibilityTable) <<
"Updating table representation with"
348 << table->rowCount() << table->columnCount();
353 rows = [self populateTableArray:NSAccessibilityRowRole count:table->rowCount()];
356 [columns autorelease];
359 columns = [self populateTableArray:NSAccessibilityColumnRole count:table->columnCount()];
365- (QAccessibleInterface *)qtInterface
367 QAccessibleInterface *iface = QAccessible::accessibleInterface(axid);
368 if (!iface || !iface->isValid())
374 if (synthesizedRole == NSAccessibilityCellRole) {
376 QAccessibleTableInterface *table = iface->tableInterface();
378 QAccessibleInterface *cell = table->cellAt(m_rowIndex, m_columnIndex);
381 Q_ASSERT(cell->isValid());
385 axid = QAccessible::uniqueId(cell);
386 synthesizedRole = nil;
388 QAccessibleCache *cache = QAccessibleCache::instance();
389 if (QMacAccessibilityElement *cellElement = cache->elementForId(axid)) {
391 Q_ASSERT(cellElement->synthesizedRole == nil);
393 if (cellElement != self) {
395 Q_ASSERT(cellElement->m_rowIndex == m_rowIndex && cellElement->m_columnIndex == m_columnIndex);
399 cache->insertElement(axid, self);
408- (BOOL)isAccessibilityFocused
411 id focusedElement = NSApp.accessibilityApplicationFocusedUIElement;
412 return [focusedElement isEqual:self];
417+ (id) lineNumberForIndex: (
int)index forText:(
const QString &)text
419 auto textBefore = QStringView(text).left(index);
420 qsizetype newlines = textBefore.count(u'\n');
424- (BOOL) accessibilityNotifiesWhenDestroyed {
428- (NSString *) accessibilityRole {
431 return synthesizedRole;
432 if (QAccessibleInterface *iface = self.qtInterface)
433 return QCocoaAccessible::macRole(iface);
434 return NSAccessibilityUnknownRole;
437- (NSString *) accessibilitySubRole {
438 if (QAccessibleInterface *iface = self.qtInterface)
439 return QCocoaAccessible::macSubrole(iface);
440 return NSAccessibilityUnknownRole;
443- (NSString *) accessibilityRoleDescription {
444 if (self.qtInterface)
445 return NSAccessibilityRoleDescription(self.accessibilityRole, self.accessibilitySubRole);
446 return NSAccessibilityUnknownRole;
449- (NSArray *) accessibilityChildren {
451 if (synthesizedRole == NSAccessibilityCellRole)
454 QAccessibleInterface *iface = self.qtInterface;
457 if (QAccessibleTableInterface *table = iface->tableInterface()) {
459 if (!synthesizedRole) {
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492 NSArray *rs = [self accessibilityRows];
493 NSArray *cs = [self accessibilityColumns];
494 const int rCount =
int([rs count]);
495 const int cCount =
int([cs count]);
496 int childCount = rCount + cCount;
497 NSMutableArray<QMacAccessibilityElement *> *tableChildren =
498 [NSMutableArray<QMacAccessibilityElement *> arrayWithCapacity:childCount];
499 for (
int i = 0; i < rCount; ++i) {
500 [tableChildren addObject:[rs objectAtIndex:i]];
502 for (
int i = 0; i < cCount; ++i) {
503 [tableChildren addObject:[cs objectAtIndex:i]];
505 return NSAccessibilityUnignoredChildren(tableChildren);
506 }
else if (synthesizedRole == NSAccessibilityColumnRole) {
508 }
else if (synthesizedRole == NSAccessibilityRowRole) {
511 Q_ASSERT(m_rowIndex >= 0);
512 Q_ASSERT(rows == nil);
513 const unsigned int numColumns = table->columnCount();
514 if (!columns || columns.count != numColumns) {
516 [columns autorelease];
519 columns = [self populateTableRow:numColumns];
522 return NSAccessibilityUnignoredChildren(columns);
525 return QCocoaAccessible::unignoredChildren(iface);
528- (NSArray *) accessibilitySelectedChildren {
529 QAccessibleInterface *iface = QAccessible::accessibleInterface(axid);
530 if (!iface || !iface->isValid())
533 QAccessibleSelectionInterface *selection = iface->selectionInterface();
537 const QList<QAccessibleInterface *> selectedList = selection->selectedItems();
538 const qsizetype numSelected = selectedList.size();
539 NSMutableArray<QMacAccessibilityElement *> *selectedChildren =
540 [NSMutableArray<QMacAccessibilityElement *> arrayWithCapacity:numSelected];
541 for (QAccessibleInterface *selectedChild : selectedList) {
542 if (selectedChild && selectedChild->isValid()) {
543 QAccessible::Id id = QAccessible::uniqueId(selectedChild);
544 QMacAccessibilityElement *element = [QMacAccessibilityElement elementWithId:id];
546 [selectedChildren addObject:element];
549 return NSAccessibilityUnignoredChildren(selectedChildren);
552- (id) accessibilityWindow {
554 NSAccessibilityElement *parent = self.accessibilityParent;
555 if (parent && parent.accessibilityRole == NSAccessibilityWindowRole)
557 return [parent accessibilityWindow];
560- (id) accessibilityTopLevelUIElementAttribute {
562 return [self.accessibilityParent accessibilityTopLevelUIElementAttribute];
565- (NSString *) accessibilityTitle {
566 if (QAccessibleInterface *iface = self.qtInterface) {
567 if (iface->role() == QAccessible::StaticText)
569 if (self.isManagedByParent)
571 return iface->text(QAccessible::Name).toNSString();
576- (id) accessibilityTitleUIElement {
577 QAccessibleInterface *iface = self.qtInterface;
581 const auto labelRelations = iface->relations(QAccessible::Label);
582 if (labelRelations.empty())
585 QAccessibleInterface *label = labelRelations.first().first;
589 QMacAccessibilityElement *accessibleElement = [QMacAccessibilityElement elementWithInterface:label];
590 if (!accessibleElement)
593 return NSAccessibilityUnignoredAncestor(accessibleElement);
596- (NSString*) accessibilityIdentifier {
597 if (QAccessibleInterface *iface = self.qtInterface)
598 return QAccessibleBridgeUtils::accessibleId(iface).toNSString();
602- (BOOL) isAccessibilityEnabled {
603 if (QAccessibleInterface *iface = self.qtInterface)
604 return !iface->state().disabled;
608- (id)accessibilityParent {
609 if (synthesizedRole == NSAccessibilityCellRole) {
611 QMacAccessibilityElement *tableElement =
612 [QMacAccessibilityElement elementWithId:axid];
613 Q_ASSERT(tableElement && tableElement->rows);
614 Q_ASSERT(
int(tableElement->rows.count) > m_rowIndex);
615 QMacAccessibilityElement *rowElement = tableElement->rows[m_rowIndex];
619 QAccessibleInterface *iface = self.qtInterface;
623 if (self.isManagedByParent) {
625 return NSAccessibilityUnignoredAncestor([QMacAccessibilityElement elementWithId:axid]);
628 if (QAccessibleInterface *parent = iface->parent()) {
629 if (parent->tableInterface()) {
630 QMacAccessibilityElement *tableElement =
631 [QMacAccessibilityElement elementWithInterface:parent];
635 if (m_rowIndex >= 0 && m_columnIndex >= 0)
636 rowIndex = m_rowIndex;
637 else if (QAccessibleTableCellInterface *cell = iface->tableCellInterface())
638 rowIndex = cell->rowIndex();
639 Q_ASSERT(tableElement->rows);
640 if (rowIndex >
int([tableElement->rows count]) || rowIndex == -1)
642 QMacAccessibilityElement *rowElement = tableElement->rows[rowIndex];
643 return NSAccessibilityUnignoredAncestor(rowElement);
648 if (!QCocoaAccessible::isRepresentedByAppKit(parent))
649 return NSAccessibilityUnignoredAncestor([QMacAccessibilityElement elementWithInterface: parent]);
652 if (QWindow *window = iface->window()) {
653 QPlatformWindow *platformWindow = window->handle();
654 if (platformWindow) {
655 QCocoaWindow *win =
static_cast<QCocoaWindow*>(platformWindow);
656 return NSAccessibilityUnignoredAncestor(qnsview_cast(win->view()));
662- (NSRect)accessibilityFrame {
663 QAccessibleInterface *iface = self.qtInterface;
668 if (self.isManagedByParent) {
669 if (QAccessibleTableInterface *table = iface->tableInterface()) {
676 const bool isRow = synthesizedRole == NSAccessibilityRowRole;
678 int &row = isRow ? cellPos.ry() : cellPos.rx();
679 int &col = isRow ? cellPos.rx() : cellPos.ry();
681 NSUInteger trackIndex = self.accessibilityIndex;
682 if (trackIndex != NSNotFound) {
683 row =
int(trackIndex);
684 if (QAccessibleInterface *firstCell = table->cellAt(cellPos.y(), cellPos.x())) {
685 rect = firstCell->rect();
686 col = isRow ? table->columnCount() : table->rowCount();
689 if (QAccessibleInterface *lastCell =
690 table->cellAt(cellPos.y(), cellPos.x()))
691 rect = rect.united(lastCell->rect());
697 rect = iface->rect();
700 return QCocoaScreen::mapToNative(rect);
703- (NSString*)accessibilityLabel {
704 if (QAccessibleInterface *iface = self.qtInterface)
705 return iface->text(QAccessible::Description).toNSString();
706 qWarning() <<
"Called accessibilityLabel on invalid object: " << axid;
710- (
void)setAccessibilityLabel:(NSString*)label{
711 if (QAccessibleInterface *iface = self.qtInterface)
712 iface->setText(QAccessible::Description, QString::fromNSString(label));
715- (NSAccessibilityOrientation)accessibilityOrientation {
716 QAccessibleInterface *iface = self.qtInterface;
718 return NSAccessibilityOrientationUnknown;
720 NSAccessibilityOrientation nsOrientation = NSAccessibilityOrientationUnknown;
721 if (QAccessibleAttributesInterface *attributesIface = iface->attributesInterface()) {
722 const QVariant orientationVariant =
723 attributesIface->attributeValue(QAccessible::Attribute::Orientation);
724 if (orientationVariant.isValid()) {
725 Q_ASSERT(orientationVariant.canConvert<Qt::Orientation>());
726 const Qt::Orientation orientation = orientationVariant.value<Qt::Orientation>();
727 nsOrientation = orientation == Qt::Horizontal ? NSAccessibilityOrientationHorizontal
728 : NSAccessibilityOrientationVertical;
731 return nsOrientation;
734- (id) accessibilityValue {
735 if (QAccessibleInterface *iface = self.qtInterface) {
738 if (QCocoaAccessible::hasValueAttribute(iface))
739 return QCocoaAccessible::getValueAttribute(iface);
745- (id) accessibilityMinValue {
746 if (QAccessibleInterface *iface = self.qtInterface) {
747 if (iface->valueInterface()) {
748 return iface->valueInterface()->minimumValue().toString().toNSString();
755- (id) accessibilityMaxValue {
756 if (QAccessibleInterface *iface = self.qtInterface) {
757 if (iface->valueInterface()) {
758 return iface->valueInterface()->maximumValue().toString().toNSString();
764- (NSInteger) accessibilityNumberOfCharacters {
765 if (QAccessibleInterface *iface = self.qtInterface) {
766 if (QAccessibleTextInterface *text = iface->textInterface())
767 return text->characterCount();
772- (NSString *) accessibilitySelectedText {
773 if (QAccessibleInterface *iface = self.qtInterface) {
774 if (QAccessibleTextInterface *text = iface->textInterface()) {
777 text->selection(0, &start, &end);
778 return text->text(start, end).toNSString();
784- (NSRange) accessibilitySelectedTextRange {
785 QAccessibleInterface *iface = self.qtInterface;
788 if (QAccessibleTextInterface *text = iface->textInterface()) {
791 if (text->selectionCount() > 0) {
792 text->selection(0, &start, &end);
794 start = text->cursorPosition();
797 return NSMakeRange(quint32(start), quint32(end - start));
799 return NSMakeRange(0, 0);
802- (NSInteger)accessibilityLineForIndex:(NSInteger)index {
803 QAccessibleInterface *iface = self.qtInterface;
806 if (QAccessibleTextInterface *text = iface->textInterface()) {
807 QString textToPos = text->text(0, index);
808 return textToPos.count(
'\n');
813- (NSRange)accessibilityVisibleCharacterRange {
814 QAccessibleInterface *iface = self.qtInterface;
818 if (QAccessibleTextInterface *text = iface->textInterface())
819 return NSMakeRange(0,
static_cast<uint>(text->characterCount()));
820 return NSMakeRange(0,
static_cast<uint>(iface->text(QAccessible::Name).length()));
823- (NSInteger) accessibilityInsertionPointLineNumber {
824 QAccessibleInterface *iface = self.qtInterface;
827 if (QAccessibleTextInterface *text = iface->textInterface()) {
828 int position = text->cursorPosition();
829 return [self accessibilityLineForIndex:position];
834- (NSArray *)accessibilityAttributeNames {
835 NSMutableArray *attributes = [[NSMutableArray
new] autorelease];
836 QAccessibleInterface *iface = self.qtInterface;
838#if QT_APPLE_SDK_EQUAL_OR_ABOVE(MACOS(26
))
839 if (@available(macOS 26, *)) {
840 [attributes addObject:NSAccessibilityLanguageAttribute];
842 if (iface && iface->role() == QAccessible::Heading) {
843 QAccessibleAttributesInterface *attributesIface = iface->attributesInterface();
845 && attributesIface->attributeKeys().contains(QAccessible::Attribute::Level))
846 [attributes addObject:NSAccessibilityHeadingLevelAttribute];
851 if (iface && iface->state().expandable)
852 [attributes addObject:NSAccessibilityExpandedAttribute];
857- (id)accessibilityAttributeValue:(NSString *)attribute {
858 QAccessibleInterface *iface = self.qtInterface;
860 qWarning() <<
"Called attribute on invalid object: " << axid;
864#if QT_APPLE_SDK_EQUAL_OR_ABOVE(MACOS(26
))
865 if (@available(macOS 26, *)) {
866 if ([attribute isEqualToString:NSAccessibilityLanguageAttribute]) {
867 QAccessibleAttributesInterface *attributesIface = iface->attributesInterface();
869 || !attributesIface->attributeKeys().contains(QAccessible::Attribute::Locale))
872 const auto &localeVariant =
873 attributesIface->attributeValue(QAccessible::Attribute::Locale);
874 return localeVariant.toLocale().bcp47Name().toNSString();
877 if ([attribute isEqualToString:NSAccessibilityHeadingLevelAttribute]) {
878 if (iface->role() != QAccessible::Heading)
881 auto attributesIface = iface->attributesInterface();
882 if (!attributesIface)
885 const QVariant levelVariant =
886 attributesIface->attributeValue(QAccessible::Attribute::Level);
887 if (!levelVariant.isValid() || !levelVariant.canConvert<
int>())
890 const int level = levelVariant.toInt();
899 if ([attribute isEqualToString:NSAccessibilityExpandedAttribute])
900 return iface->state().expanded ? @YES : @NO;
905- (NSArray *)accessibilityParameterizedAttributeNames {
907 QAccessibleInterface *iface = self.qtInterface;
909 qWarning() <<
"Called attribute on invalid object: " << axid;
913 if (iface->textInterface()) {
915 NSAccessibilityStringForRangeParameterizedAttribute,
916 NSAccessibilityLineForIndexParameterizedAttribute,
917 NSAccessibilityRangeForLineParameterizedAttribute,
918 NSAccessibilityRangeForPositionParameterizedAttribute,
920 NSAccessibilityBoundsForRangeParameterizedAttribute,
922 NSAccessibilityStyleRangeForIndexParameterizedAttribute,
923 NSAccessibilityAttributedStringForRangeParameterizedAttribute
930- (id)accessibilityAttributeValue:(NSString *)attribute forParameter:(id)parameter {
931 QAccessibleInterface *iface = self.qtInterface;
933 qWarning() <<
"Called attribute on invalid object: " << axid;
937 if (!iface->textInterface())
940 if ([attribute isEqualToString: NSAccessibilityStringForRangeParameterizedAttribute]) {
941 NSRange range = [parameter rangeValue];
942 QString text = iface->textInterface()->text(range.location, range.location + range.length);
943 return text.toNSString();
945 if ([attribute isEqualToString: NSAccessibilityLineForIndexParameterizedAttribute]) {
946 int index = [parameter intValue];
947 if (index < 0 || index > iface->textInterface()->characterCount())
950 if (iface->state().multiLine) {
952 convertLineOffset(iface->textInterface(), &line, &index);
956 if ([attribute isEqualToString: NSAccessibilityRangeForLineParameterizedAttribute]) {
957 int line = [parameter intValue];
961 NSUInteger startOffset = 0;
962 NSUInteger endOffset = 0;
963 convertLineOffset(iface->textInterface(), &line, &lineOffset, &startOffset, &endOffset);
964 return [NSValue valueWithRange:NSMakeRange(startOffset, endOffset - startOffset)];
966 if ([attribute isEqualToString: NSAccessibilityBoundsForRangeParameterizedAttribute]) {
967 NSRange range = [parameter rangeValue];
968 QRect firstRect = iface->textInterface()->characterRect(range.location);
970 if (range.length > 0) {
971 NSUInteger position = range.location + range.length - 1;
972 if (position > range.location && iface->textInterface()->text(position, position + 1) ==
"\n"_L1)
974 QRect lastRect = iface->textInterface()->characterRect(position);
975 rect = firstRect.united(lastRect);
980 return [NSValue valueWithRect:QCocoaScreen::mapToNative(rect)];
982 if ([attribute isEqualToString: NSAccessibilityAttributedStringForRangeParameterizedAttribute]) {
983 NSRange range = [parameter rangeValue];
984 QString text = iface->textInterface()->text(range.location, range.location + range.length);
985 return [[NSAttributedString alloc] initWithString:text.toNSString()];
986 }
else if ([attribute isEqualToString: NSAccessibilityRangeForPositionParameterizedAttribute]) {
987 QPoint point = QCocoaScreen::mapFromNative([parameter pointValue]).toPoint();
988 int offset = iface->textInterface()->offsetAtPoint(point);
989 return [NSValue valueWithRange:NSMakeRange(
static_cast<NSUInteger>(offset), 1)];
990 }
else if ([attribute isEqualToString: NSAccessibilityStyleRangeForIndexParameterizedAttribute]) {
993 iface->textInterface()->attributes([parameter intValue], &start, &end);
994 return [NSValue valueWithRange:NSMakeRange(
static_cast<NSUInteger>(start),
static_cast<NSUInteger>(end - start))];
999- (BOOL)accessibilityIsAttributeSettable:(NSString *)attribute {
1000 QAccessibleInterface *iface = self.qtInterface;
1004 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1005 return iface->state().focusable ? YES : NO;
1006 }
else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1007 if (iface->textInterface() && iface->state().editable)
1009 if (iface->valueInterface())
1012 }
else if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1013 return iface->textInterface() ? YES : NO;
1018- (
void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute {
1019 QAccessibleInterface *iface = self.qtInterface;
1022 if ([attribute isEqualToString:NSAccessibilityFocusedAttribute]) {
1023 if (QAccessibleActionInterface *action = iface->actionInterface())
1024 action->doAction(QAccessibleActionInterface::setFocusAction());
1025 }
else if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
1026 if (iface->textInterface()) {
1027 QString text = QString::fromNSString((NSString *)value);
1028 iface->setText(QAccessible::Value, text);
1029 }
else if (QAccessibleValueInterface *valueIface = iface->valueInterface()) {
1030 double val = [value doubleValue];
1031 valueIface->setCurrentValue(val);
1033 }
else if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
1034 if (QAccessibleTextInterface *text = iface->textInterface()) {
1035 NSRange range = [value rangeValue];
1036 if (range.length > 0)
1037 text->setSelection(0, range.location, range.location + range.length);
1039 text->setCursorPosition(range.location);
1046- (NSArray *)accessibilityActionNames {
1047 NSMutableArray *nsActions = [[NSMutableArray
new] autorelease];
1048 QAccessibleInterface *iface = self.qtInterface;
1052 const QStringList &supportedActionNames = QAccessibleBridgeUtils::effectiveActionNames(iface);
1053 for (
const QString &qtAction : supportedActionNames) {
1054 NSString *nsAction = QCocoaAccessible::getTranslatedAction(qtAction);
1056 [nsActions addObject : nsAction];
1062- (NSString *)accessibilityActionDescription:(NSString *)action {
1063 QAccessibleInterface *iface = self.qtInterface;
1066 QString qtAction = QCocoaAccessible::translateAction(action, iface);
1067 QString description;
1069 if (qtAction.isEmpty()) {
1070 if (QAccessibleActionInterface *actionInterface = iface->actionInterface()) {
1071 qtAction = QString::fromNSString((NSString *)action);
1072 description = actionInterface->localizedActionDescription(qtAction);
1075 description = qAccessibleLocalizedActionDescription(qtAction);
1077 return description.toNSString();
1080- (
void)accessibilityPerformAction:(NSString *)action {
1081 if (QAccessibleInterface *iface = self.qtInterface) {
1082 const QString qtAction = QCocoaAccessible::translateAction(action, iface);
1083 QAccessibleBridgeUtils::performEffectiveAction(iface, qtAction);
1089- (BOOL)accessibilityIsIgnored {
1093 if (self.isManagedByParent)
1096 if (QAccessibleInterface *iface = self.qtInterface)
1097 return QCocoaAccessible::shouldBeIgnored(iface);
1101- (id)accessibilityHitTest:(NSPoint)point
1103 QAccessibleInterface *iface = self.qtInterface;
1105 return NSAccessibilityUnignoredAncestor(self);
1107 QPointF screenPoint = QCocoaScreen::mapFromNative(point);
1108 QWindow *window = QAccessibleBridgeUtils::windowFor(iface);
1112 QAccessibleInterface *childInterface = iface;
1113 while (QAccessibleInterface *child = childInterface->childAt(screenPoint.x(), screenPoint.y())) {
1114 if (!child->isValid())
1117 QWindow *childWindow = QAccessibleBridgeUtils::windowFor(child, childInterface, window);
1118 if (childWindow != window) {
1119 if (
auto *view = QCocoaAccessible::accessibleViewFor(childWindow))
1120 return [view accessibilityHitTest:point];
1123 childInterface = child;
1127 if (childInterface == iface)
1128 return NSAccessibilityUnignoredAncestor(self);
1131 QMacAccessibilityElement *accessibleElement = [QMacAccessibilityElement elementWithInterface:childInterface];
1132 if (accessibleElement)
1133 return NSAccessibilityUnignoredAncestor(accessibleElement);
1134 return NSAccessibilityUnignoredAncestor(self);
1137- (id)accessibilityFocusedUIElement {
1138 QAccessibleInterface *iface = self.qtInterface;
1140 qWarning(
"FocusedUIElement for INVALID");
1144 QAccessibleInterface *focusInterface = iface->focusChild();
1145 if (focusInterface && focusInterface->isValid()) {
1152 QWindow *window = QAccessibleBridgeUtils::windowFor(iface);
1153 QWindow *focusWindow = QAccessibleBridgeUtils::windowFor(focusInterface, iface, window);
1154 if (focusWindow != window) {
1155 if (
auto *view = QCocoaAccessible::accessibleViewFor(focusWindow))
1156 return [view accessibilityFocusedUIElement];
1159 QMacAccessibilityElement *accessibleElement = [QMacAccessibilityElement elementWithInterface:focusInterface];
1160 return NSAccessibilityUnignoredAncestor(accessibleElement);
1163 return NSAccessibilityUnignoredAncestor(self);
1166- (NSString *) accessibilityHelp {
1167 if (QAccessibleInterface *iface = self.qtInterface) {
1168 const QString helpText = iface->text(QAccessible::Help);
1169 if (!helpText.isEmpty())
1170 return helpText.toNSString();
1176
1177
1178- (NSInteger) accessibilityIndex {
1179 NSInteger index = 0;
1180 if (synthesizedRole == NSAccessibilityCellRole)
1181 return m_columnIndex;
1182 if (QAccessibleInterface *iface = self.qtInterface) {
1183 if (self.isManagedByParent) {
1186 if (iface->tableInterface()) {
1187 if (m_rowIndex >= 0)
1188 index = NSInteger(m_rowIndex);
1189 else if (m_columnIndex >= 0)
1190 index = NSInteger(m_columnIndex);
1197- (NSArray *) accessibilityRows {
1198 if (!synthesizedRole && rows) {
1199 QAccessibleInterface *iface = self.qtInterface;
1200 QAccessibleTableInterface *tableInterface = iface ? iface->tableInterface() :
nullptr;
1201 if (tableInterface) {
1202 const unsigned int rowCount = tableInterface->rowCount();
1203 if (rows.count != rowCount) {
1204 qCDebug(lcAccessibilityTable) <<
"Updating table rows with" << rowCount <<
"rows";
1209 rows = [self populateTableArray:NSAccessibilityRowRole
1213 return NSAccessibilityUnignoredChildren(rows);
1219- (NSArray *) accessibilityColumns {
1221 if (!synthesizedRole && columns) {
1222 QAccessibleInterface *iface = self.qtInterface;
1223 if (iface && iface->tableInterface())
1224 return NSAccessibilityUnignoredChildren(columns);
1231- (NSArray *) accessibilityTabs {
1232 QAccessibleInterface *iface = self.qtInterface;
1233 if (iface && iface->role() == QAccessible::PageTabList) {
1234 return QCocoaAccessible::unignoredChildren(iface, [](QAccessibleInterface *child){
1235 return QCocoaAccessible::defaultUnignored(child)
1236 && child->role() == QAccessible::PageTab;