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.

667 lines
33KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2022 - 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 7 End-User License
  8. Agreement and JUCE Privacy Policy.
  9. End User License Agreement: www.juce.com/juce-7-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. static void juceFreeAccessibilityPlatformSpecificData (UIAccessibilityElement* element)
  19. {
  20. if (auto* container = juce::getIvar<UIAccessibilityElement*> (element, "container"))
  21. {
  22. object_setInstanceVariable (element, "container", nullptr);
  23. object_setInstanceVariable (container, "handler", nullptr);
  24. [container release];
  25. }
  26. }
  27. namespace juce
  28. {
  29. #define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
  30. template <typename> struct Signature;
  31. template <typename Result, typename... Args>
  32. struct Signature<Result (Args...)> {};
  33. // @selector isn't constexpr, so the 'sel' members are functions rather than static constexpr data members
  34. struct SignatureHasText : Signature<BOOL()> { static auto sel() { return @selector (hasText); } };
  35. struct SignatureSetSelectedTextRange : Signature<void (UITextRange*)> { static auto sel() { return @selector (setSelectedTextRange:); } };
  36. struct SignatureSelectedTextRange : Signature<UITextRange*()> { static auto sel() { return @selector (selectedTextRange); } };
  37. struct SignatureMarkedTextRange : Signature<UITextRange*()> { static auto sel() { return @selector (markedTextRange); } };
  38. struct SignatureSetMarkedTextSelectedRange : Signature<void (NSString*, NSRange)> { static auto sel() { return @selector (setMarkedText:selectedRange:); } };
  39. struct SignatureUnmarkText : Signature<void()> { static auto sel() { return @selector (unmarkText); } };
  40. struct SignatureMarkedTextStyle : Signature<NSDictionary<NSAttributedStringKey, id>*()> { static auto sel() { return @selector (markedTextStyle); } };
  41. struct SignatureSetMarkedTextStyle : Signature<void (NSDictionary<NSAttributedStringKey, id>*)> { static auto sel() { return @selector (setMarkedTextStyle:); } };
  42. struct SignatureBeginningOfDocument : Signature<UITextPosition*()> { static auto sel() { return @selector (beginningOfDocument); } };
  43. struct SignatureEndOfDocument : Signature<UITextPosition*()> { static auto sel() { return @selector (endOfDocument); } };
  44. struct SignatureTokenizer : Signature<id<UITextInputTokenizer>()> { static auto sel() { return @selector (tokenizer); } };
  45. struct SignatureBaseWritingDirection : Signature<NSWritingDirection (UITextPosition*, UITextStorageDirection)> { static auto sel() { return @selector (baseWritingDirectionForPosition:inDirection:); } };
  46. struct SignatureCaretRectForPosition : Signature<CGRect (UITextPosition*)> { static auto sel() { return @selector (caretRectForPosition:); } };
  47. struct SignatureCharacterRangeByExtending : Signature<UITextRange* (UITextPosition*, UITextLayoutDirection)> { static auto sel() { return @selector (characterRangeByExtendingPosition:inDirection:); } };
  48. struct SignatureCharacterRangeAtPoint : Signature<UITextRange* (CGPoint)> { static auto sel() { return @selector (characterRangeAtPoint:); } };
  49. struct SignatureClosestPositionToPoint : Signature<UITextPosition* (CGPoint)> { static auto sel() { return @selector (closestPositionToPoint:); } };
  50. struct SignatureClosestPositionToPointInRange : Signature<UITextPosition* (CGPoint, UITextRange*)> { static auto sel() { return @selector (closestPositionToPoint:withinRange:); } };
  51. struct SignatureComparePositionToPosition : Signature<NSComparisonResult (UITextPosition*, UITextPosition*)> { static auto sel() { return @selector (comparePosition:toPosition:); } };
  52. struct SignatureOffsetFromPositionToPosition : Signature<NSInteger (UITextPosition*, UITextPosition*)> { static auto sel() { return @selector (offsetFromPosition:toPosition:); } };
  53. struct SignaturePositionFromPositionInDirection : Signature<UITextPosition* (UITextPosition*, UITextLayoutDirection, NSInteger)> { static auto sel() { return @selector (positionFromPosition:inDirection:offset:); } };
  54. struct SignaturePositionFromPositionOffset : Signature<UITextPosition* (UITextPosition*, NSInteger)> { static auto sel() { return @selector (positionFromPosition:offset:); } };
  55. struct SignatureFirstRectForRange : Signature<CGRect (UITextRange*)> { static auto sel() { return @selector (firstRectForRange:); } };
  56. struct SignatureSelectionRectsForRange : Signature<NSArray<UITextSelectionRect*>* (UITextRange*)> { static auto sel() { return @selector (selectionRectsForRange:); } };
  57. struct SignaturePositionWithinRange : Signature<UITextPosition* (UITextRange*, UITextLayoutDirection)> { static auto sel() { return @selector (positionWithinRange:farthestInDirection:); } };
  58. struct SignatureReplaceRangeWithText : Signature<void (UITextRange*, NSString*)> { static auto sel() { return @selector (replaceRange:withText:); } };
  59. struct SignatureSetBaseWritingDirection : Signature<void (NSWritingDirection, UITextRange*)> { static auto sel() { return @selector (setBaseWritingDirection:forRange:); } };
  60. struct SignatureTextInRange : Signature<NSString* (UITextRange*)> { static auto sel() { return @selector (textInRange:); } };
  61. struct SignatureTextRangeFromPosition : Signature<UITextRange* (UITextPosition*, UITextPosition*)> { static auto sel() { return @selector (textRangeFromPosition:toPosition:); } };
  62. struct SignatureSetInputDelegate : Signature<void (id)> { static auto sel() { return @selector (setInputDelegate:); } };
  63. struct SignatureInputDelegate : Signature<id()> { static auto sel() { return @selector (inputDelegate); } };
  64. struct SignatureKeyboardType : Signature<UIKeyboardType()> { static auto sel() { return @selector (keyboardType); } };
  65. struct SignatureAutocapitalizationType : Signature<UITextAutocapitalizationType()> { static auto sel() { return @selector (autocapitalizationType); } };
  66. struct SignatureAutocorrectionType : Signature<UITextAutocorrectionType()> { static auto sel() { return @selector (autocorrectionType); } };
  67. //==============================================================================
  68. class AccessibilityHandler::AccessibilityNativeImpl
  69. {
  70. public:
  71. explicit AccessibilityNativeImpl (AccessibilityHandler& handler)
  72. : accessibilityElement (AccessibilityElement::create (handler))
  73. {
  74. }
  75. UIAccessibilityElement* getAccessibilityElement() const noexcept
  76. {
  77. return accessibilityElement.get();
  78. }
  79. private:
  80. //==============================================================================
  81. class AccessibilityContainer : public AccessibleObjCClass<NSObject>
  82. {
  83. public:
  84. AccessibilityContainer()
  85. : AccessibleObjCClass ("JUCEUIAccessibilityContainer_")
  86. {
  87. addMethod (@selector (isAccessibilityElement), [] (id, SEL) { return false; });
  88. addMethod (@selector (accessibilityFrame), [] (id self, SEL)
  89. {
  90. if (auto* handler = getHandler (self))
  91. return convertToCGRect (handler->getComponent().getScreenBounds());
  92. return CGRectZero;
  93. });
  94. addMethod (@selector (accessibilityElements), [] (id self, SEL) -> NSArray*
  95. {
  96. if (auto* handler = getHandler (self))
  97. return getContainerAccessibilityElements (*handler);
  98. return nil;
  99. });
  100. if (@available (iOS 11.0, *))
  101. {
  102. addMethod (@selector (accessibilityDataTableCellElementForRow:column:), [] (id self, SEL, NSUInteger row, NSUInteger column) -> id
  103. {
  104. if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
  105. if (auto* tableInterface = tableHandler->getTableInterface())
  106. if (auto* cellHandler = tableInterface->getCellHandler ((int) row, (int) column))
  107. if (auto* parent = getAccessibleParent (cellHandler))
  108. return static_cast<id> (parent->getNativeImplementation());
  109. return nil;
  110. });
  111. addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount);
  112. addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount);
  113. addMethod (@selector (accessibilityHeaderElementsForColumn:), [] (id self, SEL, NSUInteger column) -> NSArray*
  114. {
  115. if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
  116. {
  117. if (auto* tableInterface = tableHandler->getTableInterface())
  118. {
  119. if (auto* header = tableInterface->getHeaderHandler())
  120. {
  121. if (isPositiveAndBelow (column, header->getChildren().size()))
  122. {
  123. auto* result = [NSMutableArray new];
  124. [result addObject: static_cast<id> (header->getChildren()[(size_t) column]->getNativeImplementation())];
  125. return result;
  126. }
  127. }
  128. }
  129. }
  130. return nullptr;
  131. });
  132. addProtocol (@protocol (UIAccessibilityContainerDataTable));
  133. addMethod (@selector (accessibilityContainerType), [] (id self, SEL) -> NSInteger
  134. {
  135. if (auto* handler = getHandler (self))
  136. {
  137. if (handler->getTableInterface() != nullptr)
  138. {
  139. if (@available (iOS 11.0, *))
  140. return UIAccessibilityContainerTypeDataTable;
  141. return 1; // UIAccessibilityContainerTypeDataTable
  142. }
  143. const auto handlerRole = handler->getRole();
  144. if (handlerRole == AccessibilityRole::popupMenu
  145. || handlerRole == AccessibilityRole::list
  146. || handlerRole == AccessibilityRole::tree)
  147. {
  148. if (@available (iOS 11.0, *))
  149. return UIAccessibilityContainerTypeList;
  150. return 2; // UIAccessibilityContainerTypeList
  151. }
  152. }
  153. if (@available (iOS 11.0, *))
  154. return UIAccessibilityContainerTypeNone;
  155. return 0; // UIAccessibilityContainerTypeNone
  156. });
  157. }
  158. registerClass();
  159. }
  160. private:
  161. static const AccessibilityHandler* getAccessibleParent (const AccessibilityHandler* h)
  162. {
  163. if (h == nullptr)
  164. return nullptr;
  165. if ([static_cast<id> (h->getNativeImplementation()) isAccessibilityElement])
  166. return h;
  167. return getAccessibleParent (h->getParent());
  168. }
  169. static AccessibilityHandler* getHandler (id self)
  170. {
  171. return getIvar<AccessibilityHandler*> (self, "handler");
  172. }
  173. };
  174. //==============================================================================
  175. class AccessibilityElement : public AccessibleObjCClass<UIAccessibilityElement>
  176. {
  177. template <typename Func, typename... Items>
  178. static constexpr void forEach (Func&& func, Items&&... items)
  179. {
  180. (func (std::forward<Items> (items)), ...);
  181. }
  182. public:
  183. enum class Type { defaultElement, textElement };
  184. static Holder create (AccessibilityHandler& handler)
  185. {
  186. static AccessibilityElement cls { Type::defaultElement };
  187. static AccessibilityElement textCls { Type::textElement };
  188. id instance = (hasEditableText (handler) ? textCls : cls).createInstance();
  189. Holder element ([instance initWithAccessibilityContainer: static_cast<id> (handler.getComponent().getWindowHandle())]);
  190. object_setInstanceVariable (element.get(), "handler", &handler);
  191. return element;
  192. }
  193. AccessibilityElement (Type elementType)
  194. {
  195. addMethod (@selector (isAccessibilityElement), [] (id self, SEL)
  196. {
  197. auto* handler = getHandler (self);
  198. const auto hasAccessiblePropertiesOrIsTableCell = [] (auto& handlerRef)
  199. {
  200. const auto isTableCell = [&]
  201. {
  202. if (auto* tableHandler = getEnclosingHandlerWithInterface (&handlerRef, &AccessibilityHandler::getTableInterface))
  203. {
  204. if (auto* tableInterface = tableHandler->getTableInterface())
  205. {
  206. return tableInterface->getRowSpan (handlerRef).hasValue()
  207. && tableInterface->getColumnSpan (handlerRef).hasValue();
  208. }
  209. }
  210. return false;
  211. };
  212. return handlerRef.getTitle().isNotEmpty()
  213. || handlerRef.getHelp().isNotEmpty()
  214. || handlerRef.getTextInterface() != nullptr
  215. || handlerRef.getValueInterface() != nullptr
  216. || isTableCell();
  217. };
  218. return handler != nullptr
  219. && ! handler->isIgnored()
  220. && handler->getRole() != AccessibilityRole::window
  221. && hasAccessiblePropertiesOrIsTableCell (*handler);
  222. });
  223. addMethod (@selector (accessibilityContainer), [] (id self, SEL) -> id
  224. {
  225. if (auto* handler = getHandler (self))
  226. {
  227. if (handler->getComponent().isOnDesktop())
  228. return static_cast<id> (handler->getComponent().getWindowHandle());
  229. if (! handler->getChildren().empty())
  230. {
  231. if (UIAccessibilityElement* container = getContainer (self))
  232. return container;
  233. static AccessibilityContainer cls;
  234. id container = cls.createInstance();
  235. object_setInstanceVariable (container, "handler", handler);
  236. object_setInstanceVariable (self, "container", container);
  237. return container;
  238. }
  239. if (auto* parent = handler->getParent())
  240. return [static_cast<id> (parent->getNativeImplementation()) accessibilityContainer];
  241. }
  242. return nil;
  243. });
  244. addMethod (@selector (accessibilityFrame), [] (id self, SEL)
  245. {
  246. if (auto* handler = getHandler (self))
  247. return convertToCGRect (handler->getComponent().getScreenBounds());
  248. return CGRectZero;
  249. });
  250. addMethod (@selector (accessibilityTraits), [] (id self, SEL)
  251. {
  252. auto traits = UIAccessibilityTraits{};
  253. if (auto* handler = getHandler (self))
  254. {
  255. traits |= [&handler]
  256. {
  257. switch (handler->getRole())
  258. {
  259. case AccessibilityRole::button:
  260. case AccessibilityRole::toggleButton:
  261. case AccessibilityRole::radioButton:
  262. case AccessibilityRole::comboBox: return UIAccessibilityTraitButton;
  263. case AccessibilityRole::label:
  264. case AccessibilityRole::staticText: return UIAccessibilityTraitStaticText;
  265. case AccessibilityRole::image: return UIAccessibilityTraitImage;
  266. case AccessibilityRole::tableHeader: return UIAccessibilityTraitHeader;
  267. case AccessibilityRole::hyperlink: return UIAccessibilityTraitLink;
  268. case AccessibilityRole::ignored: return UIAccessibilityTraitNotEnabled;
  269. case AccessibilityRole::editableText: return UIAccessibilityTraitKeyboardKey;
  270. case AccessibilityRole::slider:
  271. case AccessibilityRole::menuItem:
  272. case AccessibilityRole::menuBar:
  273. case AccessibilityRole::popupMenu:
  274. case AccessibilityRole::table:
  275. case AccessibilityRole::column:
  276. case AccessibilityRole::row:
  277. case AccessibilityRole::cell:
  278. case AccessibilityRole::list:
  279. case AccessibilityRole::listItem:
  280. case AccessibilityRole::tree:
  281. case AccessibilityRole::treeItem:
  282. case AccessibilityRole::progressBar:
  283. case AccessibilityRole::group:
  284. case AccessibilityRole::dialogWindow:
  285. case AccessibilityRole::window:
  286. case AccessibilityRole::scrollBar:
  287. case AccessibilityRole::tooltip:
  288. case AccessibilityRole::splashScreen:
  289. case AccessibilityRole::unspecified: break;
  290. }
  291. return UIAccessibilityTraitNone;
  292. }();
  293. const auto state = handler->getCurrentState();
  294. if (state.isSelected() || state.isChecked())
  295. traits |= UIAccessibilityTraitSelected;
  296. if (auto* valueInterface = getValueInterface (self))
  297. if (! valueInterface->isReadOnly() && valueInterface->getRange().isValid())
  298. traits |= UIAccessibilityTraitAdjustable;
  299. }
  300. return traits | sendSuperclassMessage<UIAccessibilityTraits> (self, @selector (accessibilityTraits));
  301. });
  302. addMethod (@selector (accessibilityLabel), getAccessibilityTitle);
  303. addMethod (@selector (accessibilityHint), getAccessibilityHelp);
  304. addMethod (@selector (accessibilityValue), [] (id self, SEL) -> NSString*
  305. {
  306. if (auto* handler = getHandler (self))
  307. {
  308. if (handler->getCurrentState().isCheckable())
  309. return handler->getCurrentState().isChecked() ? @"1" : @"0";
  310. return (NSString*) getAccessibilityValueFromInterfaces (*handler);
  311. }
  312. return nil;
  313. });
  314. addMethod (@selector (setAccessibilityValue:), setAccessibilityValue);
  315. addMethod (@selector (accessibilityElementDidBecomeFocused), [] (id self, SEL)
  316. {
  317. if (auto* handler = getHandler (self))
  318. {
  319. const WeakReference<Component> safeComponent (&handler->getComponent());
  320. performActionIfSupported (self, AccessibilityActionType::focus);
  321. if (safeComponent != nullptr)
  322. handler->grabFocus();
  323. }
  324. });
  325. addMethod (@selector (accessibilityElementDidLoseFocus), [] (id self, SEL)
  326. {
  327. if (auto* handler = getHandler (self))
  328. handler->giveAwayFocus();
  329. });
  330. addMethod (@selector (accessibilityElementIsFocused), [] (id self, SEL) -> BOOL
  331. {
  332. if (auto* handler = getHandler (self))
  333. return handler->hasFocus (false);
  334. return NO;
  335. });
  336. addMethod (@selector (accessibilityViewIsModal), getIsAccessibilityModal);
  337. addMethod (@selector (accessibilityActivate), [] (id self, SEL)
  338. {
  339. if (auto* handler = getHandler (self))
  340. {
  341. // Occasionally VoiceOver sends accessibilityActivate to the wrong element, so we first query
  342. // which element it thinks has focus and forward the event on to that element if it differs
  343. id focusedElement = UIAccessibilityFocusedElement (UIAccessibilityNotificationVoiceOverIdentifier);
  344. if (focusedElement != nullptr && ! [static_cast<id> (handler->getNativeImplementation()) isEqual: focusedElement])
  345. return [focusedElement accessibilityActivate];
  346. if (handler->hasFocus (false))
  347. return accessibilityPerformPress (self, {});
  348. }
  349. return NO;
  350. });
  351. addMethod (@selector (accessibilityIncrement), accessibilityPerformIncrement);
  352. addMethod (@selector (accessibilityDecrement), accessibilityPerformDecrement);
  353. addMethod (@selector (accessibilityPerformEscape), [] (id self, SEL)
  354. {
  355. if (auto* handler = getHandler (self))
  356. {
  357. if (auto* modal = Component::getCurrentlyModalComponent())
  358. {
  359. if (auto* modalHandler = modal->getAccessibilityHandler())
  360. {
  361. if (modalHandler == handler || modalHandler->isParentOf (handler))
  362. {
  363. modal->exitModalState (0);
  364. return YES;
  365. }
  366. }
  367. }
  368. }
  369. return NO;
  370. });
  371. if (elementType == Type::textElement)
  372. {
  373. addMethod (@selector (deleteBackward), [] (id, SEL) {});
  374. addMethod (@selector (insertText:), [] (id, SEL, NSString*) {});
  375. forEach ([this] (auto signature) { addPassthroughMethodWithSignature (signature); },
  376. SignatureHasText{},
  377. SignatureSetSelectedTextRange{},
  378. SignatureSelectedTextRange{},
  379. SignatureMarkedTextRange{},
  380. SignatureSetMarkedTextSelectedRange{},
  381. SignatureUnmarkText{},
  382. SignatureMarkedTextStyle{},
  383. SignatureSetMarkedTextStyle{},
  384. SignatureBeginningOfDocument{},
  385. SignatureEndOfDocument{},
  386. SignatureTokenizer{},
  387. SignatureBaseWritingDirection{},
  388. SignatureCaretRectForPosition{},
  389. SignatureCharacterRangeByExtending{},
  390. SignatureCharacterRangeAtPoint{},
  391. SignatureClosestPositionToPoint{},
  392. SignatureClosestPositionToPointInRange{},
  393. SignatureComparePositionToPosition{},
  394. SignatureOffsetFromPositionToPosition{},
  395. SignaturePositionFromPositionInDirection{},
  396. SignaturePositionFromPositionOffset{},
  397. SignatureFirstRectForRange{},
  398. SignatureSelectionRectsForRange{},
  399. SignaturePositionWithinRange{},
  400. SignatureReplaceRangeWithText{},
  401. SignatureSetBaseWritingDirection{},
  402. SignatureTextInRange{},
  403. SignatureTextRangeFromPosition{},
  404. SignatureSetInputDelegate{},
  405. SignatureInputDelegate{},
  406. SignatureKeyboardType{},
  407. SignatureAutocapitalizationType{},
  408. SignatureAutocorrectionType{});
  409. addProtocol (@protocol (UITextInput));
  410. }
  411. if (@available (iOS 11.0, *))
  412. {
  413. addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange);
  414. addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange);
  415. addProtocol (@protocol (UIAccessibilityContainerDataTableCell));
  416. }
  417. addIvar<UIAccessibilityElement*> ("container");
  418. registerClass();
  419. }
  420. private:
  421. template <typename Result>
  422. static auto getResult (NSInvocation* invocation, detail::Tag<Result>)
  423. {
  424. Result result{};
  425. [invocation getReturnValue: &result];
  426. return result;
  427. }
  428. static void getResult (NSInvocation*, detail::Tag<void>) {}
  429. template <typename HasSelector, typename Result, typename... Args>
  430. auto makePassthroughCallback (HasSelector, Signature<Result (Args...)>)
  431. {
  432. return [] (id self, SEL, Args... args) -> Result
  433. {
  434. if (auto* input = getPeerTextInput (self))
  435. {
  436. const auto s = detail::makeCompileTimeStr (@encode (Result), @encode (id), @encode (SEL), @encode (Args)...);
  437. const auto signature = [NSMethodSignature signatureWithObjCTypes: s.data()];
  438. const auto invocation = [NSInvocation invocationWithMethodSignature: signature];
  439. invocation.selector = HasSelector::sel();
  440. // Indices 0 and 1 are 'id self' and 'SEL _cmd' respectively
  441. auto counter = 2;
  442. forEach ([&] (auto& arg) { [invocation setArgument: &arg atIndex: counter++]; }, args...);
  443. [invocation invokeWithTarget: input];
  444. return getResult (invocation, detail::Tag<Result>{});
  445. }
  446. jassertfalse;
  447. return {};
  448. };
  449. }
  450. template <typename Signature>
  451. void addPassthroughMethodWithSignature (Signature signature)
  452. {
  453. addMethod (Signature::sel(), makePassthroughCallback (signature, signature));
  454. }
  455. static UIAccessibilityElement* getContainer (id self)
  456. {
  457. return getIvar<UIAccessibilityElement*> (self, "container");
  458. }
  459. static UIViewComponentPeer* getPeer (id self)
  460. {
  461. if (auto* handler = getHandler (self))
  462. return static_cast<UIViewComponentPeer*> (handler->getComponent().getPeer());
  463. return nil;
  464. }
  465. static JuceTextView* getPeerTextInput (id self)
  466. {
  467. if (auto* peer = getPeer (self))
  468. return peer->hiddenTextInput.get();
  469. return nil;
  470. }
  471. //==============================================================================
  472. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityElement)
  473. };
  474. //==============================================================================
  475. AccessibilityElement::Holder accessibilityElement;
  476. //==============================================================================
  477. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
  478. };
  479. //==============================================================================
  480. AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
  481. {
  482. return (AccessibilityNativeHandle*) nativeImpl->getAccessibilityElement();
  483. }
  484. static bool areAnyAccessibilityClientsActive()
  485. {
  486. return UIAccessibilityIsVoiceOverRunning();
  487. }
  488. static void sendAccessibilityEvent (UIAccessibilityNotifications notification, id argument)
  489. {
  490. if (! areAnyAccessibilityClientsActive())
  491. return;
  492. jassert (notification != UIAccessibilityNotifications{});
  493. UIAccessibilityPostNotification (notification, argument);
  494. }
  495. void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
  496. {
  497. auto notification = [eventType]
  498. {
  499. switch (eventType)
  500. {
  501. case InternalAccessibilityEvent::elementCreated:
  502. case InternalAccessibilityEvent::elementDestroyed:
  503. case InternalAccessibilityEvent::elementMovedOrResized:
  504. case InternalAccessibilityEvent::focusChanged: return UIAccessibilityLayoutChangedNotification;
  505. case InternalAccessibilityEvent::windowOpened:
  506. case InternalAccessibilityEvent::windowClosed: return UIAccessibilityScreenChangedNotification;
  507. }
  508. return UIAccessibilityNotifications{};
  509. }();
  510. if (notification != UIAccessibilityNotifications{})
  511. {
  512. const bool moveToHandler = (eventType == InternalAccessibilityEvent::focusChanged && handler.hasFocus (false));
  513. sendAccessibilityEvent (notification,
  514. moveToHandler ? static_cast<id> (handler.getNativeImplementation()) : nil);
  515. }
  516. }
  517. void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
  518. {
  519. auto notification = [eventType]
  520. {
  521. switch (eventType)
  522. {
  523. case AccessibilityEvent::textSelectionChanged:
  524. case AccessibilityEvent::rowSelectionChanged:
  525. case AccessibilityEvent::textChanged:
  526. case AccessibilityEvent::valueChanged:
  527. case AccessibilityEvent::titleChanged: break;
  528. case AccessibilityEvent::structureChanged: return UIAccessibilityLayoutChangedNotification;
  529. }
  530. return UIAccessibilityNotifications{};
  531. }();
  532. if (notification != UIAccessibilityNotifications{})
  533. sendAccessibilityEvent (notification, static_cast<id> (getNativeImplementation()));
  534. }
  535. void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority)
  536. {
  537. sendAccessibilityEvent (UIAccessibilityAnnouncementNotification, juceStringToNS (announcementString));
  538. }
  539. } // namespace juce