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.

678 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 = detail::AccessibilityHelpers::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 = detail::AccessibilityHelpers::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 = detail::AccessibilityHelpers::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. || AccessibilityHandler::getNativeChildForComponent (handler->getComponent()) != nullptr)
  231. {
  232. if (UIAccessibilityElement* container = getContainer (self))
  233. return container;
  234. static AccessibilityContainer cls;
  235. id container = cls.createInstance();
  236. object_setInstanceVariable (container, "handler", handler);
  237. object_setInstanceVariable (self, "container", container);
  238. return container;
  239. }
  240. if (auto* parent = handler->getParent())
  241. return [static_cast<id> (parent->getNativeImplementation()) accessibilityContainer];
  242. }
  243. return nil;
  244. });
  245. addMethod (@selector (accessibilityFrame), [] (id self, SEL)
  246. {
  247. if (auto* handler = getHandler (self))
  248. return convertToCGRect (handler->getComponent().getScreenBounds());
  249. return CGRectZero;
  250. });
  251. addMethod (@selector (accessibilityTraits), [] (id self, SEL)
  252. {
  253. auto traits = UIAccessibilityTraits{};
  254. if (auto* handler = getHandler (self))
  255. {
  256. traits |= [&handler]
  257. {
  258. switch (handler->getRole())
  259. {
  260. case AccessibilityRole::button:
  261. case AccessibilityRole::toggleButton:
  262. case AccessibilityRole::radioButton:
  263. case AccessibilityRole::comboBox: return UIAccessibilityTraitButton;
  264. case AccessibilityRole::label:
  265. case AccessibilityRole::staticText: return UIAccessibilityTraitStaticText;
  266. case AccessibilityRole::image: return UIAccessibilityTraitImage;
  267. case AccessibilityRole::tableHeader: return UIAccessibilityTraitHeader;
  268. case AccessibilityRole::hyperlink: return UIAccessibilityTraitLink;
  269. case AccessibilityRole::ignored: return UIAccessibilityTraitNotEnabled;
  270. case AccessibilityRole::editableText: return UIAccessibilityTraitKeyboardKey;
  271. case AccessibilityRole::slider:
  272. case AccessibilityRole::menuItem:
  273. case AccessibilityRole::menuBar:
  274. case AccessibilityRole::popupMenu:
  275. case AccessibilityRole::table:
  276. case AccessibilityRole::column:
  277. case AccessibilityRole::row:
  278. case AccessibilityRole::cell:
  279. case AccessibilityRole::list:
  280. case AccessibilityRole::listItem:
  281. case AccessibilityRole::tree:
  282. case AccessibilityRole::treeItem:
  283. case AccessibilityRole::progressBar:
  284. case AccessibilityRole::group:
  285. case AccessibilityRole::dialogWindow:
  286. case AccessibilityRole::window:
  287. case AccessibilityRole::scrollBar:
  288. case AccessibilityRole::tooltip:
  289. case AccessibilityRole::splashScreen:
  290. case AccessibilityRole::unspecified: break;
  291. }
  292. return UIAccessibilityTraitNone;
  293. }();
  294. const auto state = handler->getCurrentState();
  295. if (state.isSelected() || state.isChecked())
  296. traits |= UIAccessibilityTraitSelected;
  297. if (auto* valueInterface = getValueInterface (self))
  298. if (! valueInterface->isReadOnly() && valueInterface->getRange().isValid())
  299. traits |= UIAccessibilityTraitAdjustable;
  300. }
  301. return traits | sendSuperclassMessage<UIAccessibilityTraits> (self, @selector (accessibilityTraits));
  302. });
  303. addMethod (@selector (accessibilityLabel), getAccessibilityTitle);
  304. addMethod (@selector (accessibilityHint), getAccessibilityHelp);
  305. addMethod (@selector (accessibilityValue), [] (id self, SEL) -> NSString*
  306. {
  307. if (auto* handler = getHandler (self))
  308. {
  309. if (handler->getCurrentState().isCheckable())
  310. return handler->getCurrentState().isChecked() ? @"1" : @"0";
  311. return (NSString*) getAccessibilityValueFromInterfaces (*handler);
  312. }
  313. return nil;
  314. });
  315. addMethod (@selector (setAccessibilityValue:), setAccessibilityValue);
  316. addMethod (@selector (accessibilityElementDidBecomeFocused), [] (id self, SEL)
  317. {
  318. if (auto* handler = getHandler (self))
  319. {
  320. const WeakReference<Component> safeComponent (&handler->getComponent());
  321. performActionIfSupported (self, AccessibilityActionType::focus);
  322. if (safeComponent != nullptr)
  323. handler->grabFocus();
  324. }
  325. });
  326. addMethod (@selector (accessibilityElementDidLoseFocus), [] (id self, SEL)
  327. {
  328. if (auto* handler = getHandler (self))
  329. handler->giveAwayFocus();
  330. });
  331. addMethod (@selector (accessibilityElementIsFocused), [] (id self, SEL) -> BOOL
  332. {
  333. if (auto* handler = getHandler (self))
  334. return handler->hasFocus (false);
  335. return NO;
  336. });
  337. addMethod (@selector (accessibilityViewIsModal), getIsAccessibilityModal);
  338. addMethod (@selector (accessibilityActivate), [] (id self, SEL)
  339. {
  340. if (auto* handler = getHandler (self))
  341. {
  342. // Occasionally VoiceOver sends accessibilityActivate to the wrong element, so we first query
  343. // which element it thinks has focus and forward the event on to that element if it differs
  344. id focusedElement = UIAccessibilityFocusedElement (UIAccessibilityNotificationVoiceOverIdentifier);
  345. if (focusedElement != nullptr && ! [static_cast<id> (handler->getNativeImplementation()) isEqual: focusedElement])
  346. return [focusedElement accessibilityActivate];
  347. if (handler->hasFocus (false))
  348. return accessibilityPerformPress (self, {});
  349. }
  350. return NO;
  351. });
  352. addMethod (@selector (accessibilityIncrement), accessibilityPerformIncrement);
  353. addMethod (@selector (accessibilityDecrement), accessibilityPerformDecrement);
  354. addMethod (@selector (accessibilityPerformEscape), [] (id self, SEL)
  355. {
  356. if (auto* handler = getHandler (self))
  357. {
  358. if (auto* modal = Component::getCurrentlyModalComponent())
  359. {
  360. if (auto* modalHandler = modal->getAccessibilityHandler())
  361. {
  362. if (modalHandler == handler || modalHandler->isParentOf (handler))
  363. {
  364. modal->exitModalState (0);
  365. return YES;
  366. }
  367. }
  368. }
  369. }
  370. return NO;
  371. });
  372. if (elementType == Type::textElement)
  373. {
  374. addMethod (@selector (deleteBackward), [] (id, SEL) {});
  375. addMethod (@selector (insertText:), [] (id, SEL, NSString*) {});
  376. forEach ([this] (auto signature) { addPassthroughMethodWithSignature (signature); },
  377. SignatureHasText{},
  378. SignatureSetSelectedTextRange{},
  379. SignatureSelectedTextRange{},
  380. SignatureMarkedTextRange{},
  381. SignatureSetMarkedTextSelectedRange{},
  382. SignatureUnmarkText{},
  383. SignatureMarkedTextStyle{},
  384. SignatureSetMarkedTextStyle{},
  385. SignatureBeginningOfDocument{},
  386. SignatureEndOfDocument{},
  387. SignatureTokenizer{},
  388. SignatureBaseWritingDirection{},
  389. SignatureCaretRectForPosition{},
  390. SignatureCharacterRangeByExtending{},
  391. SignatureCharacterRangeAtPoint{},
  392. SignatureClosestPositionToPoint{},
  393. SignatureClosestPositionToPointInRange{},
  394. SignatureComparePositionToPosition{},
  395. SignatureOffsetFromPositionToPosition{},
  396. SignaturePositionFromPositionInDirection{},
  397. SignaturePositionFromPositionOffset{},
  398. SignatureFirstRectForRange{},
  399. SignatureSelectionRectsForRange{},
  400. SignaturePositionWithinRange{},
  401. SignatureReplaceRangeWithText{},
  402. SignatureSetBaseWritingDirection{},
  403. SignatureTextInRange{},
  404. SignatureTextRangeFromPosition{},
  405. SignatureSetInputDelegate{},
  406. SignatureInputDelegate{},
  407. SignatureKeyboardType{},
  408. SignatureAutocapitalizationType{},
  409. SignatureAutocorrectionType{});
  410. addProtocol (@protocol (UITextInput));
  411. }
  412. if (@available (iOS 11.0, *))
  413. {
  414. addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange);
  415. addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange);
  416. addProtocol (@protocol (UIAccessibilityContainerDataTableCell));
  417. }
  418. addIvar<UIAccessibilityElement*> ("container");
  419. registerClass();
  420. }
  421. private:
  422. template <typename Result>
  423. static auto getResult (NSInvocation* invocation, detail::Tag<Result>)
  424. {
  425. Result result{};
  426. [invocation getReturnValue: &result];
  427. return result;
  428. }
  429. static void getResult (NSInvocation*, detail::Tag<void>) {}
  430. template <typename HasSelector, typename Result, typename... Args>
  431. auto makePassthroughCallback (HasSelector, Signature<Result (Args...)>)
  432. {
  433. return [] (id self, SEL, Args... args) -> Result
  434. {
  435. if (auto* input = getPeerTextInput (self))
  436. {
  437. const auto s = detail::makeCompileTimeStr (@encode (Result), @encode (id), @encode (SEL), @encode (Args)...);
  438. const auto signature = [NSMethodSignature signatureWithObjCTypes: s.data()];
  439. if (signature == nullptr)
  440. {
  441. jassertfalse;
  442. return {};
  443. }
  444. JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion")
  445. const auto invocation = [NSInvocation invocationWithMethodSignature: signature];
  446. JUCE_END_IGNORE_WARNINGS_GCC_LIKE
  447. invocation.selector = HasSelector::sel();
  448. // Indices 0 and 1 are 'id self' and 'SEL _cmd' respectively
  449. auto counter = 2;
  450. forEach ([&] (auto& arg) { [invocation setArgument: &arg atIndex: counter++]; }, args...);
  451. [invocation invokeWithTarget: input];
  452. return getResult (invocation, detail::Tag<Result>{});
  453. }
  454. jassertfalse;
  455. return {};
  456. };
  457. }
  458. template <typename Signature>
  459. void addPassthroughMethodWithSignature (Signature signature)
  460. {
  461. addMethod (Signature::sel(), makePassthroughCallback (signature, signature));
  462. }
  463. static UIAccessibilityElement* getContainer (id self)
  464. {
  465. return getIvar<UIAccessibilityElement*> (self, "container");
  466. }
  467. static UIViewComponentPeer* getPeer (id self)
  468. {
  469. if (auto* handler = getHandler (self))
  470. return static_cast<UIViewComponentPeer*> (handler->getComponent().getPeer());
  471. return nil;
  472. }
  473. static JuceTextView* getPeerTextInput (id self)
  474. {
  475. if (auto* peer = getPeer (self))
  476. return peer->hiddenTextInput.get();
  477. return nil;
  478. }
  479. //==============================================================================
  480. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityElement)
  481. };
  482. //==============================================================================
  483. AccessibilityElement::Holder accessibilityElement;
  484. //==============================================================================
  485. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
  486. };
  487. //==============================================================================
  488. AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
  489. {
  490. return (AccessibilityNativeHandle*) nativeImpl->getAccessibilityElement();
  491. }
  492. static bool areAnyAccessibilityClientsActive()
  493. {
  494. return UIAccessibilityIsVoiceOverRunning();
  495. }
  496. static void sendAccessibilityEvent (UIAccessibilityNotifications notification, id argument)
  497. {
  498. if (! areAnyAccessibilityClientsActive())
  499. return;
  500. jassert (notification != UIAccessibilityNotifications{});
  501. UIAccessibilityPostNotification (notification, argument);
  502. }
  503. void detail::AccessibilityHelpers::notifyAccessibilityEvent (const AccessibilityHandler& handler, Event eventType)
  504. {
  505. auto notification = [eventType]
  506. {
  507. switch (eventType)
  508. {
  509. case Event::elementCreated:
  510. case Event::elementDestroyed:
  511. case Event::elementMovedOrResized:
  512. case Event::focusChanged: return UIAccessibilityLayoutChangedNotification;
  513. case Event::windowOpened:
  514. case Event::windowClosed: return UIAccessibilityScreenChangedNotification;
  515. }
  516. return UIAccessibilityNotifications{};
  517. }();
  518. if (notification != UIAccessibilityNotifications{})
  519. {
  520. const bool moveToHandler = (eventType == Event::focusChanged && handler.hasFocus (false));
  521. sendAccessibilityEvent (notification,
  522. moveToHandler ? static_cast<id> (handler.getNativeImplementation()) : nil);
  523. }
  524. }
  525. void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
  526. {
  527. auto notification = [eventType]
  528. {
  529. switch (eventType)
  530. {
  531. case AccessibilityEvent::textSelectionChanged:
  532. case AccessibilityEvent::rowSelectionChanged:
  533. case AccessibilityEvent::textChanged:
  534. case AccessibilityEvent::valueChanged:
  535. case AccessibilityEvent::titleChanged: break;
  536. case AccessibilityEvent::structureChanged: return UIAccessibilityLayoutChangedNotification;
  537. }
  538. return UIAccessibilityNotifications{};
  539. }();
  540. if (notification != UIAccessibilityNotifications{})
  541. sendAccessibilityEvent (notification, static_cast<id> (getNativeImplementation()));
  542. }
  543. void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority)
  544. {
  545. sendAccessibilityEvent (UIAccessibilityAnnouncementNotification, juceStringToNS (announcementString));
  546. }
  547. } // namespace juce