|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2020 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 6 End-User License
- Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
-
- End User License Agreement: www.juce.com/juce-6-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace juce
- {
-
- JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
-
- int AccessibilityNativeHandle::idCounter = 0;
-
- //==============================================================================
- static String getAutomationId (const AccessibilityHandler& handler)
- {
- auto result = handler.getTitle();
- auto* parentComponent = handler.getComponent().getParentComponent();
-
- while (parentComponent != nullptr)
- {
- if (auto* parentHandler = parentComponent->getAccessibilityHandler())
- {
- auto parentTitle = parentHandler->getTitle();
- result << "." << (parentTitle.isNotEmpty() ? parentTitle : "<empty>");
- }
-
- parentComponent = parentComponent->getParentComponent();
- }
-
- return result;
- }
-
- static long roleToControlTypeId (AccessibilityRole roleType)
- {
- switch (roleType)
- {
- case AccessibilityRole::button: return UIA_ButtonControlTypeId;
- case AccessibilityRole::toggleButton: return UIA_CheckBoxControlTypeId;
- case AccessibilityRole::radioButton: return UIA_RadioButtonControlTypeId;
- case AccessibilityRole::comboBox: return UIA_ComboBoxControlTypeId;
- case AccessibilityRole::image: return UIA_ImageControlTypeId;
- case AccessibilityRole::slider: return UIA_SliderControlTypeId;
- case AccessibilityRole::label: return UIA_TextControlTypeId;
- case AccessibilityRole::staticText: return UIA_TextControlTypeId;
- case AccessibilityRole::editableText: return UIA_EditControlTypeId;
- case AccessibilityRole::menuItem: return UIA_MenuItemControlTypeId;
- case AccessibilityRole::menuBar: return UIA_MenuBarControlTypeId;
- case AccessibilityRole::popupMenu: return UIA_WindowControlTypeId;
- case AccessibilityRole::table: return UIA_TableControlTypeId;
- case AccessibilityRole::tableHeader: return UIA_HeaderControlTypeId;
- case AccessibilityRole::column: return UIA_HeaderItemControlTypeId;
- case AccessibilityRole::row: return UIA_HeaderItemControlTypeId;
- case AccessibilityRole::cell: return UIA_DataItemControlTypeId;
- case AccessibilityRole::hyperlink: return UIA_HyperlinkControlTypeId;
- case AccessibilityRole::list: return UIA_ListControlTypeId;
- case AccessibilityRole::listItem: return UIA_ListItemControlTypeId;
- case AccessibilityRole::tree: return UIA_TreeControlTypeId;
- case AccessibilityRole::treeItem: return UIA_TreeItemControlTypeId;
- case AccessibilityRole::progressBar: return UIA_ProgressBarControlTypeId;
- case AccessibilityRole::group: return UIA_GroupControlTypeId;
- case AccessibilityRole::dialogWindow: return UIA_WindowControlTypeId;
- case AccessibilityRole::window: return UIA_WindowControlTypeId;
- case AccessibilityRole::scrollBar: return UIA_ScrollBarControlTypeId;
- case AccessibilityRole::tooltip: return UIA_ToolTipControlTypeId;
- case AccessibilityRole::splashScreen: return UIA_WindowControlTypeId;
- case AccessibilityRole::ignored: return UIA_CustomControlTypeId;
- case AccessibilityRole::unspecified: return UIA_CustomControlTypeId;
- };
-
- return UIA_CustomControlTypeId;
- }
-
- //==============================================================================
- AccessibilityNativeHandle::AccessibilityNativeHandle (AccessibilityHandler& handler)
- : ComBaseClassHelper (0),
- accessibilityHandler (handler)
- {
- }
-
- //==============================================================================
- JUCE_COMRESULT AccessibilityNativeHandle::QueryInterface (REFIID refId, void** result)
- {
- *result = nullptr;
-
- if (! isElementValid())
- return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
-
- if ((refId == __uuidof (IRawElementProviderFragmentRoot) && ! isFragmentRoot()))
- return E_NOINTERFACE;
-
- return ComBaseClassHelper::QueryInterface (refId, result);
- }
-
- //==============================================================================
- JUCE_COMRESULT AccessibilityNativeHandle::get_HostRawElementProvider (IRawElementProviderSimple** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- if (isFragmentRoot())
- if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
- return wrapper->hostProviderFromHwnd ((HWND) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
-
- return S_OK;
- });
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::get_ProviderOptions (ProviderOptions* options)
- {
- if (options == nullptr)
- return E_INVALIDARG;
-
- *options = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
- return S_OK;
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUnknown** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- *pRetVal = [&]() -> IUnknown*
- {
- const auto role = accessibilityHandler.getRole();
- const auto fragmentRoot = isFragmentRoot();
-
- switch (pId)
- {
- case UIA_WindowPatternId:
- {
- if (fragmentRoot)
- return new UIAWindowProvider (this);
-
- break;
- }
- case UIA_TransformPatternId:
- {
- if (fragmentRoot)
- return new UIATransformProvider (this);
-
- break;
- }
- case UIA_TextPatternId:
- case UIA_TextPattern2Id:
- {
- if (accessibilityHandler.getTextInterface() != nullptr)
- return new UIATextProvider (this);
-
- break;
- }
- case UIA_ValuePatternId:
- {
- if (accessibilityHandler.getValueInterface() != nullptr)
- return new UIAValueProvider (this);
-
- break;
- }
- case UIA_RangeValuePatternId:
- {
- if (accessibilityHandler.getValueInterface() != nullptr
- && accessibilityHandler.getValueInterface()->getRange().isValid())
- {
- return new UIARangeValueProvider (this);
- }
-
- break;
- }
- case UIA_TogglePatternId:
- {
- if (accessibilityHandler.getActions().contains (AccessibilityActionType::toggle)
- && accessibilityHandler.getCurrentState().isCheckable())
- {
- return new UIAToggleProvider (this);
- }
-
- break;
- }
- case UIA_SelectionPatternId:
- {
- if (role == AccessibilityRole::list
- || role == AccessibilityRole::popupMenu
- || role == AccessibilityRole::tree)
- {
- return new UIASelectionProvider (this);
- }
-
- break;
- }
- case UIA_SelectionItemPatternId:
- {
- auto state = accessibilityHandler.getCurrentState();
-
- if (state.isSelectable() || state.isMultiSelectable()
- || role == AccessibilityRole::radioButton)
- {
- return new UIASelectionItemProvider (this);
- }
-
- break;
- }
- case UIA_GridPatternId:
- {
- if (accessibilityHandler.getTableInterface() != nullptr)
- return new UIAGridProvider (this);
-
- break;
- }
- case UIA_GridItemPatternId:
- {
- if (accessibilityHandler.getCellInterface() != nullptr)
- return new UIAGridItemProvider (this);
-
- break;
- }
- case UIA_InvokePatternId:
- {
- if (accessibilityHandler.getActions().contains (AccessibilityActionType::press))
- return new UIAInvokeProvider (this);
-
- break;
- }
- case UIA_ExpandCollapsePatternId:
- {
- if (accessibilityHandler.getActions().contains (AccessibilityActionType::showMenu)
- && accessibilityHandler.getCurrentState().isExpandable())
- return new UIAExpandCollapseProvider (this);
-
- break;
- }
- default:
- break;
- }
-
- return nullptr;
- }();
-
- return S_OK;
- });
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyId, VARIANT* pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- VariantHelpers::clear (pRetVal);
-
- const auto fragmentRoot = isFragmentRoot();
-
- const auto role = accessibilityHandler.getRole();
- const auto state = accessibilityHandler.getCurrentState();
-
- switch (propertyId)
- {
- case UIA_AutomationIdPropertyId:
- VariantHelpers::setString (getAutomationId (accessibilityHandler), pRetVal);
- break;
- case UIA_ControlTypePropertyId:
- VariantHelpers::setInt (roleToControlTypeId (role), pRetVal);
- break;
- case UIA_FrameworkIdPropertyId:
- VariantHelpers::setString ("JUCE", pRetVal);
- break;
- case UIA_FullDescriptionPropertyId:
- VariantHelpers::setString (accessibilityHandler.getDescription(), pRetVal);
- break;
- case UIA_HelpTextPropertyId:
- VariantHelpers::setString (accessibilityHandler.getHelp(), pRetVal);
- break;
- case UIA_IsContentElementPropertyId:
- VariantHelpers::setBool (! accessibilityHandler.isIgnored() && accessibilityHandler.isVisibleWithinParent(),
- pRetVal);
- break;
- case UIA_IsControlElementPropertyId:
- VariantHelpers::setBool (true, pRetVal);
- break;
- case UIA_IsDialogPropertyId:
- VariantHelpers::setBool (role == AccessibilityRole::dialogWindow, pRetVal);
- break;
- case UIA_IsEnabledPropertyId:
- VariantHelpers::setBool (accessibilityHandler.getComponent().isEnabled(), pRetVal);
- break;
- case UIA_IsKeyboardFocusablePropertyId:
- VariantHelpers::setBool (state.isFocusable(), pRetVal);
- break;
- case UIA_HasKeyboardFocusPropertyId:
- VariantHelpers::setBool (accessibilityHandler.hasFocus (true), pRetVal);
- break;
- case UIA_IsOffscreenPropertyId:
- VariantHelpers::setBool (! accessibilityHandler.isVisibleWithinParent(), pRetVal);
- break;
- case UIA_IsPasswordPropertyId:
- if (auto* textInterface = accessibilityHandler.getTextInterface())
- VariantHelpers::setBool (textInterface->isDisplayingProtectedText(), pRetVal);
-
- break;
- case UIA_IsPeripheralPropertyId:
- VariantHelpers::setBool (role == AccessibilityRole::tooltip
- || role == AccessibilityRole::popupMenu
- || role == AccessibilityRole::splashScreen,
- pRetVal);
- break;
- case UIA_NamePropertyId:
- VariantHelpers::setString (getElementName(), pRetVal);
- break;
- case UIA_ProcessIdPropertyId:
- VariantHelpers::setInt ((int) GetCurrentProcessId(), pRetVal);
- break;
- case UIA_NativeWindowHandlePropertyId:
- if (fragmentRoot)
- VariantHelpers::setInt ((int) (pointer_sized_int) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
-
- break;
-
- default:
- break;
- }
-
- return S_OK;
- });
- }
-
- //==============================================================================
- JUCE_COMRESULT AccessibilityNativeHandle::Navigate (NavigateDirection direction, IRawElementProviderFragment** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- auto* handler = [&]() -> AccessibilityHandler*
- {
- if (direction == NavigateDirection_Parent)
- return accessibilityHandler.getParent();
-
- if (direction == NavigateDirection_FirstChild
- || direction == NavigateDirection_LastChild)
- {
- auto children = accessibilityHandler.getChildren();
-
- return children.empty() ? nullptr
- : (direction == NavigateDirection_FirstChild ? children.front()
- : children.back());
- }
-
- if (direction == NavigateDirection_NextSibling
- || direction == NavigateDirection_PreviousSibling)
- {
- if (auto* parent = accessibilityHandler.getParent())
- {
- const auto siblings = parent->getChildren();
- const auto iter = std::find (siblings.cbegin(), siblings.cend(), &accessibilityHandler);
-
- if (iter == siblings.end())
- return nullptr;
-
- if (direction == NavigateDirection_NextSibling && iter != std::prev (siblings.cend()))
- return *std::next (iter);
-
- if (direction == NavigateDirection_PreviousSibling && iter != siblings.cbegin())
- return *std::prev (iter);
- }
- }
-
- return nullptr;
- }();
-
- if (handler != nullptr)
- if (auto* provider = handler->getNativeImplementation())
- if (provider->isElementValid())
- provider->QueryInterface (IID_PPV_ARGS (pRetVal));
-
- return S_OK;
- });
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::GetRuntimeId (SAFEARRAY** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- if (! isFragmentRoot())
- {
- *pRetVal = SafeArrayCreateVector (VT_I4, 0, 2);
-
- if (*pRetVal == nullptr)
- return E_OUTOFMEMORY;
-
- for (LONG i = 0; i < 2; ++i)
- {
- auto hr = SafeArrayPutElement (*pRetVal, &i, &rtid[(size_t) i]);
-
- if (FAILED (hr))
- return E_FAIL;
- }
- }
-
- return S_OK;
- });
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::get_BoundingRectangle (UiaRect* pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- auto bounds = Desktop::getInstance().getDisplays()
- .logicalToPhysical (accessibilityHandler.getComponent().getScreenBounds());
-
- pRetVal->left = bounds.getX();
- pRetVal->top = bounds.getY();
- pRetVal->width = bounds.getWidth();
- pRetVal->height = bounds.getHeight();
-
- return S_OK;
- });
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::GetEmbeddedFragmentRoots (SAFEARRAY** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, []
- {
- return S_OK;
- });
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::SetFocus()
- {
- if (! isElementValid())
- return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
-
- const WeakReference<Component> safeComponent (&accessibilityHandler.getComponent());
-
- accessibilityHandler.getActions().invoke (AccessibilityActionType::focus);
-
- if (safeComponent != nullptr)
- accessibilityHandler.grabFocus();
-
- return S_OK;
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::get_FragmentRoot (IRawElementProviderFragmentRoot** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
- {
- auto* handler = [&]() -> AccessibilityHandler*
- {
- if (isFragmentRoot())
- return &accessibilityHandler;
-
- if (auto* peer = accessibilityHandler.getComponent().getPeer())
- return peer->getComponent().getAccessibilityHandler();
-
- return nullptr;
- }();
-
- if (handler != nullptr)
- {
- handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
- return S_OK;
- }
-
- return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
- });
- }
-
- //==============================================================================
- JUCE_COMRESULT AccessibilityNativeHandle::ElementProviderFromPoint (double x, double y, IRawElementProviderFragment** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- auto* handler = [&]
- {
- auto logicalScreenPoint = Desktop::getInstance().getDisplays()
- .physicalToLogical (Point<int> (roundToInt (x),
- roundToInt (y)));
-
- if (auto* child = accessibilityHandler.getChildAt (logicalScreenPoint))
- return child;
-
- return &accessibilityHandler;
- }();
-
- handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
-
- return S_OK;
- });
- }
-
- JUCE_COMRESULT AccessibilityNativeHandle::GetFocus (IRawElementProviderFragment** pRetVal)
- {
- return withCheckedComArgs (pRetVal, *this, [&]
- {
- const auto getFocusHandler = [this]() -> AccessibilityHandler*
- {
- if (auto* modal = Component::getCurrentlyModalComponent())
- {
- const auto& component = accessibilityHandler.getComponent();
-
- if (! component.isParentOf (modal)
- && component.isCurrentlyBlockedByAnotherModalComponent())
- {
- if (auto* modalHandler = modal->getAccessibilityHandler())
- {
- if (auto* focusChild = modalHandler->getChildFocus())
- return focusChild;
-
- return modalHandler;
- }
- }
- }
-
- if (auto* focusChild = accessibilityHandler.getChildFocus())
- return focusChild;
-
- return nullptr;
- };
-
- if (auto* focusHandler = getFocusHandler())
- focusHandler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
-
- return S_OK;
- });
- }
-
- //==============================================================================
- String AccessibilityNativeHandle::getElementName() const
- {
- if (accessibilityHandler.getRole() == AccessibilityRole::tooltip)
- return accessibilityHandler.getDescription();
-
- auto name = accessibilityHandler.getTitle();
-
- if (name.isEmpty() && isFragmentRoot())
- return getAccessibleApplicationOrPluginName();
-
- return name;
- }
-
- JUCE_END_IGNORE_WARNINGS_GCC_LIKE
-
- } // namespace juce
|