The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1079 lines
49KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 6 End-User License
  8. Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
  9. End User License Agreement: www.juce.com/juce-6-licence
  10. Privacy Policy: www.juce.com/juce-privacy-policy
  11. Or: You may also use this code under the terms of the GPL v3 (see
  12. www.gnu.org/licenses).
  13. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  14. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  15. DISCLAIMED.
  16. ==============================================================================
  17. */
  18. namespace juce
  19. {
  20. #if (! defined MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
  21. using NSAccessibilityRole = NSString*;
  22. using NSAccessibilityNotificationName = NSString*;
  23. #endif
  24. //==============================================================================
  25. class AccessibilityHandler::AccessibilityNativeImpl
  26. {
  27. public:
  28. explicit AccessibilityNativeImpl (AccessibilityHandler& handler)
  29. : accessibilityElement (AccessibilityElement::create (handler))
  30. {}
  31. NSAccessibilityElement<NSAccessibility>* getAccessibilityElement() const noexcept
  32. {
  33. return accessibilityElement.get();
  34. }
  35. private:
  36. //==============================================================================
  37. class AccessibilityElement : public ObjCClass<NSAccessibilityElement<NSAccessibility>>
  38. {
  39. private:
  40. struct Deleter
  41. {
  42. void operator() (NSAccessibilityElement<NSAccessibility>* element) const
  43. {
  44. object_setInstanceVariable (element, "handler", nullptr);
  45. [element release];
  46. }
  47. };
  48. public:
  49. using Holder = std::unique_ptr<NSAccessibilityElement<NSAccessibility>, Deleter>;
  50. static Holder create (AccessibilityHandler& handler)
  51. {
  52. static AccessibilityElement cls;
  53. Holder element ([cls.createInstance() init]);
  54. object_setInstanceVariable (element.get(), "handler", &handler);
  55. return element;
  56. }
  57. private:
  58. AccessibilityElement() : ObjCClass<NSAccessibilityElement<NSAccessibility>> ("JUCEAccessibilityElement_")
  59. {
  60. addIvar<AccessibilityHandler*> ("handler");
  61. addMethod (@selector (accessibilityNotifiesWhenDestroyed), getAccessibilityNotifiesWhenDestroyed, "c@:");
  62. addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:");
  63. addMethod (@selector (isAccessibilityEnabled), getIsAccessibilityEnabled, "c@:");
  64. addMethod (@selector (accessibilityWindow), getAccessibilityWindow, "@@:");
  65. addMethod (@selector (accessibilityTopLevelUIElement), getAccessibilityWindow, "@@:");
  66. addMethod (@selector (accessibilityFocusedUIElement), getAccessibilityFocusedUIElement, "@@:");
  67. addMethod (@selector (accessibilityHitTest:), accessibilityHitTest, "@@:", @encode (NSPoint));
  68. addMethod (@selector (accessibilityParent), getAccessibilityParent, "@@:");
  69. addMethod (@selector (accessibilityChildren), getAccessibilityChildren, "@@:");
  70. addMethod (@selector (isAccessibilityFocused), getIsAccessibilityFocused, "c@:");
  71. addMethod (@selector (setAccessibilityFocused:), setAccessibilityFocused, "v@:c");
  72. addMethod (@selector (isAccessibilityModal), getIsAccessibilityModal, "c@:");
  73. addMethod (@selector (accessibilityFrame), getAccessibilityFrame, @encode (NSRect), "@:");
  74. addMethod (@selector (accessibilityRole), getAccessibilityRole, "@@:");
  75. addMethod (@selector (accessibilitySubrole), getAccessibilitySubrole, "@@:");
  76. addMethod (@selector (accessibilityTitle), getAccessibilityTitle, "@@:");
  77. addMethod (@selector (accessibilityLabel), getAccessibilityLabel, "@@:");
  78. addMethod (@selector (accessibilityHelp), getAccessibilityHelp, "@@:");
  79. addMethod (@selector (accessibilityValue), getAccessibilityValue, "@@:");
  80. addMethod (@selector (setAccessibilityValue:), setAccessibilityValue, "v@:@");
  81. addMethod (@selector (accessibilitySelectedChildren), getAccessibilitySelectedChildren, "@@:");
  82. addMethod (@selector (setAccessibilitySelectedChildren:), setAccessibilitySelectedChildren, "v@:@");
  83. addMethod (@selector (accessibilityOrientation), getAccessibilityOrientation, "i@:@");
  84. addMethod (@selector (accessibilityInsertionPointLineNumber), getAccessibilityInsertionPointLineNumber, "i@:");
  85. addMethod (@selector (accessibilitySharedCharacterRange), getAccessibilitySharedCharacterRange, @encode (NSRange), "@:");
  86. addMethod (@selector (accessibilitySharedTextUIElements), getAccessibilitySharedTextUIElements, "@@:");
  87. addMethod (@selector (accessibilityVisibleCharacterRange), getAccessibilityVisibleCharacterRange, @encode (NSRange), "@:");
  88. addMethod (@selector (accessibilityNumberOfCharacters), getAccessibilityNumberOfCharacters, "i@:");
  89. addMethod (@selector (accessibilitySelectedText), getAccessibilitySelectedText, "@@:");
  90. addMethod (@selector (accessibilitySelectedTextRange), getAccessibilitySelectedTextRange, @encode (NSRange), "@:");
  91. addMethod (@selector (accessibilitySelectedTextRanges), getAccessibilitySelectedTextRanges, "@@:");
  92. addMethod (@selector (accessibilityAttributedStringForRange:), getAccessibilityAttributedStringForRange, "@@:", @encode (NSRange));
  93. addMethod (@selector (accessibilityRangeForLine:), getAccessibilityRangeForLine, @encode (NSRange), "@:i");
  94. addMethod (@selector (accessibilityStringForRange:), getAccessibilityStringForRange, "@@:", @encode (NSRange));
  95. addMethod (@selector (accessibilityRangeForPosition:), getAccessibilityRangeForPosition, @encode (NSRange), "@:", @encode (NSPoint));
  96. addMethod (@selector (accessibilityRangeForIndex:), getAccessibilityRangeForIndex, @encode (NSRange), "@:i");
  97. addMethod (@selector (accessibilityFrameForRange:), getAccessibilityFrameForRange, @encode (NSRect), "@:", @encode (NSRange));
  98. addMethod (@selector (accessibilityRTFForRange:), getAccessibilityRTFForRange, "@@:", @encode (NSRange));
  99. addMethod (@selector (accessibilityStyleRangeForIndex:), getAccessibilityStyleRangeForIndex, @encode (NSRange), "@:i");
  100. addMethod (@selector (accessibilityLineForIndex:), getAccessibilityLineForIndex, "i@:i");
  101. addMethod (@selector (setAccessibilitySelectedTextRange:), setAccessibilitySelectedTextRange, "v@:", @encode (NSRange));
  102. addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount, "i@:");
  103. addMethod (@selector (accessibilityRows), getAccessibilityRows, "@@:");
  104. addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount, "i@:");
  105. addMethod (@selector (accessibilityColumns), getAccessibilityColumns, "@@:");
  106. addMethod (@selector (accessibilityRowIndexRange), getAccessibilityRowIndexRange, @encode (NSRange), "@:");
  107. addMethod (@selector (accessibilityColumnIndexRange), getAccessibilityColumnIndexRange, @encode (NSRange), "@:");
  108. addMethod (@selector (accessibilityIndex), getAccessibilityIndex, "i@:");
  109. addMethod (@selector (accessibilityDisclosureLevel), getAccessibilityDisclosureLevel, "i@:");
  110. addMethod (@selector (accessibilityPerformIncrement), accessibilityPerformIncrement, "c@:");
  111. addMethod (@selector (accessibilityPerformDecrement), accessibilityPerformDecrement, "c@:");
  112. addMethod (@selector (accessibilityPerformDelete), accessibilityPerformDelete, "c@:");
  113. addMethod (@selector (accessibilityPerformPress), accessibilityPerformPress, "c@:");
  114. addMethod (@selector (accessibilityPerformRaise), accessibilityPerformRaise, "c@:");
  115. addMethod (@selector (accessibilityPerformShowMenu), accessibilityPerformShowMenu, "c@:");
  116. addMethod (@selector (accessibilityPerformPick), accessibilityPerformPick, "c@:");
  117. addMethod (@selector (isAccessibilitySelectorAllowed:), getIsAccessibilitySelectorAllowed, "c@:@");
  118. #if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
  119. addMethod (@selector (accessibilityChildrenInNavigationOrder), getAccessibilityChildren, "@@:");
  120. #endif
  121. registerClass();
  122. }
  123. private:
  124. static AccessibilityHandler* getHandler (id self) { return getIvar<AccessibilityHandler*> (self, "handler"); }
  125. template <typename MemberFn>
  126. static auto getInterface (id self, MemberFn fn) noexcept -> decltype ((std::declval<AccessibilityHandler>().*fn)())
  127. {
  128. if (auto* handler = getHandler (self))
  129. return (handler->*fn)();
  130. return nullptr;
  131. }
  132. static AccessibilityTextInterface* getTextInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getTextInterface); }
  133. static AccessibilityValueInterface* getValueInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getValueInterface); }
  134. static AccessibilityTableInterface* getTableInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getTableInterface); }
  135. static AccessibilityCellInterface* getCellInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getCellInterface); }
  136. static bool hasEditableText (AccessibilityHandler& handler) noexcept
  137. {
  138. return handler.getRole() == AccessibilityRole::editableText
  139. && handler.getTextInterface() != nullptr;
  140. }
  141. static bool nameIsAccessibilityValue (AccessibilityRole role) noexcept
  142. {
  143. return role == AccessibilityRole::staticText;
  144. }
  145. static bool hasSelection (AccessibilityRole role) noexcept
  146. {
  147. return role == AccessibilityRole::list
  148. || role == AccessibilityRole::popupMenu
  149. || role == AccessibilityRole::tree;
  150. }
  151. static bool isSelectable (AccessibilityRole role) noexcept
  152. {
  153. return role == AccessibilityRole::listItem
  154. || role == AccessibilityRole::menuItem
  155. || role == AccessibilityRole::treeItem;
  156. }
  157. static BOOL performActionIfSupported (id self, AccessibilityActionType actionType)
  158. {
  159. if (auto* handler = getHandler (self))
  160. if (handler->getActions().invoke (actionType))
  161. return YES;
  162. return NO;
  163. }
  164. //==============================================================================
  165. static BOOL getAccessibilityNotifiesWhenDestroyed (id, SEL) { return YES; }
  166. static BOOL getIsAccessibilityElement (id self, SEL)
  167. {
  168. if (auto* handler = getHandler (self))
  169. return ! handler->isIgnored()
  170. && handler->getRole() != AccessibilityRole::window;
  171. return NO;
  172. }
  173. static BOOL getIsAccessibilityEnabled (id self, SEL)
  174. {
  175. if (auto* handler = getHandler (self))
  176. return handler->getComponent().isEnabled();
  177. return NO;
  178. }
  179. static id getAccessibilityWindow (id self, SEL)
  180. {
  181. return [[self accessibilityParent] accessibilityWindow];
  182. }
  183. static id getAccessibilityFocusedUIElement (id self, SEL)
  184. {
  185. if (auto* handler = getHandler (self))
  186. {
  187. if (auto* modal = Component::getCurrentlyModalComponent())
  188. {
  189. const auto& component = handler->getComponent();
  190. if (! component.isParentOf (modal)
  191. && component.isCurrentlyBlockedByAnotherModalComponent())
  192. {
  193. if (auto* modalHandler = modal->getAccessibilityHandler())
  194. {
  195. if (auto* focusChild = modalHandler->getChildFocus())
  196. return (id) focusChild->getNativeImplementation();
  197. return (id) modalHandler->getNativeImplementation();
  198. }
  199. }
  200. }
  201. if (auto* focusChild = handler->getChildFocus())
  202. return (id) focusChild->getNativeImplementation();
  203. }
  204. return nil;
  205. }
  206. static id accessibilityHitTest (id self, SEL, NSPoint point)
  207. {
  208. if (auto* handler = getHandler (self))
  209. {
  210. if (auto* child = handler->getChildAt (convertToIntPoint (flippedScreenPoint (point))))
  211. return (id) child->getNativeImplementation();
  212. return self;
  213. }
  214. return nil;
  215. }
  216. static id getAccessibilityParent (id self, SEL)
  217. {
  218. if (auto* handler = getHandler (self))
  219. {
  220. if (auto* parentHandler = handler->getParent())
  221. return NSAccessibilityUnignoredAncestor ((id) parentHandler->getNativeImplementation());
  222. return NSAccessibilityUnignoredAncestor ((id) handler->getComponent().getWindowHandle());
  223. }
  224. return nil;
  225. }
  226. static NSArray* getAccessibilityChildren (id self, SEL)
  227. {
  228. if (auto* handler = getHandler (self))
  229. {
  230. auto children = handler->getChildren();
  231. NSMutableArray* accessibleChildren = [NSMutableArray arrayWithCapacity: (NSUInteger) children.size()];
  232. for (auto* childHandler : children)
  233. [accessibleChildren addObject: (id) childHandler->getNativeImplementation()];
  234. return accessibleChildren;
  235. }
  236. return nil;
  237. }
  238. static NSArray* getAccessibilitySelectedChildren (id self, SEL)
  239. {
  240. if (auto* handler = getHandler (self))
  241. {
  242. NSMutableArray* selected = [[NSMutableArray new] autorelease];
  243. for (auto* child : handler->getChildren())
  244. if (isSelectable (child->getRole()) && child->getCurrentState().isSelected())
  245. [selected addObject: (id) child->getNativeImplementation()];
  246. return selected;
  247. }
  248. return nil;
  249. }
  250. static void setAccessibilitySelectedChildren (id self, SEL, NSArray* selected)
  251. {
  252. if (auto* handler = getHandler (self))
  253. {
  254. for (auto* childHandler : handler->getChildren())
  255. {
  256. id nativeHandler = (id) childHandler->getNativeImplementation();
  257. const auto isSelected = [selected containsObject: nativeHandler];
  258. if (isSelectable (childHandler->getRole()))
  259. {
  260. if (childHandler->getCurrentState().isSelected() != isSelected)
  261. childHandler->getActions().invoke (AccessibilityActionType::toggle);
  262. }
  263. else if (childHandler->getCurrentState().isFocusable())
  264. {
  265. [nativeHandler setAccessibilityFocused: isSelected];
  266. }
  267. }
  268. }
  269. }
  270. static NSAccessibilityOrientation getAccessibilityOrientation (id self, SEL)
  271. {
  272. if (auto* handler = getHandler (self))
  273. return handler->getComponent().getBounds().toFloat().getAspectRatio() > 1.0f
  274. ? NSAccessibilityOrientationHorizontal
  275. : NSAccessibilityOrientationVertical;
  276. return NSAccessibilityOrientationUnknown;
  277. }
  278. static BOOL getIsAccessibilityFocused (id self, SEL)
  279. {
  280. return [[self accessibilityWindow] accessibilityFocusedUIElement] == self;
  281. }
  282. static void setAccessibilityFocused (id self, SEL, BOOL focused)
  283. {
  284. if (auto* handler = getHandler (self))
  285. {
  286. if (focused)
  287. handler->grabFocus();
  288. else
  289. handler->giveAwayFocus();
  290. }
  291. }
  292. static BOOL getIsAccessibilityModal (id self, SEL)
  293. {
  294. if (auto* handler = getHandler (self))
  295. return handler->getComponent().isCurrentlyModal();
  296. return NO;
  297. }
  298. static NSRect getAccessibilityFrame (id self, SEL)
  299. {
  300. if (auto* handler = getHandler (self))
  301. return flippedScreenRect (makeNSRect (handler->getComponent().getScreenBounds()));
  302. return NSZeroRect;
  303. }
  304. static NSAccessibilityRole getAccessibilityRole (id self, SEL)
  305. {
  306. if (auto* handler = getHandler (self))
  307. {
  308. switch (handler->getRole())
  309. {
  310. case AccessibilityRole::button: return NSAccessibilityButtonRole;
  311. case AccessibilityRole::toggleButton: return NSAccessibilityCheckBoxRole;
  312. case AccessibilityRole::radioButton: return NSAccessibilityRadioButtonRole;
  313. case AccessibilityRole::comboBox: return NSAccessibilityPopUpButtonRole;
  314. case AccessibilityRole::image: return NSAccessibilityImageRole;
  315. case AccessibilityRole::slider: return NSAccessibilitySliderRole;
  316. case AccessibilityRole::staticText: return NSAccessibilityStaticTextRole;
  317. case AccessibilityRole::editableText: return NSAccessibilityTextAreaRole;
  318. case AccessibilityRole::menuItem: return NSAccessibilityMenuItemRole;
  319. case AccessibilityRole::menuBar: return NSAccessibilityMenuRole;
  320. case AccessibilityRole::popupMenu: return NSAccessibilityMenuRole;
  321. case AccessibilityRole::table: return NSAccessibilityTableRole;
  322. case AccessibilityRole::tableHeader: return NSAccessibilityGroupRole;
  323. case AccessibilityRole::column: return NSAccessibilityColumnRole;
  324. case AccessibilityRole::row: return NSAccessibilityRowRole;
  325. case AccessibilityRole::cell: return NSAccessibilityCellRole;
  326. case AccessibilityRole::hyperlink: return NSAccessibilityLinkRole;
  327. case AccessibilityRole::list: return NSAccessibilityListRole;
  328. case AccessibilityRole::listItem: return NSAccessibilityRowRole;
  329. case AccessibilityRole::tree: return NSAccessibilityListRole;
  330. case AccessibilityRole::treeItem: return NSAccessibilityRowRole;
  331. case AccessibilityRole::progressBar: return NSAccessibilityProgressIndicatorRole;
  332. case AccessibilityRole::group: return NSAccessibilityGroupRole;
  333. case AccessibilityRole::dialogWindow: return NSAccessibilityWindowRole;
  334. case AccessibilityRole::window: return NSAccessibilityWindowRole;
  335. case AccessibilityRole::scrollBar: return NSAccessibilityScrollBarRole;
  336. case AccessibilityRole::tooltip: return NSAccessibilityWindowRole;
  337. case AccessibilityRole::splashScreen: return NSAccessibilityWindowRole;
  338. case AccessibilityRole::ignored: return NSAccessibilityUnknownRole;
  339. case AccessibilityRole::unspecified: return NSAccessibilityGroupRole;
  340. default: break;
  341. }
  342. return NSAccessibilityUnknownRole;
  343. }
  344. return nil;
  345. }
  346. static NSAccessibilityRole getAccessibilitySubrole (id self, SEL)
  347. {
  348. if (auto* handler = getHandler (self))
  349. {
  350. if (auto* textInterface = getTextInterface (self))
  351. if (textInterface->isDisplayingProtectedText())
  352. return NSAccessibilitySecureTextFieldSubrole;
  353. const auto role = handler->getRole();
  354. if (role == AccessibilityRole::window) return NSAccessibilityStandardWindowSubrole;
  355. if (role == AccessibilityRole::dialogWindow) return NSAccessibilityDialogSubrole;
  356. if (role == AccessibilityRole::tooltip
  357. || role == AccessibilityRole::splashScreen) return NSAccessibilityFloatingWindowSubrole;
  358. if (role == AccessibilityRole::toggleButton) return NSAccessibilityToggleSubrole;
  359. if (role == AccessibilityRole::treeItem) return NSAccessibilityOutlineRowSubrole;
  360. if (role == AccessibilityRole::row && getCellInterface (self) != nullptr) return NSAccessibilityTableRowSubrole;
  361. const auto& component = handler->getComponent();
  362. if (auto* documentWindow = component.findParentComponentOfClass<DocumentWindow>())
  363. {
  364. if (role == AccessibilityRole::button)
  365. {
  366. if (&component == documentWindow->getCloseButton()) return NSAccessibilityCloseButtonSubrole;
  367. if (&component == documentWindow->getMinimiseButton()) return NSAccessibilityMinimizeButtonSubrole;
  368. if (&component == documentWindow->getMaximiseButton()) return NSAccessibilityFullScreenButtonSubrole;
  369. }
  370. }
  371. }
  372. return NSAccessibilityUnknownRole;
  373. }
  374. static NSString* getAccessibilityTitle (id self, SEL)
  375. {
  376. if (auto* handler = getHandler (self))
  377. {
  378. if (nameIsAccessibilityValue (handler->getRole()))
  379. return @"";
  380. auto title = handler->getTitle();
  381. if (title.isEmpty() && handler->getComponent().isOnDesktop())
  382. title = getAccessibleApplicationOrPluginName();
  383. return juceStringToNS (title);
  384. }
  385. return nil;
  386. }
  387. static NSString* getAccessibilityLabel (id self, SEL)
  388. {
  389. if (auto* handler = getHandler (self))
  390. return juceStringToNS (handler->getDescription());
  391. return nil;
  392. }
  393. static NSString* getAccessibilityHelp (id self, SEL)
  394. {
  395. if (auto* handler = getHandler (self))
  396. return juceStringToNS (handler->getHelp());
  397. return nil;
  398. }
  399. static id getAccessibilityValue (id self, SEL)
  400. {
  401. if (auto* handler = getHandler (self))
  402. {
  403. if (nameIsAccessibilityValue (handler->getRole()))
  404. return juceStringToNS (handler->getTitle());
  405. if (hasEditableText (*handler))
  406. {
  407. auto* textInterface = handler->getTextInterface();
  408. return juceStringToNS (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() }));
  409. }
  410. if (handler->getCurrentState().isCheckable())
  411. return handler->getCurrentState().isChecked() ? @(1) : @(0);
  412. if (auto* valueInterface = handler->getValueInterface())
  413. return juceStringToNS (valueInterface->getCurrentValueAsString());
  414. }
  415. return nil;
  416. }
  417. static void setAccessibilityValue (id self, SEL, NSString* value)
  418. {
  419. if (auto* handler = getHandler (self))
  420. {
  421. if (hasEditableText (*handler))
  422. {
  423. handler->getTextInterface()->setText (nsStringToJuce (value));
  424. return;
  425. }
  426. if (auto* valueInterface = handler->getValueInterface())
  427. if (! valueInterface->isReadOnly())
  428. valueInterface->setValueAsString (nsStringToJuce (value));
  429. }
  430. }
  431. //==============================================================================
  432. static NSInteger getAccessibilityInsertionPointLineNumber (id self, SEL)
  433. {
  434. if (auto* textInterface = getTextInterface (self))
  435. return [self accessibilityLineForIndex: textInterface->getTextInsertionOffset()];
  436. return 0;
  437. }
  438. static NSRange getAccessibilitySharedCharacterRange (id self, SEL)
  439. {
  440. return [self accessibilityVisibleCharacterRange];
  441. }
  442. static NSArray* getAccessibilitySharedTextUIElements (id self, SEL)
  443. {
  444. return [NSArray arrayWithObject: self];
  445. }
  446. static NSRange getAccessibilityVisibleCharacterRange (id self, SEL)
  447. {
  448. if (auto* textInterface = getTextInterface (self))
  449. return juceRangeToNS ({ 0, textInterface->getTotalNumCharacters() });
  450. return NSMakeRange (0, 0);
  451. }
  452. static NSInteger getAccessibilityNumberOfCharacters (id self, SEL)
  453. {
  454. if (auto* textInterface = getTextInterface (self))
  455. return textInterface->getTotalNumCharacters();
  456. return 0;
  457. }
  458. static NSString* getAccessibilitySelectedText (id self, SEL)
  459. {
  460. if (auto* textInterface = getTextInterface (self))
  461. return juceStringToNS (textInterface->getText (textInterface->getSelection()));
  462. return nil;
  463. }
  464. static NSRange getAccessibilitySelectedTextRange (id self, SEL)
  465. {
  466. if (auto* textInterface = getTextInterface (self))
  467. return juceRangeToNS (textInterface->getSelection());
  468. return NSMakeRange (0, 0);
  469. }
  470. static NSArray* getAccessibilitySelectedTextRanges (id self, SEL)
  471. {
  472. return [NSArray arrayWithObject: [NSValue valueWithRange: [self accessibilitySelectedTextRange]]];
  473. }
  474. static NSAttributedString* getAccessibilityAttributedStringForRange (id self, SEL, NSRange range)
  475. {
  476. NSString* string = [self accessibilityStringForRange: range];
  477. if (string != nil)
  478. return [[[NSAttributedString alloc] initWithString: string] autorelease];
  479. return nil;
  480. }
  481. static NSRange getAccessibilityRangeForLine (id self, SEL, NSInteger line)
  482. {
  483. if (auto* textInterface = getTextInterface (self))
  484. {
  485. auto text = textInterface->getText ({ 0, textInterface->getTotalNumCharacters() });
  486. auto lines = StringArray::fromLines (text);
  487. if (line < lines.size())
  488. {
  489. auto lineText = lines[(int) line];
  490. auto start = text.indexOf (lineText);
  491. if (start >= 0)
  492. return NSMakeRange ((NSUInteger) start, (NSUInteger) lineText.length());
  493. }
  494. }
  495. return NSMakeRange (0, 0);
  496. }
  497. static NSString* getAccessibilityStringForRange (id self, SEL, NSRange range)
  498. {
  499. if (auto* textInterface = getTextInterface (self))
  500. return juceStringToNS (textInterface->getText (nsRangeToJuce (range)));
  501. return nil;
  502. }
  503. static NSRange getAccessibilityRangeForPosition (id self, SEL, NSPoint position)
  504. {
  505. if (auto* handler = getHandler (self))
  506. {
  507. if (auto* textInterface = handler->getTextInterface())
  508. {
  509. auto screenPoint = convertToIntPoint (flippedScreenPoint (position));
  510. if (handler->getComponent().getScreenBounds().contains (screenPoint))
  511. {
  512. auto offset = textInterface->getOffsetAtPoint (screenPoint);
  513. if (offset >= 0)
  514. return NSMakeRange ((NSUInteger) offset, 1);
  515. }
  516. }
  517. }
  518. return NSMakeRange (0, 0);
  519. }
  520. static NSRange getAccessibilityRangeForIndex (id self, SEL, NSInteger index)
  521. {
  522. if (auto* textInterface = getTextInterface (self))
  523. if (isPositiveAndBelow (index, textInterface->getTotalNumCharacters()))
  524. return NSMakeRange ((NSUInteger) index, 1);
  525. return NSMakeRange (0, 0);
  526. }
  527. static NSRect getAccessibilityFrameForRange (id self, SEL, NSRange range)
  528. {
  529. if (auto* textInterface = getTextInterface (self))
  530. return flippedScreenRect (makeNSRect (textInterface->getTextBounds (nsRangeToJuce (range)).getBounds()));
  531. return NSZeroRect;
  532. }
  533. static NSData* getAccessibilityRTFForRange (id, SEL, NSRange)
  534. {
  535. return nil;
  536. }
  537. static NSRange getAccessibilityStyleRangeForIndex (id self, SEL, NSInteger)
  538. {
  539. return [self accessibilityVisibleCharacterRange];
  540. }
  541. static NSInteger getAccessibilityLineForIndex (id self, SEL, NSInteger index)
  542. {
  543. if (auto* textInterface = getTextInterface (self))
  544. {
  545. auto text = textInterface->getText ({ 0, (int) index });
  546. if (! text.isEmpty())
  547. return StringArray::fromLines (text).size() - 1;
  548. }
  549. return 0;
  550. }
  551. static void setAccessibilitySelectedTextRange (id self, SEL, NSRange selectedRange)
  552. {
  553. if (auto* textInterface = getTextInterface (self))
  554. {
  555. textInterface->setSelection ({});
  556. textInterface->setSelection (nsRangeToJuce (selectedRange));
  557. }
  558. }
  559. //==============================================================================
  560. static NSInteger getAccessibilityRowCount (id self, SEL)
  561. {
  562. if (auto* tableInterface = getTableInterface (self))
  563. return tableInterface->getNumRows();
  564. return 0;
  565. }
  566. static NSArray* getAccessibilityRows (id self, SEL)
  567. {
  568. NSMutableArray* rows = [[NSMutableArray new] autorelease];
  569. if (auto* tableInterface = getTableInterface (self))
  570. for (int row = 0; row < tableInterface->getNumRows(); ++row)
  571. if (auto* handler = tableInterface->getCellHandler (row, 0))
  572. [rows addObject: (id) handler->getNativeImplementation()];
  573. return rows;
  574. }
  575. static NSInteger getAccessibilityColumnCount (id self, SEL)
  576. {
  577. if (auto* tableInterface = getTableInterface (self))
  578. return tableInterface->getNumColumns();
  579. return 0;
  580. }
  581. static NSArray* getAccessibilityColumns (id self, SEL)
  582. {
  583. NSMutableArray* columns = [[NSMutableArray new] autorelease];
  584. if (auto* tableInterface = getTableInterface (self))
  585. for (int column = 0; column < tableInterface->getNumColumns(); ++column)
  586. if (auto* handler = tableInterface->getCellHandler (0, column))
  587. [columns addObject: (id) handler->getNativeImplementation()];
  588. return columns;
  589. }
  590. //==============================================================================
  591. static NSRange getAccessibilityRowIndexRange (id self, SEL)
  592. {
  593. if (auto* cellInterface = getCellInterface (self))
  594. return NSMakeRange ((NSUInteger) cellInterface->getRowIndex(),
  595. (NSUInteger) cellInterface->getRowSpan());
  596. return NSMakeRange (0, 0);
  597. }
  598. static NSRange getAccessibilityColumnIndexRange (id self, SEL)
  599. {
  600. if (auto* cellInterface = getCellInterface (self))
  601. return NSMakeRange ((NSUInteger) cellInterface->getColumnIndex(),
  602. (NSUInteger) cellInterface->getColumnSpan());
  603. return NSMakeRange (0, 0);
  604. }
  605. static NSInteger getAccessibilityIndex (id self, SEL)
  606. {
  607. if (auto* handler = getHandler (self))
  608. {
  609. if (auto* cellInterface = handler->getCellInterface())
  610. {
  611. NSAccessibilityRole role = [self accessibilityRole];
  612. if ([role isEqual: NSAccessibilityRowRole])
  613. return cellInterface->getRowIndex();
  614. if ([role isEqual: NSAccessibilityColumnRole])
  615. return cellInterface->getColumnIndex();
  616. }
  617. }
  618. return 0;
  619. }
  620. static NSInteger getAccessibilityDisclosureLevel (id self, SEL)
  621. {
  622. if (auto* handler = getHandler (self))
  623. if (auto* cellInterface = handler->getCellInterface())
  624. return cellInterface->getDisclosureLevel();
  625. return 0;
  626. }
  627. //==============================================================================
  628. static BOOL accessibilityPerformPress (id self, SEL) { return performActionIfSupported (self, AccessibilityActionType::press); }
  629. static BOOL accessibilityPerformShowMenu (id self, SEL) { return performActionIfSupported (self, AccessibilityActionType::showMenu); }
  630. static BOOL accessibilityPerformPick (id self, SEL) { return [self accessibilityPerformPress]; }
  631. static BOOL accessibilityPerformRaise (id self, SEL)
  632. {
  633. if (auto* handler = getHandler (self))
  634. {
  635. if (handler->getCurrentState().isFocusable())
  636. {
  637. handler->grabFocus();
  638. return YES;
  639. }
  640. }
  641. return NO;
  642. }
  643. static BOOL accessibilityPerformIncrement (id self, SEL)
  644. {
  645. if (auto* valueInterface = getValueInterface (self))
  646. {
  647. if (! valueInterface->isReadOnly())
  648. {
  649. auto range = valueInterface->getRange();
  650. if (range.isValid())
  651. {
  652. valueInterface->setValue (jlimit (range.getMinimumValue(),
  653. range.getMaximumValue(),
  654. valueInterface->getCurrentValue() + range.getInterval()));
  655. return YES;
  656. }
  657. }
  658. }
  659. return NO;
  660. }
  661. static BOOL accessibilityPerformDecrement (id self, SEL)
  662. {
  663. if (auto* valueInterface = getValueInterface (self))
  664. {
  665. if (! valueInterface->isReadOnly())
  666. {
  667. auto range = valueInterface->getRange();
  668. if (range.isValid())
  669. {
  670. valueInterface->setValue (jlimit (range.getMinimumValue(),
  671. range.getMaximumValue(),
  672. valueInterface->getCurrentValue() - range.getInterval()));
  673. return YES;
  674. }
  675. }
  676. }
  677. return NO;
  678. }
  679. static BOOL accessibilityPerformDelete (id self, SEL)
  680. {
  681. if (auto* handler = getHandler (self))
  682. {
  683. if (hasEditableText (*handler))
  684. {
  685. handler->getTextInterface()->setText ({});
  686. return YES;
  687. }
  688. if (auto* valueInterface = handler->getValueInterface())
  689. {
  690. if (! valueInterface->isReadOnly())
  691. {
  692. valueInterface->setValue ({});
  693. return YES;
  694. }
  695. }
  696. }
  697. return NO;
  698. }
  699. //==============================================================================
  700. static BOOL getIsAccessibilitySelectorAllowed (id self, SEL, SEL selector)
  701. {
  702. if (auto* handler = getHandler (self))
  703. {
  704. for (auto textSelector : { @selector (accessibilityInsertionPointLineNumber),
  705. @selector (accessibilitySharedCharacterRange),
  706. @selector (accessibilitySharedTextUIElements),
  707. @selector (accessibilityVisibleCharacterRange),
  708. @selector (accessibilityNumberOfCharacters),
  709. @selector (accessibilitySelectedText),
  710. @selector (accessibilitySelectedTextRange),
  711. @selector (accessibilitySelectedTextRanges),
  712. @selector (accessibilityAttributedStringForRange:),
  713. @selector (accessibilityRangeForLine:),
  714. @selector (accessibilityStringForRange:),
  715. @selector (accessibilityRangeForPosition:),
  716. @selector (accessibilityRangeForIndex:),
  717. @selector (accessibilityFrameForRange:),
  718. @selector (accessibilityRTFForRange:),
  719. @selector (accessibilityStyleRangeForIndex:),
  720. @selector (accessibilityLineForIndex:),
  721. @selector (setAccessibilitySelectedTextRange:) })
  722. {
  723. if (selector == textSelector)
  724. return handler->getTextInterface() != nullptr;
  725. }
  726. for (auto tableSelector : { @selector (accessibilityRowCount),
  727. @selector (accessibilityRows),
  728. @selector (accessibilityColumnCount),
  729. @selector (accessibilityColumns) })
  730. {
  731. if (selector == tableSelector)
  732. return handler->getTableInterface() != nullptr;
  733. }
  734. for (auto cellSelector : { @selector (accessibilityRowIndexRange),
  735. @selector (accessibilityColumnIndexRange),
  736. @selector (accessibilityIndex),
  737. @selector (accessibilityDisclosureLevel) })
  738. {
  739. if (selector == cellSelector)
  740. return handler->getCellInterface() != nullptr;
  741. }
  742. for (auto valueSelector : { @selector (accessibilityValue),
  743. @selector (setAccessibilityValue:),
  744. @selector (accessibilityPerformDelete),
  745. @selector (accessibilityPerformIncrement),
  746. @selector (accessibilityPerformDecrement) })
  747. {
  748. if (selector != valueSelector)
  749. continue;
  750. auto* valueInterface = handler->getValueInterface();
  751. if (selector == @selector (accessibilityValue))
  752. return valueInterface != nullptr
  753. || hasEditableText (*handler)
  754. || nameIsAccessibilityValue (handler->getRole())
  755. || handler->getCurrentState().isCheckable();
  756. auto hasEditableValue = [valueInterface] { return valueInterface != nullptr && ! valueInterface->isReadOnly(); };
  757. if (selector == @selector (setAccessibilityValue:)
  758. || selector == @selector (accessibilityPerformDelete))
  759. return hasEditableValue() || hasEditableText (*handler);
  760. auto isRanged = [valueInterface] { return valueInterface != nullptr && valueInterface->getRange().isValid(); };
  761. if (selector == @selector (accessibilityPerformIncrement)
  762. || selector == @selector (accessibilityPerformDecrement))
  763. return hasEditableValue() && isRanged();
  764. return NO;
  765. }
  766. for (auto actionSelector : { @selector (accessibilityPerformPick),
  767. @selector (accessibilityPerformPress),
  768. @selector (accessibilityPerformRaise),
  769. @selector (accessibilityPerformShowMenu),
  770. @selector (setAccessibilityFocused:) })
  771. {
  772. if (selector != actionSelector)
  773. continue;
  774. if (selector == @selector (accessibilityPerformPick)
  775. || selector == @selector (accessibilityPerformPress))
  776. return handler->getActions().contains (AccessibilityActionType::press);
  777. if (selector == @selector (accessibilityPerformRaise)
  778. || selector == @selector (setAccessibilityFocused:))
  779. return handler->getCurrentState().isFocusable();
  780. if (selector == @selector (accessibilityPerformShowMenu))
  781. return handler->getActions().contains (AccessibilityActionType::showMenu);
  782. }
  783. if (selector == @selector (accessibilitySelectedChildren))
  784. return hasSelection (handler->getRole());
  785. if (selector == @selector (accessibilityOrientation))
  786. return handler->getRole() == AccessibilityRole::scrollBar;
  787. return sendSuperclassMessage<BOOL> (self, @selector (isAccessibilitySelectorAllowed:), selector);
  788. }
  789. return NO;
  790. }
  791. //==============================================================================
  792. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityElement)
  793. };
  794. //==============================================================================
  795. AccessibilityElement::Holder accessibilityElement;
  796. //==============================================================================
  797. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
  798. };
  799. //==============================================================================
  800. AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
  801. {
  802. return (AccessibilityNativeHandle*) nativeImpl->getAccessibilityElement();
  803. }
  804. static void sendAccessibilityEvent (id accessibilityElement,
  805. NSAccessibilityNotificationName notification,
  806. NSDictionary* userInfo)
  807. {
  808. jassert (notification != NSAccessibilityNotificationName{});
  809. NSAccessibilityPostNotificationWithUserInfo (accessibilityElement, notification, userInfo);
  810. }
  811. void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
  812. {
  813. auto notification = [eventType]
  814. {
  815. switch (eventType)
  816. {
  817. case InternalAccessibilityEvent::elementCreated: return NSAccessibilityCreatedNotification;
  818. case InternalAccessibilityEvent::elementDestroyed: return NSAccessibilityUIElementDestroyedNotification;
  819. case InternalAccessibilityEvent::focusChanged: return NSAccessibilityFocusedUIElementChangedNotification;
  820. case InternalAccessibilityEvent::windowOpened: return NSAccessibilityWindowCreatedNotification;
  821. case InternalAccessibilityEvent::windowClosed: break;
  822. }
  823. return NSAccessibilityNotificationName{};
  824. }();
  825. if (notification != NSAccessibilityNotificationName{})
  826. sendAccessibilityEvent ((id) handler.getNativeImplementation(), notification, nil);
  827. }
  828. void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
  829. {
  830. auto notification = [eventType]
  831. {
  832. switch (eventType)
  833. {
  834. case AccessibilityEvent::textSelectionChanged: return NSAccessibilitySelectedTextChangedNotification;
  835. case AccessibilityEvent::rowSelectionChanged: return NSAccessibilitySelectedRowsChangedNotification;
  836. case AccessibilityEvent::textChanged:
  837. case AccessibilityEvent::valueChanged: return NSAccessibilityValueChangedNotification;
  838. case AccessibilityEvent::structureChanged: return NSAccessibilityLayoutChangedNotification;
  839. }
  840. return NSAccessibilityNotificationName{};
  841. }();
  842. if (notification != NSAccessibilityNotificationName{})
  843. {
  844. id accessibilityElement = (id) getNativeImplementation();
  845. sendAccessibilityEvent (accessibilityElement, notification,
  846. (notification == NSAccessibilityLayoutChangedNotification
  847. ? @{ NSAccessibilityUIElementsKey: accessibilityElement }
  848. : nil));
  849. }
  850. }
  851. void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority)
  852. {
  853. auto nsPriority = [priority]
  854. {
  855. switch (priority)
  856. {
  857. case AnnouncementPriority::low: return NSAccessibilityPriorityLow;
  858. case AnnouncementPriority::medium: return NSAccessibilityPriorityMedium;
  859. case AnnouncementPriority::high: return NSAccessibilityPriorityHigh;
  860. }
  861. jassertfalse;
  862. return NSAccessibilityPriorityLow;
  863. }();
  864. sendAccessibilityEvent ((id) [NSApp mainWindow],
  865. NSAccessibilityAnnouncementRequestedNotification,
  866. @{ NSAccessibilityAnnouncementKey: juceStringToNS (announcementString),
  867. NSAccessibilityPriorityKey: @(nsPriority) });
  868. }
  869. AccessibilityHandler::AccessibilityNativeImpl* AccessibilityHandler::createNativeImpl (AccessibilityHandler& handler)
  870. {
  871. return new AccessibilityHandler::AccessibilityNativeImpl (handler);
  872. }
  873. void AccessibilityHandler::DestroyNativeImpl::operator() (AccessibilityHandler::AccessibilityNativeImpl* impl) const noexcept
  874. {
  875. delete impl;
  876. }
  877. } // namespace juce