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.

1149 lines
52KB

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