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.

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