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.

275 lines
9.2KB

  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. namespace juce
  19. {
  20. //==============================================================================
  21. struct AccessibleObjCClassDeleter
  22. {
  23. template <typename ElementType>
  24. void operator() (ElementType* element) const
  25. {
  26. juceFreeAccessibilityPlatformSpecificData (element);
  27. object_setInstanceVariable (element, "handler", nullptr);
  28. [element release];
  29. }
  30. };
  31. template <typename Base>
  32. class AccessibleObjCClass : public ObjCClass<Base>
  33. {
  34. public:
  35. using Holder = std::unique_ptr<Base, AccessibleObjCClassDeleter>;
  36. protected:
  37. AccessibleObjCClass() : AccessibleObjCClass ("JUCEAccessibilityElement_") {}
  38. explicit AccessibleObjCClass (const char* name) : ObjCClass<Base> (name)
  39. {
  40. ObjCClass<Base>::template addIvar<AccessibilityHandler*> ("handler");
  41. }
  42. //==============================================================================
  43. static AccessibilityHandler* getHandler (id self)
  44. {
  45. return getIvar<AccessibilityHandler*> (self, "handler");
  46. }
  47. template <typename MemberFn>
  48. static auto getInterface (id self, MemberFn fn) noexcept -> decltype ((std::declval<AccessibilityHandler>().*fn)())
  49. {
  50. if (auto* handler = getHandler (self))
  51. return (handler->*fn)();
  52. return nullptr;
  53. }
  54. static AccessibilityTextInterface* getTextInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getTextInterface); }
  55. static AccessibilityValueInterface* getValueInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getValueInterface); }
  56. static AccessibilityTableInterface* getTableInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getTableInterface); }
  57. static AccessibilityCellInterface* getCellInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getCellInterface); }
  58. static bool hasEditableText (AccessibilityHandler& handler) noexcept
  59. {
  60. return handler.getRole() == AccessibilityRole::editableText
  61. && handler.getTextInterface() != nullptr
  62. && ! handler.getTextInterface()->isReadOnly();
  63. }
  64. static id getAccessibilityValueFromInterfaces (const AccessibilityHandler& handler)
  65. {
  66. if (auto* textInterface = handler.getTextInterface())
  67. return juceStringToNS (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() }));
  68. if (auto* valueInterface = handler.getValueInterface())
  69. return juceStringToNS (valueInterface->getCurrentValueAsString());
  70. return nil;
  71. }
  72. //==============================================================================
  73. static BOOL getIsAccessibilityElement (id self, SEL)
  74. {
  75. if (auto* handler = getHandler (self))
  76. return ! handler->isIgnored() && handler->getRole() != AccessibilityRole::window;
  77. return NO;
  78. }
  79. static void setAccessibilityValue (id self, SEL, NSString* value)
  80. {
  81. if (auto* handler = getHandler (self))
  82. {
  83. if (hasEditableText (*handler))
  84. {
  85. handler->getTextInterface()->setText (nsStringToJuce (value));
  86. return;
  87. }
  88. if (auto* valueInterface = handler->getValueInterface())
  89. if (! valueInterface->isReadOnly())
  90. valueInterface->setValueAsString (nsStringToJuce (value));
  91. }
  92. }
  93. static BOOL performActionIfSupported (id self, AccessibilityActionType actionType)
  94. {
  95. if (auto* handler = getHandler (self))
  96. if (handler->getActions().invoke (actionType))
  97. return YES;
  98. return NO;
  99. }
  100. static BOOL accessibilityPerformPress (id self, SEL)
  101. {
  102. if (auto* handler = getHandler (self))
  103. if (handler->getCurrentState().isCheckable() && handler->getActions().invoke (AccessibilityActionType::toggle))
  104. return YES;
  105. return performActionIfSupported (self, AccessibilityActionType::press);
  106. }
  107. static BOOL accessibilityPerformIncrement (id self, SEL)
  108. {
  109. if (auto* valueInterface = getValueInterface (self))
  110. {
  111. if (! valueInterface->isReadOnly())
  112. {
  113. auto range = valueInterface->getRange();
  114. if (range.isValid())
  115. {
  116. valueInterface->setValue (jlimit (range.getMinimumValue(),
  117. range.getMaximumValue(),
  118. valueInterface->getCurrentValue() + range.getInterval()));
  119. return YES;
  120. }
  121. }
  122. }
  123. return NO;
  124. }
  125. static BOOL accessibilityPerformDecrement (id self, SEL)
  126. {
  127. if (auto* valueInterface = getValueInterface (self))
  128. {
  129. if (! valueInterface->isReadOnly())
  130. {
  131. auto range = valueInterface->getRange();
  132. if (range.isValid())
  133. {
  134. valueInterface->setValue (jlimit (range.getMinimumValue(),
  135. range.getMaximumValue(),
  136. valueInterface->getCurrentValue() - range.getInterval()));
  137. return YES;
  138. }
  139. }
  140. }
  141. return NO;
  142. }
  143. static NSString* getAccessibilityTitle (id self, SEL)
  144. {
  145. if (auto* handler = getHandler (self))
  146. {
  147. auto title = handler->getTitle();
  148. if (title.isEmpty() && handler->getComponent().isOnDesktop())
  149. title = getAccessibleApplicationOrPluginName();
  150. NSString* nsString = juceStringToNS (title);
  151. if (nsString != nil && [[self accessibilityValue] isEqual: nsString])
  152. return @"";
  153. return nsString;
  154. }
  155. return nil;
  156. }
  157. static NSString* getAccessibilityHelp (id self, SEL)
  158. {
  159. if (auto* handler = getHandler (self))
  160. return juceStringToNS (handler->getHelp());
  161. return nil;
  162. }
  163. static BOOL getIsAccessibilityModal (id self, SEL)
  164. {
  165. if (auto* handler = getHandler (self))
  166. return handler->getComponent().isCurrentlyModal();
  167. return NO;
  168. }
  169. static NSInteger getAccessibilityRowCount (id self, SEL)
  170. {
  171. if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
  172. if (auto* tableInterface = tableHandler->getTableInterface())
  173. return tableInterface->getNumRows();
  174. return 0;
  175. }
  176. static NSInteger getAccessibilityColumnCount (id self, SEL)
  177. {
  178. if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
  179. if (auto* tableInterface = tableHandler->getTableInterface())
  180. return tableInterface->getNumColumns();
  181. return 0;
  182. }
  183. template <typename Getter>
  184. static NSRange getCellDimensions (id self, Getter getter)
  185. {
  186. const auto notFound = NSMakeRange (NSNotFound, 0);
  187. auto* handler = getHandler (self);
  188. if (handler == nullptr)
  189. return notFound;
  190. auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface);
  191. if (tableHandler == nullptr)
  192. return notFound;
  193. auto* tableInterface = tableHandler->getTableInterface();
  194. if (tableInterface == nullptr)
  195. return notFound;
  196. const auto result = (tableInterface->*getter) (*handler);
  197. if (! result.hasValue())
  198. return notFound;
  199. return NSMakeRange ((NSUInteger) result->begin, (NSUInteger) result->num);
  200. }
  201. static NSRange getAccessibilityRowIndexRange (id self, SEL)
  202. {
  203. return getCellDimensions (self, &AccessibilityTableInterface::getRowSpan);
  204. }
  205. static NSRange getAccessibilityColumnIndexRange (id self, SEL)
  206. {
  207. return getCellDimensions (self, &AccessibilityTableInterface::getColumnSpan);
  208. }
  209. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibleObjCClass)
  210. };
  211. }