VST3: fetch midi mappings all at once, use it for note/sound-off
[carla.git] / source / modules / juce_gui_basics / native / accessibility / juce_win32_AccessibilityElement.cpp
blobde17aae67d3b94f836dd2e07f70a3c399653b454
1 /*
2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
8 licensing.
10 By using JUCE, you agree to the terms of both the JUCE 7 End-User License
11 Agreement and JUCE Privacy Policy.
13 End User License Agreement: www.juce.com/juce-7-licence
14 Privacy Policy: www.juce.com/juce-privacy-policy
16 Or: You may also use this code under the terms of the GPL v3 (see
17 www.gnu.org/licenses).
19 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
20 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
21 DISCLAIMED.
23 ==============================================================================
26 namespace juce
29 JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
31 int AccessibilityNativeHandle::idCounter = 0;
33 //==============================================================================
34 static String getAutomationId (const AccessibilityHandler& handler)
36 auto result = handler.getTitle();
37 auto* parentComponent = handler.getComponent().getParentComponent();
39 while (parentComponent != nullptr)
41 if (auto* parentHandler = parentComponent->getAccessibilityHandler())
43 auto parentTitle = parentHandler->getTitle();
44 result << "." << (parentTitle.isNotEmpty() ? parentTitle : "<empty>");
47 parentComponent = parentComponent->getParentComponent();
50 return result;
53 static auto roleToControlTypeId (AccessibilityRole roleType)
55 switch (roleType)
57 case AccessibilityRole::popupMenu:
58 case AccessibilityRole::dialogWindow:
59 case AccessibilityRole::splashScreen:
60 case AccessibilityRole::window: return ComTypes::UIA_WindowControlTypeId;
62 case AccessibilityRole::label:
63 case AccessibilityRole::staticText: return ComTypes::UIA_TextControlTypeId;
65 case AccessibilityRole::column:
66 case AccessibilityRole::row: return ComTypes::UIA_HeaderItemControlTypeId;
68 case AccessibilityRole::button: return ComTypes::UIA_ButtonControlTypeId;
69 case AccessibilityRole::toggleButton: return ComTypes::UIA_CheckBoxControlTypeId;
70 case AccessibilityRole::radioButton: return ComTypes::UIA_RadioButtonControlTypeId;
71 case AccessibilityRole::comboBox: return ComTypes::UIA_ComboBoxControlTypeId;
72 case AccessibilityRole::image: return ComTypes::UIA_ImageControlTypeId;
73 case AccessibilityRole::slider: return ComTypes::UIA_SliderControlTypeId;
74 case AccessibilityRole::editableText: return ComTypes::UIA_EditControlTypeId;
75 case AccessibilityRole::menuItem: return ComTypes::UIA_MenuItemControlTypeId;
76 case AccessibilityRole::menuBar: return ComTypes::UIA_MenuBarControlTypeId;
77 case AccessibilityRole::table: return ComTypes::UIA_TableControlTypeId;
78 case AccessibilityRole::tableHeader: return ComTypes::UIA_HeaderControlTypeId;
79 case AccessibilityRole::cell: return ComTypes::UIA_DataItemControlTypeId;
80 case AccessibilityRole::hyperlink: return ComTypes::UIA_HyperlinkControlTypeId;
81 case AccessibilityRole::list: return ComTypes::UIA_ListControlTypeId;
82 case AccessibilityRole::listItem: return ComTypes::UIA_ListItemControlTypeId;
83 case AccessibilityRole::tree: return ComTypes::UIA_TreeControlTypeId;
84 case AccessibilityRole::treeItem: return ComTypes::UIA_TreeItemControlTypeId;
85 case AccessibilityRole::progressBar: return ComTypes::UIA_ProgressBarControlTypeId;
86 case AccessibilityRole::group: return ComTypes::UIA_GroupControlTypeId;
87 case AccessibilityRole::scrollBar: return ComTypes::UIA_ScrollBarControlTypeId;
88 case AccessibilityRole::tooltip: return ComTypes::UIA_ToolTipControlTypeId;
90 case AccessibilityRole::ignored:
91 case AccessibilityRole::unspecified: break;
94 return ComTypes::UIA_CustomControlTypeId;
97 //==============================================================================
98 AccessibilityNativeHandle::AccessibilityNativeHandle (AccessibilityHandler& handler)
99 : ComBaseClassHelper (0),
100 accessibilityHandler (handler)
104 //==============================================================================
105 JUCE_COMRESULT AccessibilityNativeHandle::QueryInterface (REFIID refId, void** result)
107 *result = nullptr;
109 if (! isElementValid())
110 return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
112 if ((refId == __uuidof (ComTypes::IRawElementProviderFragmentRoot) && ! isFragmentRoot()))
113 return E_NOINTERFACE;
115 return ComBaseClassHelper::QueryInterface (refId, result);
118 //==============================================================================
119 JUCE_COMRESULT AccessibilityNativeHandle::get_HostRawElementProvider (IRawElementProviderSimple** pRetVal)
121 return withCheckedComArgs (pRetVal, *this, [&]
123 if (isFragmentRoot())
124 if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
125 return wrapper->hostProviderFromHwnd ((HWND) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
127 return S_OK;
131 JUCE_COMRESULT AccessibilityNativeHandle::get_ProviderOptions (ProviderOptions* options)
133 if (options == nullptr)
134 return E_INVALIDARG;
136 *options = (ProviderOptions) (ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading);
137 return S_OK;
140 JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUnknown** pRetVal)
142 return withCheckedComArgs (pRetVal, *this, [&]
144 *pRetVal = [&]() -> IUnknown*
146 const auto role = accessibilityHandler.getRole();
147 const auto fragmentRoot = isFragmentRoot();
149 switch (pId)
151 case ComTypes::UIA_WindowPatternId:
153 if (fragmentRoot)
154 return new UIAWindowProvider (this);
156 break;
158 case ComTypes::UIA_TransformPatternId:
160 if (fragmentRoot)
161 return new UIATransformProvider (this);
163 break;
165 case ComTypes::UIA_TextPatternId:
166 case ComTypes::UIA_TextPattern2Id:
168 if (accessibilityHandler.getTextInterface() != nullptr)
169 return new UIATextProvider (this);
171 break;
173 case ComTypes::UIA_ValuePatternId:
175 if (accessibilityHandler.getValueInterface() != nullptr)
176 return new UIAValueProvider (this);
178 break;
180 case ComTypes::UIA_RangeValuePatternId:
182 if (accessibilityHandler.getValueInterface() != nullptr
183 && accessibilityHandler.getValueInterface()->getRange().isValid())
185 return new UIARangeValueProvider (this);
188 break;
190 case ComTypes::UIA_TogglePatternId:
192 if (accessibilityHandler.getCurrentState().isCheckable()
193 && (accessibilityHandler.getActions().contains (AccessibilityActionType::toggle)
194 || accessibilityHandler.getActions().contains (AccessibilityActionType::press)))
196 return new UIAToggleProvider (this);
199 break;
201 case ComTypes::UIA_SelectionPatternId:
203 if (role == AccessibilityRole::list
204 || role == AccessibilityRole::popupMenu
205 || role == AccessibilityRole::tree)
207 return new UIASelectionProvider (this);
210 break;
212 case ComTypes::UIA_SelectionItemPatternId:
214 auto state = accessibilityHandler.getCurrentState();
216 if (state.isSelectable() || state.isMultiSelectable()
217 || role == AccessibilityRole::radioButton)
219 return new UIASelectionItemProvider (this);
222 break;
224 case ComTypes::UIA_GridPatternId:
226 if (accessibilityHandler.getTableInterface() != nullptr)
227 return new UIAGridProvider (this);
229 break;
231 case ComTypes::UIA_GridItemPatternId:
233 if (accessibilityHandler.getCellInterface() != nullptr)
234 return new UIAGridItemProvider (this);
236 break;
238 case ComTypes::UIA_InvokePatternId:
240 if (accessibilityHandler.getActions().contains (AccessibilityActionType::press))
241 return new UIAInvokeProvider (this);
243 break;
245 case ComTypes::UIA_ExpandCollapsePatternId:
247 if (accessibilityHandler.getActions().contains (AccessibilityActionType::showMenu)
248 && accessibilityHandler.getCurrentState().isExpandable())
249 return new UIAExpandCollapseProvider (this);
251 break;
255 return nullptr;
256 }();
258 return S_OK;
262 JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyId, VARIANT* pRetVal)
264 return withCheckedComArgs (pRetVal, *this, [&]
266 VariantHelpers::clear (pRetVal);
268 const auto role = accessibilityHandler.getRole();
269 const auto state = accessibilityHandler.getCurrentState();
270 const auto ignored = accessibilityHandler.isIgnored();
272 switch (propertyId)
274 case UIA_AutomationIdPropertyId:
275 VariantHelpers::setString (getAutomationId (accessibilityHandler), pRetVal);
276 break;
277 case UIA_ControlTypePropertyId:
278 VariantHelpers::setInt (roleToControlTypeId (role), pRetVal);
279 break;
280 case UIA_FrameworkIdPropertyId:
281 VariantHelpers::setString ("JUCE", pRetVal);
282 break;
283 case UIA_FullDescriptionPropertyId:
284 VariantHelpers::setString (accessibilityHandler.getDescription(), pRetVal);
285 break;
286 case UIA_HelpTextPropertyId:
287 VariantHelpers::setString (accessibilityHandler.getHelp(), pRetVal);
288 break;
289 case UIA_IsContentElementPropertyId:
290 VariantHelpers::setBool (! ignored && accessibilityHandler.isVisibleWithinParent(),
291 pRetVal);
292 break;
293 case UIA_IsControlElementPropertyId:
294 VariantHelpers::setBool (true, pRetVal);
295 break;
296 case UIA_IsDialogPropertyId:
297 VariantHelpers::setBool (role == AccessibilityRole::dialogWindow, pRetVal);
298 break;
299 case UIA_IsEnabledPropertyId:
300 VariantHelpers::setBool (accessibilityHandler.getComponent().isEnabled(), pRetVal);
301 break;
302 case UIA_IsKeyboardFocusablePropertyId:
303 VariantHelpers::setBool (state.isFocusable(), pRetVal);
304 break;
305 case UIA_HasKeyboardFocusPropertyId:
306 VariantHelpers::setBool (accessibilityHandler.hasFocus (true), pRetVal);
307 break;
308 case UIA_IsOffscreenPropertyId:
309 VariantHelpers::setBool (! accessibilityHandler.isVisibleWithinParent(), pRetVal);
310 break;
311 case UIA_IsPasswordPropertyId:
312 if (auto* textInterface = accessibilityHandler.getTextInterface())
313 VariantHelpers::setBool (textInterface->isDisplayingProtectedText(), pRetVal);
315 break;
316 case ComTypes::UIA_IsPeripheralPropertyId:
317 VariantHelpers::setBool (role == AccessibilityRole::tooltip
318 || role == AccessibilityRole::popupMenu
319 || role == AccessibilityRole::splashScreen,
320 pRetVal);
321 break;
322 case UIA_NamePropertyId:
323 if (! ignored)
324 VariantHelpers::setString (getElementName(), pRetVal);
326 break;
327 case UIA_ProcessIdPropertyId:
328 VariantHelpers::setInt ((int) GetCurrentProcessId(), pRetVal);
329 break;
330 case UIA_NativeWindowHandlePropertyId:
331 if (isFragmentRoot())
332 VariantHelpers::setInt ((int) (pointer_sized_int) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
334 break;
337 return S_OK;
341 //==============================================================================
342 JUCE_COMRESULT AccessibilityNativeHandle::Navigate (ComTypes::NavigateDirection direction, ComTypes::IRawElementProviderFragment** pRetVal)
344 return withCheckedComArgs (pRetVal, *this, [&]
346 auto* handler = [&]() -> AccessibilityHandler*
348 if (direction == ComTypes::NavigateDirection_Parent)
349 return accessibilityHandler.getParent();
351 if (direction == ComTypes::NavigateDirection_FirstChild
352 || direction == ComTypes::NavigateDirection_LastChild)
354 auto children = accessibilityHandler.getChildren();
356 return children.empty() ? nullptr
357 : (direction == ComTypes::NavigateDirection_FirstChild ? children.front()
358 : children.back());
361 if (direction == ComTypes::NavigateDirection_NextSibling
362 || direction == ComTypes::NavigateDirection_PreviousSibling)
364 if (auto* parent = accessibilityHandler.getParent())
366 const auto siblings = parent->getChildren();
367 const auto iter = std::find (siblings.cbegin(), siblings.cend(), &accessibilityHandler);
369 if (iter == siblings.end())
370 return nullptr;
372 if (direction == ComTypes::NavigateDirection_NextSibling && iter != std::prev (siblings.cend()))
373 return *std::next (iter);
375 if (direction == ComTypes::NavigateDirection_PreviousSibling && iter != siblings.cbegin())
376 return *std::prev (iter);
380 return nullptr;
381 }();
383 if (handler != nullptr)
384 if (auto* provider = handler->getNativeImplementation())
385 if (provider->isElementValid())
386 provider->QueryInterface (IID_PPV_ARGS (pRetVal));
388 return S_OK;
392 JUCE_COMRESULT AccessibilityNativeHandle::GetRuntimeId (SAFEARRAY** pRetVal)
394 return withCheckedComArgs (pRetVal, *this, [&]
396 if (! isFragmentRoot())
398 *pRetVal = SafeArrayCreateVector (VT_I4, 0, 2);
400 if (*pRetVal == nullptr)
401 return E_OUTOFMEMORY;
403 for (LONG i = 0; i < 2; ++i)
405 auto hr = SafeArrayPutElement (*pRetVal, &i, &rtid[(size_t) i]);
407 if (FAILED (hr))
408 return E_FAIL;
412 return S_OK;
416 JUCE_COMRESULT AccessibilityNativeHandle::get_BoundingRectangle (ComTypes::UiaRect* pRetVal)
418 return withCheckedComArgs (pRetVal, *this, [&]
420 auto bounds = Desktop::getInstance().getDisplays()
421 .logicalToPhysical (accessibilityHandler.getComponent().getScreenBounds());
423 pRetVal->left = bounds.getX();
424 pRetVal->top = bounds.getY();
425 pRetVal->width = bounds.getWidth();
426 pRetVal->height = bounds.getHeight();
428 return S_OK;
432 JUCE_COMRESULT AccessibilityNativeHandle::GetEmbeddedFragmentRoots (SAFEARRAY** pRetVal)
434 return withCheckedComArgs (pRetVal, *this, []
436 return S_OK;
440 JUCE_COMRESULT AccessibilityNativeHandle::SetFocus()
442 if (! isElementValid())
443 return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
445 const WeakReference<Component> safeComponent (&accessibilityHandler.getComponent());
447 accessibilityHandler.getActions().invoke (AccessibilityActionType::focus);
449 if (safeComponent != nullptr)
450 accessibilityHandler.grabFocus();
452 return S_OK;
455 JUCE_COMRESULT AccessibilityNativeHandle::get_FragmentRoot (ComTypes::IRawElementProviderFragmentRoot** pRetVal)
457 return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
459 auto* handler = [&]() -> AccessibilityHandler*
461 if (isFragmentRoot())
462 return &accessibilityHandler;
464 if (auto* peer = accessibilityHandler.getComponent().getPeer())
465 return peer->getComponent().getAccessibilityHandler();
467 return nullptr;
468 }();
470 if (handler != nullptr)
472 handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
473 return S_OK;
476 return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
480 //==============================================================================
481 JUCE_COMRESULT AccessibilityNativeHandle::ElementProviderFromPoint (double x, double y, ComTypes::IRawElementProviderFragment** pRetVal)
483 return withCheckedComArgs (pRetVal, *this, [&]
485 auto* handler = [&]
487 auto logicalScreenPoint = Desktop::getInstance().getDisplays()
488 .physicalToLogical (Point<int> (roundToInt (x),
489 roundToInt (y)));
491 if (auto* child = accessibilityHandler.getChildAt (logicalScreenPoint))
492 return child;
494 return &accessibilityHandler;
495 }();
497 handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
499 return S_OK;
503 JUCE_COMRESULT AccessibilityNativeHandle::GetFocus (ComTypes::IRawElementProviderFragment** pRetVal)
505 return withCheckedComArgs (pRetVal, *this, [&]
507 const auto getFocusHandler = [this]() -> AccessibilityHandler*
509 if (auto* modal = Component::getCurrentlyModalComponent())
511 const auto& component = accessibilityHandler.getComponent();
513 if (! component.isParentOf (modal)
514 && component.isCurrentlyBlockedByAnotherModalComponent())
516 if (auto* modalHandler = modal->getAccessibilityHandler())
518 if (auto* focusChild = modalHandler->getChildFocus())
519 return focusChild;
521 return modalHandler;
526 if (auto* focusChild = accessibilityHandler.getChildFocus())
527 return focusChild;
529 return nullptr;
532 if (auto* focusHandler = getFocusHandler())
533 focusHandler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
535 return S_OK;
539 //==============================================================================
540 String AccessibilityNativeHandle::getElementName() const
542 if (accessibilityHandler.getRole() == AccessibilityRole::tooltip)
543 return accessibilityHandler.getDescription();
545 auto name = accessibilityHandler.getTitle();
547 if (name.isEmpty() && isFragmentRoot())
548 return getAccessibleApplicationOrPluginName();
550 return name;
553 JUCE_END_IGNORE_WARNINGS_GCC_LIKE
555 } // namespace juce