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.

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