build(deps): vendor JUCE 7.0.12
This commit is contained in:
+37
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
#if ! JUCE_NATIVE_ACCESSIBILITY_INCLUDED
|
||||
class AccessibilityHandler::AccessibilityNativeImpl
|
||||
{
|
||||
public:
|
||||
AccessibilityNativeImpl (AccessibilityHandler&) {}
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+658
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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;
|
||||
|
||||
//==============================================================================
|
||||
class UIAScrollProvider final : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IScrollProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
JUCE_COMCALL Scroll (ComTypes::ScrollAmount, ComTypes::ScrollAmount) override { return E_FAIL; }
|
||||
JUCE_COMCALL SetScrollPercent (double, double) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_HorizontalScrollPercent (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_VerticalScrollPercent (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_HorizontalViewSize (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_VerticalViewSize (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_HorizontallyScrollable (BOOL*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_VerticallyScrollable (BOOL*) override { return E_FAIL; }
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAScrollProvider)
|
||||
};
|
||||
|
||||
class UIAScrollItemProvider final : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IScrollItemProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
JUCE_COMCALL ScrollIntoView() override
|
||||
{
|
||||
if (auto* handler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (&getHandler(), &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = handler->getTableInterface())
|
||||
{
|
||||
tableInterface->showCell (getHandler());
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAScrollItemProvider)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
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 auto roleToControlTypeId (AccessibilityRole roleType)
|
||||
{
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
switch (roleType)
|
||||
{
|
||||
case AccessibilityRole::popupMenu:
|
||||
case AccessibilityRole::dialogWindow:
|
||||
case AccessibilityRole::splashScreen:
|
||||
case AccessibilityRole::window: return UIA_WindowControlTypeId;
|
||||
|
||||
case AccessibilityRole::label:
|
||||
case AccessibilityRole::staticText: return UIA_TextControlTypeId;
|
||||
|
||||
case AccessibilityRole::column:
|
||||
case AccessibilityRole::row: return UIA_ListItemControlTypeId;
|
||||
|
||||
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::editableText: return UIA_EditControlTypeId;
|
||||
case AccessibilityRole::menuItem: return UIA_MenuItemControlTypeId;
|
||||
case AccessibilityRole::menuBar: return UIA_MenuBarControlTypeId;
|
||||
case AccessibilityRole::table: return UIA_TableControlTypeId;
|
||||
case AccessibilityRole::tableHeader: return UIA_HeaderControlTypeId;
|
||||
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::scrollBar: return UIA_ScrollBarControlTypeId;
|
||||
case AccessibilityRole::tooltip: return UIA_ToolTipControlTypeId;
|
||||
|
||||
case AccessibilityRole::ignored:
|
||||
case AccessibilityRole::unspecified: break;
|
||||
};
|
||||
|
||||
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 (ComTypes::IRawElementProviderFragmentRoot) && ! isFragmentRoot()))
|
||||
return E_NOINTERFACE;
|
||||
|
||||
return ComBaseClassHelper::QueryInterface (refId, result);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::get_HostRawElementProvider (IRawElementProviderSimple** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
{
|
||||
if (isFragmentRoot())
|
||||
return wrapper->hostProviderFromHwnd ((HWND) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
|
||||
|
||||
if (auto* embeddedWindow = static_cast<HWND> (AccessibilityHandler::getNativeChildForComponent (accessibilityHandler.getComponent())))
|
||||
return wrapper->hostProviderFromHwnd (embeddedWindow, pRetVal);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::get_ProviderOptions (ProviderOptions* options)
|
||||
{
|
||||
if (options == nullptr)
|
||||
return E_INVALIDARG;
|
||||
|
||||
*options = (ProviderOptions) (ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading);
|
||||
|
||||
if (AccessibilityHandler::getNativeChildForComponent (accessibilityHandler.getComponent()) != nullptr)
|
||||
*options = (ProviderOptions) (*options | ProviderOptions_OverrideProvider);
|
||||
|
||||
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();
|
||||
|
||||
const auto isListOrTableCell = [] (auto& handler)
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (&handler, &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
const auto row = tableInterface->getRowSpan (handler);
|
||||
const auto column = tableInterface->getColumnSpan (handler);
|
||||
|
||||
return row.hasValue() && column.hasValue();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
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.getCurrentState().isCheckable()
|
||||
&& (accessibilityHandler.getActions().contains (AccessibilityActionType::toggle)
|
||||
|| accessibilityHandler.getActions().contains (AccessibilityActionType::press)))
|
||||
{
|
||||
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_TablePatternId:
|
||||
case UIA_GridPatternId:
|
||||
{
|
||||
if (accessibilityHandler.getTableInterface() != nullptr
|
||||
&& (pId == UIA_GridPatternId || accessibilityHandler.getRole() == AccessibilityRole::table))
|
||||
return static_cast<ComTypes::IGridProvider*> (new UIAGridProvider (this));
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_TableItemPatternId:
|
||||
case UIA_GridItemPatternId:
|
||||
{
|
||||
if (isListOrTableCell (accessibilityHandler))
|
||||
return static_cast<ComTypes::IGridItemProvider*> (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;
|
||||
}
|
||||
case UIA_ScrollPatternId:
|
||||
{
|
||||
if (accessibilityHandler.getTableInterface() != nullptr)
|
||||
return new UIAScrollProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_ScrollItemPatternId:
|
||||
{
|
||||
if (isListOrTableCell (accessibilityHandler))
|
||||
return new UIAScrollItemProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyId, VARIANT* pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
VariantHelpers::clear (pRetVal);
|
||||
|
||||
const auto role = accessibilityHandler.getRole();
|
||||
const auto state = accessibilityHandler.getCurrentState();
|
||||
const auto ignored = accessibilityHandler.isIgnored();
|
||||
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
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 (! ignored && 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:
|
||||
if (! ignored)
|
||||
VariantHelpers::setString (getElementName(), pRetVal);
|
||||
|
||||
break;
|
||||
case UIA_ProcessIdPropertyId:
|
||||
VariantHelpers::setInt ((int) GetCurrentProcessId(), pRetVal);
|
||||
break;
|
||||
case UIA_NativeWindowHandlePropertyId:
|
||||
if (isFragmentRoot())
|
||||
VariantHelpers::setInt ((int) (pointer_sized_int) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::Navigate (ComTypes::NavigateDirection direction, ComTypes::IRawElementProviderFragment** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto* handler = [&]() -> AccessibilityHandler*
|
||||
{
|
||||
if (direction == ComTypes::NavigateDirection_Parent)
|
||||
return accessibilityHandler.getParent();
|
||||
|
||||
if (direction == ComTypes::NavigateDirection_FirstChild
|
||||
|| direction == ComTypes::NavigateDirection_LastChild)
|
||||
{
|
||||
auto children = accessibilityHandler.getChildren();
|
||||
|
||||
return children.empty() ? nullptr
|
||||
: (direction == ComTypes::NavigateDirection_FirstChild ? children.front()
|
||||
: children.back());
|
||||
}
|
||||
|
||||
if (direction == ComTypes::NavigateDirection_NextSibling
|
||||
|| direction == ComTypes::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 == ComTypes::NavigateDirection_NextSibling && iter != std::prev (siblings.cend()))
|
||||
return *std::next (iter);
|
||||
|
||||
if (direction == ComTypes::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 (ComTypes::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 (ComTypes::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, ComTypes::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 (ComTypes::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;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetOverrideProviderForHwnd (HWND hwnd, IRawElementProviderSimple** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (auto* component = AccessibilityHandler::getComponentForNativeChild (hwnd))
|
||||
if (auto* handler = component->getAccessibilityHandler())
|
||||
handler->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 detail::AccessibilityHelpers::getApplicationOrPluginName();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
class AccessibilityNativeHandle : public ComBaseClassHelper<IRawElementProviderSimple,
|
||||
ComTypes::IRawElementProviderFragment,
|
||||
ComTypes::IRawElementProviderFragmentRoot,
|
||||
ComTypes::IRawElementProviderHwndOverride>
|
||||
{
|
||||
public:
|
||||
explicit AccessibilityNativeHandle (AccessibilityHandler& handler);
|
||||
|
||||
//==============================================================================
|
||||
void invalidateElement() noexcept { valid = false; }
|
||||
bool isElementValid() const noexcept { return valid; }
|
||||
|
||||
const AccessibilityHandler& getHandler() { return accessibilityHandler; }
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT QueryInterface (REFIID refId, void** result) override;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT get_HostRawElementProvider (IRawElementProviderSimple** provider) override;
|
||||
JUCE_COMRESULT get_ProviderOptions (ProviderOptions* options) override;
|
||||
JUCE_COMRESULT GetPatternProvider (PATTERNID pId, IUnknown** provider) override;
|
||||
JUCE_COMRESULT GetPropertyValue (PROPERTYID propertyId, VARIANT* pRetVal) override;
|
||||
|
||||
JUCE_COMRESULT Navigate (ComTypes::NavigateDirection direction, ComTypes::IRawElementProviderFragment** pRetVal) override;
|
||||
JUCE_COMRESULT GetRuntimeId (SAFEARRAY** pRetVal) override;
|
||||
JUCE_COMRESULT get_BoundingRectangle (ComTypes::UiaRect* pRetVal) override;
|
||||
JUCE_COMRESULT GetEmbeddedFragmentRoots (SAFEARRAY** pRetVal) override;
|
||||
JUCE_COMRESULT SetFocus() override;
|
||||
JUCE_COMRESULT get_FragmentRoot (ComTypes::IRawElementProviderFragmentRoot** pRetVal) override;
|
||||
|
||||
JUCE_COMRESULT ElementProviderFromPoint (double x, double y, ComTypes::IRawElementProviderFragment** pRetVal) override;
|
||||
JUCE_COMRESULT GetFocus (ComTypes::IRawElementProviderFragment** pRetVal) override;
|
||||
|
||||
JUCE_COMRESULT GetOverrideProviderForHwnd (HWND hwnd, IRawElementProviderSimple** pRetVal) override;
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
String getElementName() const;
|
||||
bool isFragmentRoot() const { return accessibilityHandler.getComponent().isOnDesktop(); }
|
||||
|
||||
//==============================================================================
|
||||
AccessibilityHandler& accessibilityHandler;
|
||||
|
||||
static int idCounter;
|
||||
std::array<int, 2> rtid { UiaAppendRuntimeId, ++idCounter };
|
||||
bool valid = true;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeHandle)
|
||||
};
|
||||
|
||||
}
|
||||
Vendored
+274
@@ -0,0 +1,274 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
struct AccessibleObjCClassDeleter
|
||||
{
|
||||
template <typename ElementType>
|
||||
void operator() (ElementType* element) const
|
||||
{
|
||||
juceFreeAccessibilityPlatformSpecificData (element);
|
||||
|
||||
object_setInstanceVariable (element, "handler", nullptr);
|
||||
[element release];
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Base>
|
||||
class AccessibleObjCClass : public ObjCClass<Base>
|
||||
{
|
||||
public:
|
||||
using Holder = std::unique_ptr<Base, AccessibleObjCClassDeleter>;
|
||||
|
||||
protected:
|
||||
AccessibleObjCClass() : AccessibleObjCClass ("JUCEAccessibilityElement_") {}
|
||||
|
||||
explicit AccessibleObjCClass (const char* name) : ObjCClass<Base> (name)
|
||||
{
|
||||
ObjCClass<Base>::template addIvar<AccessibilityHandler*> ("handler");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static AccessibilityHandler* getHandler (id self)
|
||||
{
|
||||
return getIvar<AccessibilityHandler*> (self, "handler");
|
||||
}
|
||||
|
||||
template <typename MemberFn>
|
||||
static auto getInterface (id self, MemberFn fn) noexcept -> decltype ((std::declval<AccessibilityHandler>().*fn)())
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return (handler->*fn)();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static AccessibilityTextInterface* getTextInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getTextInterface); }
|
||||
static AccessibilityValueInterface* getValueInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getValueInterface); }
|
||||
static AccessibilityTableInterface* getTableInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getTableInterface); }
|
||||
static AccessibilityCellInterface* getCellInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getCellInterface); }
|
||||
|
||||
static bool hasEditableText (AccessibilityHandler& handler) noexcept
|
||||
{
|
||||
return handler.getRole() == AccessibilityRole::editableText
|
||||
&& handler.getTextInterface() != nullptr
|
||||
&& ! handler.getTextInterface()->isReadOnly();
|
||||
}
|
||||
|
||||
static id getAccessibilityValueFromInterfaces (const AccessibilityHandler& handler)
|
||||
{
|
||||
if (auto* textInterface = handler.getTextInterface())
|
||||
return juceStringToNS (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() }));
|
||||
|
||||
if (auto* valueInterface = handler.getValueInterface())
|
||||
return juceStringToNS (valueInterface->getCurrentValueAsString());
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static BOOL getIsAccessibilityElement (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return ! handler->isIgnored() && handler->getRole() != AccessibilityRole::window;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static void setAccessibilityValue (id self, SEL, NSString* value)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (hasEditableText (*handler))
|
||||
{
|
||||
handler->getTextInterface()->setText (nsStringToJuce (value));
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto* valueInterface = handler->getValueInterface())
|
||||
if (! valueInterface->isReadOnly())
|
||||
valueInterface->setValueAsString (nsStringToJuce (value));
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL performActionIfSupported (id self, AccessibilityActionType actionType)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
if (handler->getActions().invoke (actionType))
|
||||
return YES;
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static BOOL accessibilityPerformPress (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
if (handler->getCurrentState().isCheckable() && handler->getActions().invoke (AccessibilityActionType::toggle))
|
||||
return YES;
|
||||
|
||||
return performActionIfSupported (self, AccessibilityActionType::press);
|
||||
}
|
||||
|
||||
static BOOL accessibilityPerformIncrement (id self, SEL)
|
||||
{
|
||||
if (auto* valueInterface = getValueInterface (self))
|
||||
{
|
||||
if (! valueInterface->isReadOnly())
|
||||
{
|
||||
auto range = valueInterface->getRange();
|
||||
|
||||
if (range.isValid())
|
||||
{
|
||||
valueInterface->setValue (jlimit (range.getMinimumValue(),
|
||||
range.getMaximumValue(),
|
||||
valueInterface->getCurrentValue() + range.getInterval()));
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static BOOL accessibilityPerformDecrement (id self, SEL)
|
||||
{
|
||||
if (auto* valueInterface = getValueInterface (self))
|
||||
{
|
||||
if (! valueInterface->isReadOnly())
|
||||
{
|
||||
auto range = valueInterface->getRange();
|
||||
|
||||
if (range.isValid())
|
||||
{
|
||||
valueInterface->setValue (jlimit (range.getMinimumValue(),
|
||||
range.getMaximumValue(),
|
||||
valueInterface->getCurrentValue() - range.getInterval()));
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static NSString* getAccessibilityTitle (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
auto title = handler->getTitle();
|
||||
|
||||
if (title.isEmpty() && handler->getComponent().isOnDesktop())
|
||||
title = detail::AccessibilityHelpers::getApplicationOrPluginName();
|
||||
|
||||
NSString* nsString = juceStringToNS (title);
|
||||
|
||||
if (nsString != nil && [[self accessibilityValue] isEqual: nsString])
|
||||
return @"";
|
||||
|
||||
return nsString;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSString* getAccessibilityHelp (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return juceStringToNS (handler->getHelp());
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static BOOL getIsAccessibilityModal (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->getComponent().isCurrentlyModal();
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityRowCount (id self, SEL)
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
return tableInterface->getNumRows();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityColumnCount (id self, SEL)
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
return tableInterface->getNumColumns();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename Getter>
|
||||
static NSRange getCellDimensions (id self, Getter getter)
|
||||
{
|
||||
const auto notFound = NSMakeRange (NSNotFound, 0);
|
||||
|
||||
auto* handler = getHandler (self);
|
||||
|
||||
if (handler == nullptr)
|
||||
return notFound;
|
||||
|
||||
auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface);
|
||||
|
||||
if (tableHandler == nullptr)
|
||||
return notFound;
|
||||
|
||||
auto* tableInterface = tableHandler->getTableInterface();
|
||||
|
||||
if (tableInterface == nullptr)
|
||||
return notFound;
|
||||
|
||||
const auto result = (tableInterface->*getter) (*handler);
|
||||
|
||||
if (! result.hasValue())
|
||||
return notFound;
|
||||
|
||||
return NSMakeRange ((NSUInteger) result->begin, (NSUInteger) result->num);
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityRowIndexRange (id self, SEL)
|
||||
{
|
||||
return getCellDimensions (self, &AccessibilityTableInterface::getRowSpan);
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityColumnIndexRange (id self, SEL)
|
||||
{
|
||||
return getCellDimensions (self, &AccessibilityTableInterface::getColumnSpan);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibleObjCClass)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+302
@@ -0,0 +1,302 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
struct AccessibilityTextHelpers
|
||||
{
|
||||
/* Wraps a CharPtr into a stdlib-compatible iterator.
|
||||
|
||||
MSVC's std::reverse_iterator requires the wrapped iterator to be default constructible
|
||||
when building in C++20 mode, but I don't really want to add public default constructors to
|
||||
the CharPtr types. Instead, we add a very basic default constructor here which sets the
|
||||
wrapped CharPtr to nullptr.
|
||||
*/
|
||||
template <typename CharPtr>
|
||||
class CharPtrIteratorAdapter
|
||||
{
|
||||
public:
|
||||
using difference_type = int;
|
||||
using value_type = decltype (*std::declval<CharPtr>());
|
||||
using pointer = value_type*;
|
||||
using reference = value_type;
|
||||
using iterator_category = std::bidirectional_iterator_tag;
|
||||
|
||||
CharPtrIteratorAdapter() = default;
|
||||
constexpr explicit CharPtrIteratorAdapter (CharPtr arg) : ptr (arg) {}
|
||||
|
||||
constexpr auto operator*() const { return *ptr; }
|
||||
|
||||
constexpr CharPtrIteratorAdapter& operator++()
|
||||
{
|
||||
++ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr CharPtrIteratorAdapter& operator--()
|
||||
{
|
||||
--ptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
constexpr bool operator== (const CharPtrIteratorAdapter& other) const { return ptr == other.ptr; }
|
||||
constexpr bool operator!= (const CharPtrIteratorAdapter& other) const { return ptr != other.ptr; }
|
||||
|
||||
constexpr auto operator+ (difference_type offset) const { return CharPtrIteratorAdapter { ptr + offset }; }
|
||||
constexpr auto operator- (difference_type offset) const { return CharPtrIteratorAdapter { ptr - offset }; }
|
||||
|
||||
private:
|
||||
CharPtr ptr { {} };
|
||||
};
|
||||
|
||||
template <typename CharPtr>
|
||||
static auto makeCharPtrIteratorAdapter (CharPtr ptr)
|
||||
{
|
||||
return CharPtrIteratorAdapter<CharPtr> { ptr };
|
||||
}
|
||||
|
||||
enum class BoundaryType
|
||||
{
|
||||
character,
|
||||
word,
|
||||
line,
|
||||
document
|
||||
};
|
||||
|
||||
enum class Direction
|
||||
{
|
||||
forwards,
|
||||
backwards
|
||||
};
|
||||
|
||||
enum class ExtendSelection
|
||||
{
|
||||
no,
|
||||
yes
|
||||
};
|
||||
|
||||
/* Indicates whether a function may return the current text position, in the case that the
|
||||
position already falls on a text unit boundary.
|
||||
*/
|
||||
enum class IncludeThisBoundary
|
||||
{
|
||||
no, //< Always search for the following boundary, even if the current position falls on a boundary
|
||||
yes //< Return the current position if it falls on a boundary
|
||||
};
|
||||
|
||||
/* Indicates whether a word boundary should include any whitespaces that follow the
|
||||
non-whitespace characters.
|
||||
*/
|
||||
enum class IncludeWhitespaceAfterWords
|
||||
{
|
||||
no, //< The word ends on the first whitespace character
|
||||
yes //< The word ends after the last whitespace character
|
||||
};
|
||||
|
||||
/* Like std::distance, but always does an O(N) count rather than an O(1) count, and doesn't
|
||||
require the iterators to have any member type aliases.
|
||||
*/
|
||||
template <typename Iter>
|
||||
static int countDifference (Iter from, Iter to)
|
||||
{
|
||||
int distance = 0;
|
||||
|
||||
while (from != to)
|
||||
{
|
||||
++from;
|
||||
++distance;
|
||||
}
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
/* Returns the number of characters between ptr and the next word end in a specific
|
||||
direction.
|
||||
|
||||
If ptr is inside a word, the result will be the distance to the end of the same
|
||||
word.
|
||||
*/
|
||||
template <typename CharPtr>
|
||||
static int findNextWordEndOffset (CharPtr beginIn,
|
||||
CharPtr endIn,
|
||||
CharPtr ptrIn,
|
||||
Direction direction,
|
||||
IncludeThisBoundary includeBoundary,
|
||||
IncludeWhitespaceAfterWords includeWhitespace)
|
||||
{
|
||||
const auto begin = makeCharPtrIteratorAdapter (beginIn);
|
||||
const auto end = makeCharPtrIteratorAdapter (endIn);
|
||||
const auto ptr = makeCharPtrIteratorAdapter (ptrIn);
|
||||
|
||||
const auto move = [&] (auto b, auto e, auto iter)
|
||||
{
|
||||
const auto isSpace = [] (juce_wchar c) { return CharacterFunctions::isWhitespace (c); };
|
||||
|
||||
const auto start = [&]
|
||||
{
|
||||
if (iter == b && includeBoundary == IncludeThisBoundary::yes)
|
||||
return b;
|
||||
|
||||
const auto nudged = iter - (iter != b && includeBoundary == IncludeThisBoundary::yes ? 1 : 0);
|
||||
|
||||
return includeWhitespace == IncludeWhitespaceAfterWords::yes
|
||||
? std::find_if (nudged, e, isSpace)
|
||||
: std::find_if_not (nudged, e, isSpace);
|
||||
}();
|
||||
|
||||
const auto found = includeWhitespace == IncludeWhitespaceAfterWords::yes
|
||||
? std::find_if_not (start, e, isSpace)
|
||||
: std::find_if (start, e, isSpace);
|
||||
|
||||
return countDifference (iter, found);
|
||||
};
|
||||
|
||||
return direction == Direction::forwards ? move (begin, end, ptr)
|
||||
: -move (std::make_reverse_iterator (end),
|
||||
std::make_reverse_iterator (begin),
|
||||
std::make_reverse_iterator (ptr));
|
||||
}
|
||||
|
||||
/* Returns the number of characters between ptr and the beginning of the next line in a
|
||||
specific direction.
|
||||
*/
|
||||
template <typename CharPtr>
|
||||
static int findNextLineOffset (CharPtr beginIn,
|
||||
CharPtr endIn,
|
||||
CharPtr ptrIn,
|
||||
Direction direction,
|
||||
IncludeThisBoundary includeBoundary)
|
||||
{
|
||||
const auto begin = makeCharPtrIteratorAdapter (beginIn);
|
||||
const auto end = makeCharPtrIteratorAdapter (endIn);
|
||||
const auto ptr = makeCharPtrIteratorAdapter (ptrIn);
|
||||
|
||||
const auto findNewline = [] (auto from, auto to) { return std::find (from, to, juce_wchar { '\n' }); };
|
||||
|
||||
if (direction == Direction::forwards)
|
||||
{
|
||||
if (ptr != begin && includeBoundary == IncludeThisBoundary::yes && *(ptr - 1) == '\n')
|
||||
return 0;
|
||||
|
||||
const auto newline = findNewline (ptr, end);
|
||||
return countDifference (ptr, newline) + (newline == end ? 0 : 1);
|
||||
}
|
||||
|
||||
const auto rbegin = std::make_reverse_iterator (ptr);
|
||||
const auto rend = std::make_reverse_iterator (begin);
|
||||
|
||||
return -countDifference (rbegin, findNewline (rbegin + (rbegin == rend || includeBoundary == IncludeThisBoundary::yes ? 0 : 1), rend));
|
||||
}
|
||||
|
||||
/* Unfortunately, the method of computing end-points of text units depends on context, and on
|
||||
the current platform.
|
||||
|
||||
Some examples of different behaviour:
|
||||
- On Android, updating the cursor/selection always searches for the next text unit boundary;
|
||||
but on Windows, ExpandToEnclosingUnit() should not move the starting point of the
|
||||
selection if it already at a unit boundary. This means that we need both inclusive and
|
||||
exclusive methods for finding the next text boundary.
|
||||
- On Android, moving the cursor by 'words' should move to the first space following a
|
||||
non-space character in the requested direction. On Windows, a 'word' includes trailing
|
||||
whitespace, but not preceding whitespace. This means that we need a way of specifying
|
||||
whether whitespace should be included when navigating by words.
|
||||
*/
|
||||
static int findTextBoundary (const AccessibilityTextInterface& textInterface,
|
||||
int currentPosition,
|
||||
BoundaryType boundary,
|
||||
Direction direction,
|
||||
IncludeThisBoundary includeBoundary,
|
||||
IncludeWhitespaceAfterWords includeWhitespace)
|
||||
{
|
||||
const auto numCharacters = textInterface.getTotalNumCharacters();
|
||||
const auto isForwards = (direction == Direction::forwards);
|
||||
const auto currentClamped = jlimit (0, numCharacters, currentPosition);
|
||||
|
||||
switch (boundary)
|
||||
{
|
||||
case BoundaryType::character:
|
||||
{
|
||||
const auto offset = includeBoundary == IncludeThisBoundary::yes ? 0
|
||||
: (isForwards ? 1 : -1);
|
||||
return jlimit (0, numCharacters, currentPosition + offset);
|
||||
}
|
||||
|
||||
case BoundaryType::word:
|
||||
{
|
||||
const auto str = textInterface.getText ({ 0, numCharacters });
|
||||
return currentClamped + findNextWordEndOffset (str.begin(),
|
||||
str.end(),
|
||||
str.begin() + currentClamped,
|
||||
direction,
|
||||
includeBoundary,
|
||||
includeWhitespace);
|
||||
}
|
||||
|
||||
case BoundaryType::line:
|
||||
{
|
||||
const auto str = textInterface.getText ({ 0, numCharacters });
|
||||
return currentClamped + findNextLineOffset (str.begin(),
|
||||
str.end(),
|
||||
str.begin() + currentClamped,
|
||||
direction,
|
||||
includeBoundary);
|
||||
}
|
||||
|
||||
case BoundaryType::document:
|
||||
return isForwards ? numCharacters : 0;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Adjusts the current text selection range, using an algorithm appropriate for cursor movement
|
||||
on Android.
|
||||
*/
|
||||
static Range<int> findNewSelectionRangeAndroid (const AccessibilityTextInterface& textInterface,
|
||||
BoundaryType boundaryType,
|
||||
ExtendSelection extend,
|
||||
Direction direction)
|
||||
{
|
||||
const auto oldPos = textInterface.getTextInsertionOffset();
|
||||
const auto cursorPos = findTextBoundary (textInterface,
|
||||
oldPos,
|
||||
boundaryType,
|
||||
direction,
|
||||
IncludeThisBoundary::no,
|
||||
IncludeWhitespaceAfterWords::no);
|
||||
|
||||
if (extend == ExtendSelection::no)
|
||||
return { cursorPos, cursorPos };
|
||||
|
||||
const auto currentSelection = textInterface.getSelection();
|
||||
const auto start = currentSelection.getStart();
|
||||
const auto end = currentSelection.getEnd();
|
||||
return Range<int>::between (cursorPos, oldPos == start ? end : start);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
third_party/JUCE/modules/juce_gui_basics/native/accessibility/juce_AccessibilityTextHelpers_test.cpp
Vendored
+155
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
struct AccessibilityTextHelpersTest final : public UnitTest
|
||||
{
|
||||
AccessibilityTextHelpersTest()
|
||||
: UnitTest ("AccessibilityTextHelpers", UnitTestCategories::gui) {}
|
||||
|
||||
void runTest() override
|
||||
{
|
||||
using ATH = AccessibilityTextHelpers;
|
||||
|
||||
beginTest ("Android find word end");
|
||||
{
|
||||
const auto testMultiple = [this] (String str,
|
||||
int start,
|
||||
const std::vector<int>& collection)
|
||||
{
|
||||
auto it = collection.begin();
|
||||
|
||||
for (const auto direction : { ATH::Direction::forwards, ATH::Direction::backwards })
|
||||
{
|
||||
for (const auto includeBoundary : { ATH::IncludeThisBoundary::no, ATH::IncludeThisBoundary::yes })
|
||||
{
|
||||
for (const auto includeWhitespace : { ATH::IncludeWhitespaceAfterWords::no, ATH::IncludeWhitespaceAfterWords::yes })
|
||||
{
|
||||
const auto actual = ATH::findNextWordEndOffset (str.begin(), str.end(), str.begin() + start, direction, includeBoundary, includeWhitespace);
|
||||
const auto expected = *it++;
|
||||
expect (expected == actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Character Indices 0 3 56 13 50 51
|
||||
// | | || | | |
|
||||
const auto string = String ("hello world \r\n with some spaces in this sentence ") + String (CharPointer_UTF8 ("\xe2\x88\xae E\xe2\x8b\x85""da = Q"));
|
||||
// Direction forwards forwards forwards forwards backwards backwards backwards backwards
|
||||
// IncludeBoundary no no yes yes no no yes yes
|
||||
// IncludeWhitespace no yes no yes no yes no yes
|
||||
testMultiple (string, 0, { 5, 6, 5, 0, 0, 0, 0, 0 });
|
||||
testMultiple (string, 3, { 2, 3, 2, 3, -3, -3, -3, -3 });
|
||||
testMultiple (string, 5, { 6, 1, 0, 1, -5, -5, -5, 0 });
|
||||
testMultiple (string, 6, { 5, 9, 5, 0, -6, -1, 0, -1 });
|
||||
testMultiple (string, 13, { 6, 2, 6, 2, -7, -2, -7, -2 });
|
||||
testMultiple (string, 50, { 1, 2, 1, 0, -9, -1, 0, -1 });
|
||||
testMultiple (string, 51, { 5, 1, 0, 1, -1, -2, -1, 0 });
|
||||
|
||||
testMultiple (" a b ", 0, { 3, 2, 0, 2, 0, 0, 0, 0 });
|
||||
testMultiple (" a b ", 1, { 2, 1, 2, 1, -1, -1, -1, -1 });
|
||||
}
|
||||
|
||||
beginTest ("Android text range adjustment");
|
||||
{
|
||||
const auto testMultiple = [this] (String str,
|
||||
Range<int> initial,
|
||||
auto boundary,
|
||||
const std::vector<Range<int>>& collection)
|
||||
{
|
||||
auto it = collection.begin();
|
||||
|
||||
for (auto extend : { ATH::ExtendSelection::no, ATH::ExtendSelection::yes })
|
||||
{
|
||||
for (auto direction : { ATH::Direction::forwards, ATH::Direction::backwards })
|
||||
{
|
||||
for (auto insert : { CursorPosition::begin, CursorPosition::end })
|
||||
{
|
||||
const MockAccessibilityTextInterface mock { str, initial, insert };
|
||||
const auto actual = ATH::findNewSelectionRangeAndroid (mock, boundary, extend, direction);
|
||||
const auto expected = *it++;
|
||||
expect (expected == actual);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Extend no no no no yes yes yes yes
|
||||
// Direction forwards forwards backwards backwards forwards forwards backwards backwards
|
||||
// Insert begin end begin end begin end begin end
|
||||
testMultiple ("hello world", { 5, 5 }, ATH::BoundaryType::character, { { 6, 6 }, { 6, 6 }, { 4, 4 }, { 4, 4 }, { 5, 6 }, { 5, 6 }, { 4, 5 }, { 4, 5 } });
|
||||
testMultiple ("hello world", { 0, 0 }, ATH::BoundaryType::character, { { 1, 1 }, { 1, 1 }, { 0, 0 }, { 0, 0 }, { 0, 1 }, { 0, 1 }, { 0, 0 }, { 0, 0 } });
|
||||
testMultiple ("hello world", { 11, 11 }, ATH::BoundaryType::character, { { 11, 11 }, { 11, 11 }, { 10, 10 }, { 10, 10 }, { 11, 11 }, { 11, 11 }, { 10, 11 }, { 10, 11 } });
|
||||
testMultiple ("hello world", { 4, 5 }, ATH::BoundaryType::character, { { 5, 5 }, { 6, 6 }, { 3, 3 }, { 4, 4 }, { 5, 5 }, { 4, 6 }, { 3, 5 }, { 4, 4 } });
|
||||
testMultiple ("hello world", { 0, 1 }, ATH::BoundaryType::character, { { 1, 1 }, { 2, 2 }, { 0, 0 }, { 0, 0 }, { 1, 1 }, { 0, 2 }, { 0, 1 }, { 0, 0 } });
|
||||
testMultiple ("hello world", { 10, 11 }, ATH::BoundaryType::character, { { 11, 11 }, { 11, 11 }, { 9, 9 }, { 10, 10 }, { 11, 11 }, { 10, 11 }, { 9, 11 }, { 10, 10 } });
|
||||
|
||||
testMultiple ("foo bar baz", { 0, 0 }, ATH::BoundaryType::word, { { 3, 3 }, { 3, 3 }, { 0, 0 }, { 0, 0 }, { 0, 3 }, { 0, 3 }, { 0, 0 }, { 0, 0 } });
|
||||
testMultiple ("foo bar baz", { 1, 6 }, ATH::BoundaryType::word, { { 3, 3 }, { 8, 8 }, { 0, 0 }, { 5, 5 }, { 3, 6 }, { 1, 8 }, { 0, 6 }, { 1, 5 } });
|
||||
testMultiple ("foo bar baz", { 3, 3 }, ATH::BoundaryType::word, { { 8, 8 }, { 8, 8 }, { 0, 0 }, { 0, 0 }, { 3, 8 }, { 3, 8 }, { 0, 3 }, { 0, 3 } });
|
||||
testMultiple ("foo bar baz", { 3, 5 }, ATH::BoundaryType::word, { { 8, 8 }, { 8, 8 }, { 0, 0 }, { 0, 0 }, { 5, 8 }, { 3, 8 }, { 0, 5 }, { 0, 3 } });
|
||||
|
||||
testMultiple ("foo bar\n\n\na b\nc d e", { 0, 0 }, ATH::BoundaryType::line, { { 8, 8 }, { 8, 8 }, { 0, 0 }, { 0, 0 }, { 0, 8 }, { 0, 8 }, { 0, 0 }, { 0, 0 } });
|
||||
testMultiple ("foo bar\n\n\na b\nc d e", { 7, 7 }, ATH::BoundaryType::line, { { 8, 8 }, { 8, 8 }, { 0, 0 }, { 0, 0 }, { 7, 8 }, { 7, 8 }, { 0, 7 }, { 0, 7 } });
|
||||
testMultiple ("foo bar\n\n\na b\nc d e", { 8, 8 }, ATH::BoundaryType::line, { { 9, 9 }, { 9, 9 }, { 0, 0 }, { 0, 0 }, { 8, 9 }, { 8, 9 }, { 0, 8 }, { 0, 8 } });
|
||||
|
||||
testMultiple ("foo bar\r\na b\r\nxyz", { 0, 0 }, ATH::BoundaryType::line, { { 9, 9 }, { 9, 9 }, { 0, 0 }, { 0, 0 }, { 0, 9 }, { 0, 9 }, { 0, 0 }, { 0, 0 } });
|
||||
testMultiple ("foo bar\r\na b\r\nxyz", { 10, 10 }, ATH::BoundaryType::line, { { 14, 14 }, { 14, 14 }, { 9, 9 }, { 9, 9 }, { 10, 14 }, { 10, 14 }, { 9, 10 }, { 9, 10 } });
|
||||
}
|
||||
}
|
||||
|
||||
enum class CursorPosition { begin, end };
|
||||
|
||||
class MockAccessibilityTextInterface final : public AccessibilityTextInterface
|
||||
{
|
||||
public:
|
||||
MockAccessibilityTextInterface (String stringIn, Range<int> selectionIn, CursorPosition insertIn)
|
||||
: string (stringIn), selection (selectionIn), insert (insertIn) {}
|
||||
|
||||
bool isDisplayingProtectedText() const override { return false; }
|
||||
bool isReadOnly() const override { return false; }
|
||||
int getTotalNumCharacters() const override { return string.length(); }
|
||||
Range<int> getSelection() const override { return selection; }
|
||||
int getTextInsertionOffset() const override { return insert == CursorPosition::begin ? selection.getStart() : selection.getEnd(); }
|
||||
String getText (Range<int> range) const override { return string.substring (range.getStart(), range.getEnd()); }
|
||||
RectangleList<int> getTextBounds (Range<int>) const override { return {}; }
|
||||
int getOffsetAtPoint (Point<int>) const override { return 0; }
|
||||
|
||||
void setSelection (Range<int> newRange) override { selection = newRange; }
|
||||
void setText (const String& newText) override { string = newText; }
|
||||
|
||||
private:
|
||||
String string;
|
||||
Range<int> selection;
|
||||
CursorPosition insert;
|
||||
};
|
||||
};
|
||||
|
||||
static AccessibilityTextHelpersTest accessibilityTextHelpersTest;
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+1059
File diff suppressed because it is too large
Load Diff
+677
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
static void juceFreeAccessibilityPlatformSpecificData (UIAccessibilityElement* element)
|
||||
{
|
||||
if (auto* container = juce::getIvar<UIAccessibilityElement*> (element, "container"))
|
||||
{
|
||||
object_setInstanceVariable (element, "container", nullptr);
|
||||
object_setInstanceVariable (container, "handler", nullptr);
|
||||
|
||||
[container release];
|
||||
}
|
||||
}
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
|
||||
|
||||
template <typename> struct Signature {};
|
||||
|
||||
template <typename Result, typename... Args>
|
||||
struct Signature<Result (Args...)> {};
|
||||
|
||||
// @selector isn't constexpr, so the 'sel' members are functions rather than static constexpr data members
|
||||
struct SignatureHasText final : public Signature<BOOL()> { static auto sel() { return @selector (hasText); } };
|
||||
struct SignatureSetSelectedTextRange final : public Signature<void (UITextRange*)> { static auto sel() { return @selector (setSelectedTextRange:); } };
|
||||
struct SignatureSelectedTextRange final : public Signature<UITextRange*()> { static auto sel() { return @selector (selectedTextRange); } };
|
||||
struct SignatureMarkedTextRange final : public Signature<UITextRange*()> { static auto sel() { return @selector (markedTextRange); } };
|
||||
struct SignatureSetMarkedTextSelectedRange final : public Signature<void (NSString*, NSRange)> { static auto sel() { return @selector (setMarkedText:selectedRange:); } };
|
||||
struct SignatureUnmarkText final : public Signature<void()> { static auto sel() { return @selector (unmarkText); } };
|
||||
struct SignatureMarkedTextStyle final : public Signature<NSDictionary<NSAttributedStringKey, id>*()> { static auto sel() { return @selector (markedTextStyle); } };
|
||||
struct SignatureSetMarkedTextStyle final : public Signature<void (NSDictionary<NSAttributedStringKey, id>*)> { static auto sel() { return @selector (setMarkedTextStyle:); } };
|
||||
struct SignatureBeginningOfDocument final : public Signature<UITextPosition*()> { static auto sel() { return @selector (beginningOfDocument); } };
|
||||
struct SignatureEndOfDocument final : public Signature<UITextPosition*()> { static auto sel() { return @selector (endOfDocument); } };
|
||||
struct SignatureTokenizer final : public Signature<id<UITextInputTokenizer>()> { static auto sel() { return @selector (tokenizer); } };
|
||||
struct SignatureBaseWritingDirection final : public Signature<NSWritingDirection (UITextPosition*, UITextStorageDirection)> { static auto sel() { return @selector (baseWritingDirectionForPosition:inDirection:); } };
|
||||
struct SignatureCaretRectForPosition final : public Signature<CGRect (UITextPosition*)> { static auto sel() { return @selector (caretRectForPosition:); } };
|
||||
struct SignatureCharacterRangeByExtending final : public Signature<UITextRange* (UITextPosition*, UITextLayoutDirection)> { static auto sel() { return @selector (characterRangeByExtendingPosition:inDirection:); } };
|
||||
struct SignatureCharacterRangeAtPoint final : public Signature<UITextRange* (CGPoint)> { static auto sel() { return @selector (characterRangeAtPoint:); } };
|
||||
struct SignatureClosestPositionToPoint final : public Signature<UITextPosition* (CGPoint)> { static auto sel() { return @selector (closestPositionToPoint:); } };
|
||||
struct SignatureClosestPositionToPointInRange final : public Signature<UITextPosition* (CGPoint, UITextRange*)> { static auto sel() { return @selector (closestPositionToPoint:withinRange:); } };
|
||||
struct SignatureComparePositionToPosition final : public Signature<NSComparisonResult (UITextPosition*, UITextPosition*)> { static auto sel() { return @selector (comparePosition:toPosition:); } };
|
||||
struct SignatureOffsetFromPositionToPosition final : public Signature<NSInteger (UITextPosition*, UITextPosition*)> { static auto sel() { return @selector (offsetFromPosition:toPosition:); } };
|
||||
struct SignaturePositionFromPositionInDirection final : public Signature<UITextPosition* (UITextPosition*, UITextLayoutDirection, NSInteger)> { static auto sel() { return @selector (positionFromPosition:inDirection:offset:); } };
|
||||
struct SignaturePositionFromPositionOffset final : public Signature<UITextPosition* (UITextPosition*, NSInteger)> { static auto sel() { return @selector (positionFromPosition:offset:); } };
|
||||
struct SignatureFirstRectForRange final : public Signature<CGRect (UITextRange*)> { static auto sel() { return @selector (firstRectForRange:); } };
|
||||
struct SignatureSelectionRectsForRange final : public Signature<NSArray<UITextSelectionRect*>* (UITextRange*)> { static auto sel() { return @selector (selectionRectsForRange:); } };
|
||||
struct SignaturePositionWithinRange final : public Signature<UITextPosition* (UITextRange*, UITextLayoutDirection)> { static auto sel() { return @selector (positionWithinRange:farthestInDirection:); } };
|
||||
struct SignatureReplaceRangeWithText final : public Signature<void (UITextRange*, NSString*)> { static auto sel() { return @selector (replaceRange:withText:); } };
|
||||
struct SignatureSetBaseWritingDirection final : public Signature<void (NSWritingDirection, UITextRange*)> { static auto sel() { return @selector (setBaseWritingDirection:forRange:); } };
|
||||
struct SignatureTextInRange final : public Signature<NSString* (UITextRange*)> { static auto sel() { return @selector (textInRange:); } };
|
||||
struct SignatureTextRangeFromPosition final : public Signature<UITextRange* (UITextPosition*, UITextPosition*)> { static auto sel() { return @selector (textRangeFromPosition:toPosition:); } };
|
||||
struct SignatureSetInputDelegate final : public Signature<void (id)> { static auto sel() { return @selector (setInputDelegate:); } };
|
||||
struct SignatureInputDelegate final : public Signature<id()> { static auto sel() { return @selector (inputDelegate); } };
|
||||
struct SignatureKeyboardType final : public Signature<UIKeyboardType()> { static auto sel() { return @selector (keyboardType); } };
|
||||
struct SignatureAutocapitalizationType final : public Signature<UITextAutocapitalizationType()> { static auto sel() { return @selector (autocapitalizationType); } };
|
||||
struct SignatureAutocorrectionType final : public Signature<UITextAutocorrectionType()> { static auto sel() { return @selector (autocorrectionType); } };
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityHandler::AccessibilityNativeImpl
|
||||
{
|
||||
public:
|
||||
explicit AccessibilityNativeImpl (AccessibilityHandler& handler)
|
||||
: accessibilityElement (AccessibilityElement::create (handler))
|
||||
{
|
||||
}
|
||||
|
||||
UIAccessibilityElement* getAccessibilityElement() const noexcept
|
||||
{
|
||||
return accessibilityElement.get();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class AccessibilityContainer final : public AccessibleObjCClass<NSObject>
|
||||
{
|
||||
public:
|
||||
AccessibilityContainer()
|
||||
: AccessibleObjCClass ("JUCEUIAccessibilityContainer_")
|
||||
{
|
||||
addMethod (@selector (isAccessibilityElement), [] (id, SEL) { return false; });
|
||||
|
||||
addMethod (@selector (accessibilityFrame), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return convertToCGRect (handler->getComponent().getScreenBounds());
|
||||
|
||||
return CGRectZero;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityElements), [] (id self, SEL) -> NSArray*
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return getContainerAccessibilityElements (*handler);
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
if (@available (iOS 11.0, *))
|
||||
{
|
||||
addMethod (@selector (accessibilityDataTableCellElementForRow:column:), [] (id self, SEL, NSUInteger row, NSUInteger column) -> id
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
if (auto* cellHandler = tableInterface->getCellHandler ((int) row, (int) column))
|
||||
if (auto* parent = getAccessibleParent (cellHandler))
|
||||
return static_cast<id> (parent->getNativeImplementation());
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount);
|
||||
addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount);
|
||||
|
||||
addMethod (@selector (accessibilityHeaderElementsForColumn:), [] (id self, SEL, NSUInteger column) -> NSArray*
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
if (auto* header = tableInterface->getHeaderHandler())
|
||||
{
|
||||
if (isPositiveAndBelow (column, header->getChildren().size()))
|
||||
{
|
||||
auto* result = [NSMutableArray new];
|
||||
[result addObject: static_cast<id> (header->getChildren()[(size_t) column]->getNativeImplementation())];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTable));
|
||||
|
||||
addMethod (@selector (accessibilityContainerType), [] (id self, SEL) -> NSInteger
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getTableInterface() != nullptr)
|
||||
{
|
||||
if (@available (iOS 11.0, *))
|
||||
return UIAccessibilityContainerTypeDataTable;
|
||||
|
||||
return 1; // UIAccessibilityContainerTypeDataTable
|
||||
}
|
||||
|
||||
const auto handlerRole = handler->getRole();
|
||||
|
||||
if (handlerRole == AccessibilityRole::popupMenu
|
||||
|| handlerRole == AccessibilityRole::list
|
||||
|| handlerRole == AccessibilityRole::tree)
|
||||
{
|
||||
if (@available (iOS 11.0, *))
|
||||
return UIAccessibilityContainerTypeList;
|
||||
|
||||
return 2; // UIAccessibilityContainerTypeList
|
||||
}
|
||||
}
|
||||
|
||||
if (@available (iOS 11.0, *))
|
||||
return UIAccessibilityContainerTypeNone;
|
||||
|
||||
return 0; // UIAccessibilityContainerTypeNone
|
||||
});
|
||||
}
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
static const AccessibilityHandler* getAccessibleParent (const AccessibilityHandler* h)
|
||||
{
|
||||
if (h == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if ([static_cast<id> (h->getNativeImplementation()) isAccessibilityElement])
|
||||
return h;
|
||||
|
||||
return getAccessibleParent (h->getParent());
|
||||
}
|
||||
|
||||
static AccessibilityHandler* getHandler (id self)
|
||||
{
|
||||
return getIvar<AccessibilityHandler*> (self, "handler");
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityElement final : public AccessibleObjCClass<UIAccessibilityElement>
|
||||
{
|
||||
template <typename Func, typename... Items>
|
||||
static constexpr void forEach (Func&& func, Items&&... items)
|
||||
{
|
||||
(func (std::forward<Items> (items)), ...);
|
||||
}
|
||||
|
||||
public:
|
||||
enum class Type { defaultElement, textElement };
|
||||
|
||||
static Holder create (AccessibilityHandler& handler)
|
||||
{
|
||||
static AccessibilityElement cls { Type::defaultElement };
|
||||
static AccessibilityElement textCls { Type::textElement };
|
||||
|
||||
id instance = (hasEditableText (handler) ? textCls : cls).createInstance();
|
||||
|
||||
Holder element ([instance initWithAccessibilityContainer: static_cast<id> (handler.getComponent().getWindowHandle())]);
|
||||
object_setInstanceVariable (element.get(), "handler", &handler);
|
||||
return element;
|
||||
}
|
||||
|
||||
AccessibilityElement (Type elementType)
|
||||
{
|
||||
addMethod (@selector (isAccessibilityElement), [] (id self, SEL)
|
||||
{
|
||||
auto* handler = getHandler (self);
|
||||
|
||||
const auto hasAccessiblePropertiesOrIsTableCell = [] (auto& handlerRef)
|
||||
{
|
||||
const auto isTableCell = [&]
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (&handlerRef, &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
return tableInterface->getRowSpan (handlerRef).hasValue()
|
||||
&& tableInterface->getColumnSpan (handlerRef).hasValue();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
return handlerRef.getTitle().isNotEmpty()
|
||||
|| handlerRef.getHelp().isNotEmpty()
|
||||
|| handlerRef.getTextInterface() != nullptr
|
||||
|| handlerRef.getValueInterface() != nullptr
|
||||
|| isTableCell();
|
||||
};
|
||||
|
||||
return handler != nullptr
|
||||
&& ! handler->isIgnored()
|
||||
&& handler->getRole() != AccessibilityRole::window
|
||||
&& hasAccessiblePropertiesOrIsTableCell (*handler);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityContainer), [] (id self, SEL) -> id
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getComponent().isOnDesktop())
|
||||
return static_cast<id> (handler->getComponent().getWindowHandle());
|
||||
|
||||
if ( ! handler->getChildren().empty()
|
||||
|| AccessibilityHandler::getNativeChildForComponent (handler->getComponent()) != nullptr)
|
||||
{
|
||||
if (UIAccessibilityElement* container = getContainer (self))
|
||||
return container;
|
||||
|
||||
static AccessibilityContainer cls;
|
||||
|
||||
id container = cls.createInstance();
|
||||
|
||||
object_setInstanceVariable (container, "handler", handler);
|
||||
object_setInstanceVariable (self, "container", container);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
if (auto* parent = handler->getParent())
|
||||
return [static_cast<id> (parent->getNativeImplementation()) accessibilityContainer];
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityFrame), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return convertToCGRect (handler->getComponent().getScreenBounds());
|
||||
|
||||
return CGRectZero;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityTraits), [] (id self, SEL)
|
||||
{
|
||||
auto traits = UIAccessibilityTraits{};
|
||||
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
traits |= [&handler]
|
||||
{
|
||||
switch (handler->getRole())
|
||||
{
|
||||
case AccessibilityRole::button:
|
||||
case AccessibilityRole::toggleButton:
|
||||
case AccessibilityRole::radioButton:
|
||||
case AccessibilityRole::comboBox: return UIAccessibilityTraitButton;
|
||||
|
||||
case AccessibilityRole::label:
|
||||
case AccessibilityRole::staticText: return UIAccessibilityTraitStaticText;
|
||||
|
||||
case AccessibilityRole::image: return UIAccessibilityTraitImage;
|
||||
case AccessibilityRole::tableHeader: return UIAccessibilityTraitHeader;
|
||||
case AccessibilityRole::hyperlink: return UIAccessibilityTraitLink;
|
||||
case AccessibilityRole::ignored: return UIAccessibilityTraitNotEnabled;
|
||||
|
||||
case AccessibilityRole::editableText: return UIAccessibilityTraitKeyboardKey;
|
||||
|
||||
case AccessibilityRole::slider:
|
||||
case AccessibilityRole::menuItem:
|
||||
case AccessibilityRole::menuBar:
|
||||
case AccessibilityRole::popupMenu:
|
||||
case AccessibilityRole::table:
|
||||
case AccessibilityRole::column:
|
||||
case AccessibilityRole::row:
|
||||
case AccessibilityRole::cell:
|
||||
case AccessibilityRole::list:
|
||||
case AccessibilityRole::listItem:
|
||||
case AccessibilityRole::tree:
|
||||
case AccessibilityRole::treeItem:
|
||||
case AccessibilityRole::progressBar:
|
||||
case AccessibilityRole::group:
|
||||
case AccessibilityRole::dialogWindow:
|
||||
case AccessibilityRole::window:
|
||||
case AccessibilityRole::scrollBar:
|
||||
case AccessibilityRole::tooltip:
|
||||
case AccessibilityRole::splashScreen:
|
||||
case AccessibilityRole::unspecified: break;
|
||||
}
|
||||
|
||||
return UIAccessibilityTraitNone;
|
||||
}();
|
||||
|
||||
const auto state = handler->getCurrentState();
|
||||
|
||||
if (state.isSelected() || state.isChecked())
|
||||
traits |= UIAccessibilityTraitSelected;
|
||||
|
||||
if (auto* valueInterface = getValueInterface (self))
|
||||
if (! valueInterface->isReadOnly() && valueInterface->getRange().isValid())
|
||||
traits |= UIAccessibilityTraitAdjustable;
|
||||
}
|
||||
|
||||
return traits | sendSuperclassMessage<UIAccessibilityTraits> (self, @selector (accessibilityTraits));
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityLabel), getAccessibilityTitle);
|
||||
addMethod (@selector (accessibilityHint), getAccessibilityHelp);
|
||||
|
||||
addMethod (@selector (accessibilityValue), [] (id self, SEL) -> NSString*
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getCurrentState().isCheckable())
|
||||
return handler->getCurrentState().isChecked() ? @"1" : @"0";
|
||||
|
||||
return (NSString*) getAccessibilityValueFromInterfaces (*handler);
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (setAccessibilityValue:), setAccessibilityValue);
|
||||
|
||||
addMethod (@selector (accessibilityElementDidBecomeFocused), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
const WeakReference<Component> safeComponent (&handler->getComponent());
|
||||
|
||||
performActionIfSupported (self, AccessibilityActionType::focus);
|
||||
|
||||
if (safeComponent != nullptr)
|
||||
handler->grabFocus();
|
||||
}
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityElementDidLoseFocus), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
handler->giveAwayFocus();
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityElementIsFocused), [] (id self, SEL) -> BOOL
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->hasFocus (false);
|
||||
|
||||
return NO;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityViewIsModal), getIsAccessibilityModal);
|
||||
|
||||
addMethod (@selector (accessibilityActivate), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
// Occasionally VoiceOver sends accessibilityActivate to the wrong element, so we first query
|
||||
// which element it thinks has focus and forward the event on to that element if it differs
|
||||
id focusedElement = UIAccessibilityFocusedElement (UIAccessibilityNotificationVoiceOverIdentifier);
|
||||
|
||||
if (focusedElement != nullptr && ! [static_cast<id> (handler->getNativeImplementation()) isEqual: focusedElement])
|
||||
return [focusedElement accessibilityActivate];
|
||||
|
||||
if (handler->hasFocus (false))
|
||||
return accessibilityPerformPress (self, {});
|
||||
}
|
||||
|
||||
return NO;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityIncrement), accessibilityPerformIncrement);
|
||||
addMethod (@selector (accessibilityDecrement), accessibilityPerformDecrement);
|
||||
|
||||
addMethod (@selector (accessibilityPerformEscape), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* modal = Component::getCurrentlyModalComponent())
|
||||
{
|
||||
if (auto* modalHandler = modal->getAccessibilityHandler())
|
||||
{
|
||||
if (modalHandler == handler || modalHandler->isParentOf (handler))
|
||||
{
|
||||
modal->exitModalState (0);
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
});
|
||||
|
||||
if (elementType == Type::textElement)
|
||||
{
|
||||
addMethod (@selector (deleteBackward), [] (id, SEL) {});
|
||||
addMethod (@selector (insertText:), [] (id, SEL, NSString*) {});
|
||||
|
||||
forEach ([this] (auto signature) { addPassthroughMethodWithSignature (signature); },
|
||||
SignatureHasText{},
|
||||
SignatureSetSelectedTextRange{},
|
||||
SignatureSelectedTextRange{},
|
||||
SignatureMarkedTextRange{},
|
||||
SignatureSetMarkedTextSelectedRange{},
|
||||
SignatureUnmarkText{},
|
||||
SignatureMarkedTextStyle{},
|
||||
SignatureSetMarkedTextStyle{},
|
||||
SignatureBeginningOfDocument{},
|
||||
SignatureEndOfDocument{},
|
||||
SignatureTokenizer{},
|
||||
SignatureBaseWritingDirection{},
|
||||
SignatureCaretRectForPosition{},
|
||||
SignatureCharacterRangeByExtending{},
|
||||
SignatureCharacterRangeAtPoint{},
|
||||
SignatureClosestPositionToPoint{},
|
||||
SignatureClosestPositionToPointInRange{},
|
||||
SignatureComparePositionToPosition{},
|
||||
SignatureOffsetFromPositionToPosition{},
|
||||
SignaturePositionFromPositionInDirection{},
|
||||
SignaturePositionFromPositionOffset{},
|
||||
SignatureFirstRectForRange{},
|
||||
SignatureSelectionRectsForRange{},
|
||||
SignaturePositionWithinRange{},
|
||||
SignatureReplaceRangeWithText{},
|
||||
SignatureSetBaseWritingDirection{},
|
||||
SignatureTextInRange{},
|
||||
SignatureTextRangeFromPosition{},
|
||||
SignatureSetInputDelegate{},
|
||||
SignatureInputDelegate{},
|
||||
SignatureKeyboardType{},
|
||||
SignatureAutocapitalizationType{},
|
||||
SignatureAutocorrectionType{});
|
||||
|
||||
addProtocol (@protocol (UITextInput));
|
||||
}
|
||||
|
||||
if (@available (iOS 11.0, *))
|
||||
{
|
||||
addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange);
|
||||
addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange);
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTableCell));
|
||||
}
|
||||
|
||||
addIvar<UIAccessibilityElement*> ("container");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Result>
|
||||
static auto getResult (NSInvocation* invocation, detail::Tag<Result>)
|
||||
{
|
||||
Result result{};
|
||||
[invocation getReturnValue: &result];
|
||||
return result;
|
||||
}
|
||||
|
||||
static void getResult (NSInvocation*, detail::Tag<void>) {}
|
||||
|
||||
template <typename HasSelector, typename Result, typename... Args>
|
||||
auto makePassthroughCallback (HasSelector, Signature<Result (Args...)>)
|
||||
{
|
||||
return [] (id self, SEL, Args... args) -> Result
|
||||
{
|
||||
if (auto* input = getPeerTextInput (self))
|
||||
{
|
||||
const auto s = detail::makeCompileTimeStr (@encode (Result), @encode (id), @encode (SEL), @encode (Args)...);
|
||||
const auto signature = [NSMethodSignature signatureWithObjCTypes: s.data()];
|
||||
|
||||
if (signature == nullptr)
|
||||
{
|
||||
jassertfalse;
|
||||
return {};
|
||||
}
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wnullable-to-nonnull-conversion")
|
||||
const auto invocation = [NSInvocation invocationWithMethodSignature: signature];
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
invocation.selector = HasSelector::sel();
|
||||
|
||||
// Indices 0 and 1 are 'id self' and 'SEL _cmd' respectively
|
||||
auto counter = 2;
|
||||
forEach ([&] (auto& arg) { [invocation setArgument: &arg atIndex: counter++]; }, args...);
|
||||
|
||||
[invocation invokeWithTarget: input];
|
||||
|
||||
return getResult (invocation, detail::Tag<Result>{});
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return {};
|
||||
};
|
||||
}
|
||||
|
||||
template <typename Signature>
|
||||
void addPassthroughMethodWithSignature (Signature signature)
|
||||
{
|
||||
addMethod (Signature::sel(), makePassthroughCallback (signature, signature));
|
||||
}
|
||||
|
||||
static UIAccessibilityElement* getContainer (id self)
|
||||
{
|
||||
return getIvar<UIAccessibilityElement*> (self, "container");
|
||||
}
|
||||
|
||||
static UIViewComponentPeer* getPeer (id self)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return static_cast<UIViewComponentPeer*> (handler->getComponent().getPeer());
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static JuceTextView* getPeerTextInput (id self)
|
||||
{
|
||||
if (auto* peer = getPeer (self))
|
||||
return peer->hiddenTextInput.get();
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityElement)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AccessibilityElement::Holder accessibilityElement;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
|
||||
{
|
||||
return (AccessibilityNativeHandle*) nativeImpl->getAccessibilityElement();
|
||||
}
|
||||
|
||||
static bool areAnyAccessibilityClientsActive()
|
||||
{
|
||||
return UIAccessibilityIsVoiceOverRunning();
|
||||
}
|
||||
|
||||
static void sendAccessibilityEvent (UIAccessibilityNotifications notification, id argument)
|
||||
{
|
||||
if (! areAnyAccessibilityClientsActive())
|
||||
return;
|
||||
|
||||
jassert (notification != UIAccessibilityNotifications{});
|
||||
|
||||
UIAccessibilityPostNotification (notification, argument);
|
||||
}
|
||||
|
||||
void detail::AccessibilityHelpers::notifyAccessibilityEvent (const AccessibilityHandler& handler, Event eventType)
|
||||
{
|
||||
auto notification = [eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case Event::elementCreated:
|
||||
case Event::elementDestroyed:
|
||||
case Event::elementMovedOrResized:
|
||||
case Event::focusChanged: return UIAccessibilityLayoutChangedNotification;
|
||||
|
||||
case Event::windowOpened:
|
||||
case Event::windowClosed: return UIAccessibilityScreenChangedNotification;
|
||||
}
|
||||
|
||||
return UIAccessibilityNotifications{};
|
||||
}();
|
||||
|
||||
if (notification != UIAccessibilityNotifications{})
|
||||
{
|
||||
const bool moveToHandler = (eventType == Event::focusChanged && handler.hasFocus (false));
|
||||
|
||||
sendAccessibilityEvent (notification,
|
||||
moveToHandler ? static_cast<id> (handler.getNativeImplementation()) : nil);
|
||||
}
|
||||
}
|
||||
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
|
||||
{
|
||||
auto notification = [eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case AccessibilityEvent::textSelectionChanged:
|
||||
case AccessibilityEvent::rowSelectionChanged:
|
||||
case AccessibilityEvent::textChanged:
|
||||
case AccessibilityEvent::valueChanged:
|
||||
case AccessibilityEvent::titleChanged: break;
|
||||
|
||||
case AccessibilityEvent::structureChanged: return UIAccessibilityLayoutChangedNotification;
|
||||
}
|
||||
|
||||
return UIAccessibilityNotifications{};
|
||||
}();
|
||||
|
||||
if (notification != UIAccessibilityNotifications{})
|
||||
sendAccessibilityEvent (notification, static_cast<id> (getNativeImplementation()));
|
||||
}
|
||||
|
||||
void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority)
|
||||
{
|
||||
sendAccessibilityEvent (UIAccessibilityAnnouncementNotification, juceStringToNS (announcementString));
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
+960
@@ -0,0 +1,960 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
API_AVAILABLE (macos (10.10))
|
||||
static void juceFreeAccessibilityPlatformSpecificData (NSAccessibilityElement<NSAccessibility>*) {}
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityHandler::AccessibilityNativeImpl
|
||||
{
|
||||
public:
|
||||
explicit AccessibilityNativeImpl (AccessibilityHandler& handler)
|
||||
{
|
||||
if (@available (macOS 10.10, *))
|
||||
accessibilityElement = AccessibilityElement::create (handler);
|
||||
}
|
||||
|
||||
API_AVAILABLE (macos (10.10))
|
||||
NSAccessibilityElement<NSAccessibility>* getAccessibilityElement() const noexcept
|
||||
{
|
||||
return accessibilityElement.get();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class API_AVAILABLE (macos (10.10)) AccessibilityElement final : public AccessibleObjCClass<NSAccessibilityElement<NSAccessibility>>
|
||||
{
|
||||
public:
|
||||
static Holder create (AccessibilityHandler& handler)
|
||||
{
|
||||
if (@available (macOS 10.10, *))
|
||||
{
|
||||
static AccessibilityElement cls;
|
||||
Holder element ([cls.createInstance() init]);
|
||||
object_setInstanceVariable (element.get(), "handler", &handler);
|
||||
return element;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
AccessibilityElement()
|
||||
{
|
||||
addMethod (@selector (accessibilityNotifiesWhenDestroyed), [] (id, SEL) { return YES; });
|
||||
addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement);
|
||||
|
||||
addMethod (@selector (isAccessibilityEnabled), [] (id self, SEL) -> BOOL
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->getComponent().isEnabled();
|
||||
|
||||
return NO;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityWindow), getAccessibilityWindow);
|
||||
addMethod (@selector (accessibilityTopLevelUIElement), getAccessibilityWindow);
|
||||
addMethod (@selector (accessibilityChildren), getAccessibilityChildren);
|
||||
addMethod (@selector (isAccessibilityModal), getIsAccessibilityModal);
|
||||
|
||||
addMethod (@selector (accessibilityFocusedUIElement), [] (id self, SEL) -> id
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* modal = Component::getCurrentlyModalComponent())
|
||||
{
|
||||
const auto& handlerComponent = handler->getComponent();
|
||||
|
||||
if (! handlerComponent.isParentOf (modal)
|
||||
&& handlerComponent.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (auto* modalHandler = modal->getAccessibilityHandler())
|
||||
{
|
||||
if (auto* focusChild = modalHandler->getChildFocus())
|
||||
return static_cast<id> (focusChild->getNativeImplementation());
|
||||
|
||||
return static_cast<id> (modalHandler->getNativeImplementation());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* focusChild = handler->getChildFocus())
|
||||
return static_cast<id> (focusChild->getNativeImplementation());
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityHitTest:), [] (id self, SEL, NSPoint point) -> id
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* child = handler->getChildAt (roundToIntPoint (flippedScreenPoint (point))))
|
||||
return static_cast<id> (child->getNativeImplementation());
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityParent), [] (id self, SEL) -> id
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* parentHandler = handler->getParent())
|
||||
return NSAccessibilityUnignoredAncestor (static_cast<id> (parentHandler->getNativeImplementation()));
|
||||
|
||||
return NSAccessibilityUnignoredAncestor (static_cast<id> (handler->getComponent().getWindowHandle()));
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (isAccessibilityFocused), [] (id self, SEL)
|
||||
{
|
||||
return [[self accessibilityWindow] accessibilityFocusedUIElement] == self;
|
||||
});
|
||||
|
||||
addMethod (@selector (setAccessibilityFocused:), [] (id self, SEL, BOOL focused)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (focused)
|
||||
{
|
||||
const WeakReference<Component> safeComponent (&handler->getComponent());
|
||||
|
||||
performActionIfSupported (self, AccessibilityActionType::focus);
|
||||
|
||||
if (safeComponent != nullptr)
|
||||
handler->grabFocus();
|
||||
}
|
||||
else
|
||||
{
|
||||
handler->giveAwayFocus();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityFrame), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return flippedScreenRect (makeNSRect (handler->getComponent().getScreenBounds()));
|
||||
|
||||
return NSZeroRect;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRole), [] (id self, SEL) -> NSAccessibilityRole
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
switch (handler->getRole())
|
||||
{
|
||||
case AccessibilityRole::popupMenu:
|
||||
case AccessibilityRole::tooltip:
|
||||
case AccessibilityRole::splashScreen:
|
||||
case AccessibilityRole::dialogWindow:
|
||||
case AccessibilityRole::window: return NSAccessibilityWindowRole;
|
||||
|
||||
case AccessibilityRole::tableHeader:
|
||||
case AccessibilityRole::unspecified:
|
||||
case AccessibilityRole::group: return NSAccessibilityGroupRole;
|
||||
|
||||
case AccessibilityRole::label:
|
||||
case AccessibilityRole::staticText: return NSAccessibilityStaticTextRole;
|
||||
|
||||
case AccessibilityRole::tree:
|
||||
case AccessibilityRole::list: return NSAccessibilityOutlineRole;
|
||||
|
||||
case AccessibilityRole::listItem:
|
||||
case AccessibilityRole::treeItem: return NSAccessibilityRowRole;
|
||||
|
||||
case AccessibilityRole::button: return NSAccessibilityButtonRole;
|
||||
case AccessibilityRole::toggleButton: return NSAccessibilityCheckBoxRole;
|
||||
case AccessibilityRole::radioButton: return NSAccessibilityRadioButtonRole;
|
||||
case AccessibilityRole::comboBox: return NSAccessibilityPopUpButtonRole;
|
||||
case AccessibilityRole::image: return NSAccessibilityImageRole;
|
||||
case AccessibilityRole::slider: return NSAccessibilitySliderRole;
|
||||
case AccessibilityRole::editableText: return NSAccessibilityTextAreaRole;
|
||||
case AccessibilityRole::menuItem: return NSAccessibilityMenuItemRole;
|
||||
case AccessibilityRole::menuBar: return NSAccessibilityMenuRole;
|
||||
case AccessibilityRole::table: return NSAccessibilityOutlineRole;
|
||||
case AccessibilityRole::column: return NSAccessibilityColumnRole;
|
||||
case AccessibilityRole::row: return NSAccessibilityRowRole;
|
||||
case AccessibilityRole::cell: return NSAccessibilityCellRole;
|
||||
case AccessibilityRole::hyperlink: return NSAccessibilityLinkRole;
|
||||
case AccessibilityRole::progressBar: return NSAccessibilityProgressIndicatorRole;
|
||||
case AccessibilityRole::scrollBar: return NSAccessibilityScrollBarRole;
|
||||
|
||||
case AccessibilityRole::ignored: break;
|
||||
}
|
||||
|
||||
return NSAccessibilityUnknownRole;
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilitySubrole), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
if (textInterface->isDisplayingProtectedText())
|
||||
return NSAccessibilitySecureTextFieldSubrole;
|
||||
|
||||
const auto handlerRole = handler->getRole();
|
||||
|
||||
if (handlerRole == AccessibilityRole::window) return NSAccessibilityStandardWindowSubrole;
|
||||
if (handlerRole == AccessibilityRole::dialogWindow) return NSAccessibilityDialogSubrole;
|
||||
if (handlerRole == AccessibilityRole::tooltip
|
||||
|| handlerRole == AccessibilityRole::splashScreen) return NSAccessibilityFloatingWindowSubrole;
|
||||
if (handlerRole == AccessibilityRole::toggleButton) return NSAccessibilityToggleSubrole;
|
||||
if (handlerRole == AccessibilityRole::treeItem
|
||||
|| handlerRole == AccessibilityRole::listItem) return NSAccessibilityOutlineRowSubrole;
|
||||
if (handlerRole == AccessibilityRole::row && getCellInterface (self) != nullptr) return NSAccessibilityTableRowSubrole;
|
||||
|
||||
const auto& handlerComponent = handler->getComponent();
|
||||
|
||||
if (auto* documentWindow = handlerComponent.findParentComponentOfClass<DocumentWindow>())
|
||||
{
|
||||
if (handlerRole == AccessibilityRole::button)
|
||||
{
|
||||
if (&handlerComponent == documentWindow->getCloseButton()) return NSAccessibilityCloseButtonSubrole;
|
||||
if (&handlerComponent == documentWindow->getMinimiseButton()) return NSAccessibilityMinimizeButtonSubrole;
|
||||
if (&handlerComponent == documentWindow->getMaximiseButton()) return NSAccessibilityFullScreenButtonSubrole;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NSAccessibilityUnknownRole;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityLabel), [] (id self, SEL) -> NSString*
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return juceStringToNS (handler->getDescription());
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityValue), [] (id self, SEL) -> id
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (! handler->getCurrentState().isCheckable())
|
||||
return getAccessibilityValueFromInterfaces (*handler);
|
||||
|
||||
const auto checked = handler->getCurrentState().isChecked();
|
||||
|
||||
if ( handler->getRole() == AccessibilityRole::toggleButton
|
||||
|| handler->getRole() == AccessibilityRole::radioButton)
|
||||
{
|
||||
return checked ? @YES : @NO;
|
||||
}
|
||||
|
||||
return juceStringToNS (checked ? TRANS ("On") : TRANS ("Off"));
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityTitle), getAccessibilityTitle);
|
||||
addMethod (@selector (accessibilityHelp), getAccessibilityHelp);
|
||||
addMethod (@selector (setAccessibilityValue:), setAccessibilityValue);
|
||||
|
||||
addMethod (@selector (accessibilitySelectedChildren), [] (id self, SEL)
|
||||
{
|
||||
return getSelectedChildren ([self accessibilityChildren]);
|
||||
});
|
||||
|
||||
addMethod (@selector (setAccessibilitySelectedChildren:), [] (id self, SEL, NSArray* selected)
|
||||
{
|
||||
setSelectedChildren ([self accessibilityChildren], selected);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityOrientation), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->getComponent().getBounds().toFloat().getAspectRatio() > 1.0f
|
||||
? NSAccessibilityOrientationHorizontal
|
||||
: NSAccessibilityOrientationVertical;
|
||||
|
||||
return NSAccessibilityOrientationUnknown;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityInsertionPointLineNumber), [] (id self, SEL) -> NSInteger
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return [self accessibilityLineForIndex: textInterface->getTextInsertionOffset()];
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityVisibleCharacterRange), [] (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return juceRangeToNS ({ 0, textInterface->getTotalNumCharacters() });
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityNumberOfCharacters), [] (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return textInterface->getTotalNumCharacters();
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilitySelectedText), [] (id self, SEL) -> NSString*
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return juceStringToNS (textInterface->getText (textInterface->getSelection()));
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilitySelectedTextRange), [] (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
{
|
||||
const auto currentSelection = textInterface->getSelection();
|
||||
|
||||
if (currentSelection.isEmpty())
|
||||
return NSMakeRange ((NSUInteger) textInterface->getTextInsertionOffset(), 0);
|
||||
|
||||
return juceRangeToNS (currentSelection);
|
||||
}
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityAttributedStringForRange:), [] (id self, SEL, NSRange range) -> NSAttributedString*
|
||||
{
|
||||
NSString* string = [self accessibilityStringForRange: range];
|
||||
|
||||
if (string != nil)
|
||||
return [[[NSAttributedString alloc] initWithString: string] autorelease];
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRangeForLine:), [] (id self, SEL, NSInteger line)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
{
|
||||
auto text = textInterface->getText ({ 0, textInterface->getTotalNumCharacters() });
|
||||
auto lines = StringArray::fromLines (text);
|
||||
|
||||
if (line < lines.size())
|
||||
{
|
||||
auto lineText = lines[(int) line];
|
||||
auto start = text.indexOf (lineText);
|
||||
|
||||
if (start >= 0)
|
||||
return NSMakeRange ((NSUInteger) start, (NSUInteger) lineText.length());
|
||||
}
|
||||
}
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityStringForRange:), [] (id self, SEL, NSRange range) -> NSString*
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return juceStringToNS (textInterface->getText (nsRangeToJuce (range)));
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRangeForPosition:), [] (id self, SEL, NSPoint position)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* textInterface = handler->getTextInterface())
|
||||
{
|
||||
auto screenPoint = roundToIntPoint (flippedScreenPoint (position));
|
||||
|
||||
if (handler->getComponent().getScreenBounds().contains (screenPoint))
|
||||
{
|
||||
auto offset = textInterface->getOffsetAtPoint (screenPoint);
|
||||
|
||||
if (offset >= 0)
|
||||
return NSMakeRange ((NSUInteger) offset, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRangeForIndex:), [] (id self, SEL, NSInteger index)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
if (isPositiveAndBelow (index, textInterface->getTotalNumCharacters()))
|
||||
return NSMakeRange ((NSUInteger) index, 1);
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityFrameForRange:), [] (id self, SEL, NSRange range)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return flippedScreenRect (makeNSRect (textInterface->getTextBounds (nsRangeToJuce (range)).getBounds()));
|
||||
|
||||
return NSZeroRect;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityLineForIndex:), [] (id self, SEL, NSInteger index)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
{
|
||||
auto text = textInterface->getText ({ 0, (int) index });
|
||||
|
||||
if (! text.isEmpty())
|
||||
return StringArray::fromLines (text).size() - 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
addMethod (@selector (setAccessibilitySelectedTextRange:), [] (id self, SEL, NSRange selectedRange)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
textInterface->setSelection (nsRangeToJuce (selectedRange));
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRows), [] (id self, SEL) -> NSArray*
|
||||
{
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
{
|
||||
auto* rows = [[NSMutableArray new] autorelease];
|
||||
|
||||
for (int row = 0, numRows = tableInterface->getNumRows(); row < numRows; ++row)
|
||||
{
|
||||
if (auto* rowHandler = tableInterface->getRowHandler (row))
|
||||
{
|
||||
[rows addObject: static_cast<id> (rowHandler->getNativeImplementation())];
|
||||
}
|
||||
else
|
||||
{
|
||||
[rows addObject: [NSAccessibilityElement accessibilityElementWithRole: NSAccessibilityRowRole
|
||||
frame: NSZeroRect
|
||||
label: @"Offscreen Row"
|
||||
parent: self]];
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilitySelectedRows), [] (id self, SEL)
|
||||
{
|
||||
return getSelectedChildren ([self accessibilityRows]);
|
||||
});
|
||||
|
||||
addMethod (@selector (setAccessibilitySelectedRows:), [] (id self, SEL, NSArray* selected)
|
||||
{
|
||||
setSelectedChildren ([self accessibilityRows], selected);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityHeader), [] (id self, SEL) -> id
|
||||
{
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
if (auto* handler = tableInterface->getHeaderHandler())
|
||||
return static_cast<id> (handler->getNativeImplementation());
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount);
|
||||
addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount);
|
||||
addMethod (@selector (accessibilityRowIndexRange), getAccessibilityRowIndexRange);
|
||||
addMethod (@selector (accessibilityColumnIndexRange), getAccessibilityColumnIndexRange);
|
||||
|
||||
addMethod (@selector (accessibilityIndex), [] (id self, SEL) -> NSInteger
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (handler, &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
NSAccessibilityRole handlerRole = [self accessibilityRole];
|
||||
|
||||
if ([handlerRole isEqual: NSAccessibilityRowRole])
|
||||
if (const auto span = tableInterface->getRowSpan (*handler))
|
||||
return span->begin;
|
||||
|
||||
if ([handlerRole isEqual: NSAccessibilityColumnRole])
|
||||
if (const auto span = tableInterface->getColumnSpan (*handler))
|
||||
return span->begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityDisclosureLevel), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
if (auto* cellInterface = handler->getCellInterface())
|
||||
return cellInterface->getDisclosureLevel();
|
||||
|
||||
return 0;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityDisclosedRows), [] (id self, SEL) -> id
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* cellInterface = handler->getCellInterface())
|
||||
{
|
||||
const auto rows = cellInterface->getDisclosedRows();
|
||||
auto* result = [NSMutableArray arrayWithCapacity: rows.size()];
|
||||
|
||||
for (const auto& row : rows)
|
||||
{
|
||||
if (row != nullptr)
|
||||
[result addObject: static_cast<id> (row->getNativeImplementation())];
|
||||
else
|
||||
[result addObject: [NSAccessibilityElement accessibilityElementWithRole: NSAccessibilityRowRole
|
||||
frame: NSZeroRect
|
||||
label: @"Offscreen Row"
|
||||
parent: self]];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (isAccessibilityExpanded), [] (id self, SEL) -> BOOL
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->getCurrentState().isExpanded();
|
||||
|
||||
return NO;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityPerformIncrement), accessibilityPerformIncrement);
|
||||
addMethod (@selector (accessibilityPerformDecrement), accessibilityPerformDecrement);
|
||||
addMethod (@selector (accessibilityPerformDelete), [] (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (hasEditableText (*handler))
|
||||
{
|
||||
handler->getTextInterface()->setText ({});
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (auto* valueInterface = handler->getValueInterface())
|
||||
{
|
||||
if (! valueInterface->isReadOnly())
|
||||
{
|
||||
valueInterface->setValue ({});
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NO;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityPerformPress), accessibilityPerformPress);
|
||||
|
||||
addMethod (@selector (accessibilityPerformShowMenu), [] (id self, SEL)
|
||||
{
|
||||
return performActionIfSupported (self, AccessibilityActionType::showMenu);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityPerformRaise), [] (id self, SEL)
|
||||
{
|
||||
[self setAccessibilityFocused: YES]; return YES;
|
||||
});
|
||||
|
||||
addMethod (@selector (isAccessibilitySelectorAllowed:), [] (id self, SEL, SEL selector) -> BOOL
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
const auto handlerRole = handler->getRole();
|
||||
const auto currentState = handler->getCurrentState();
|
||||
|
||||
for (auto textSelector : { @selector (accessibilityInsertionPointLineNumber),
|
||||
@selector (accessibilityVisibleCharacterRange),
|
||||
@selector (accessibilityNumberOfCharacters),
|
||||
@selector (accessibilitySelectedText),
|
||||
@selector (accessibilitySelectedTextRange),
|
||||
@selector (accessibilityAttributedStringForRange:),
|
||||
@selector (accessibilityRangeForLine:),
|
||||
@selector (accessibilityStringForRange:),
|
||||
@selector (accessibilityRangeForPosition:),
|
||||
@selector (accessibilityRangeForIndex:),
|
||||
@selector (accessibilityFrameForRange:),
|
||||
@selector (accessibilityLineForIndex:),
|
||||
@selector (setAccessibilitySelectedTextRange:) })
|
||||
{
|
||||
if (selector == textSelector)
|
||||
return handler->getTextInterface() != nullptr;
|
||||
}
|
||||
|
||||
for (auto tableSelector : { @selector (accessibilityRowCount),
|
||||
@selector (accessibilityRows),
|
||||
@selector (accessibilitySelectedRows),
|
||||
@selector (accessibilityColumnCount),
|
||||
@selector (accessibilityHeader) })
|
||||
{
|
||||
if (selector == tableSelector)
|
||||
return handler->getTableInterface() != nullptr;
|
||||
}
|
||||
|
||||
for (auto cellSelector : { @selector (accessibilityRowIndexRange),
|
||||
@selector (accessibilityColumnIndexRange),
|
||||
@selector (accessibilityIndex),
|
||||
@selector (accessibilityDisclosureLevel) })
|
||||
{
|
||||
if (selector == cellSelector)
|
||||
return handler->getCellInterface() != nullptr;
|
||||
}
|
||||
|
||||
for (auto valueSelector : { @selector (accessibilityValue),
|
||||
@selector (setAccessibilityValue:),
|
||||
@selector (accessibilityPerformDelete),
|
||||
@selector (accessibilityPerformIncrement),
|
||||
@selector (accessibilityPerformDecrement) })
|
||||
{
|
||||
if (selector != valueSelector)
|
||||
continue;
|
||||
|
||||
auto* valueInterface = handler->getValueInterface();
|
||||
|
||||
if (selector == @selector (accessibilityValue))
|
||||
return valueInterface != nullptr
|
||||
|| hasEditableText (*handler)
|
||||
|| currentState.isCheckable();
|
||||
|
||||
auto hasEditableValue = [valueInterface] { return valueInterface != nullptr && ! valueInterface->isReadOnly(); };
|
||||
|
||||
if (selector == @selector (setAccessibilityValue:)
|
||||
|| selector == @selector (accessibilityPerformDelete))
|
||||
return hasEditableValue() || hasEditableText (*handler);
|
||||
|
||||
auto isRanged = [valueInterface] { return valueInterface != nullptr && valueInterface->getRange().isValid(); };
|
||||
|
||||
if (selector == @selector (accessibilityPerformIncrement)
|
||||
|| selector == @selector (accessibilityPerformDecrement))
|
||||
return hasEditableValue() && isRanged();
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
for (auto actionSelector : { @selector (accessibilityPerformPress),
|
||||
@selector (accessibilityPerformShowMenu),
|
||||
@selector (accessibilityPerformRaise),
|
||||
@selector (setAccessibilityFocused:) })
|
||||
{
|
||||
if (selector != actionSelector)
|
||||
continue;
|
||||
|
||||
if (selector == @selector (accessibilityPerformPress))
|
||||
return (handler->getCurrentState().isCheckable() && handler->getActions().contains (AccessibilityActionType::toggle))
|
||||
|| handler->getActions().contains (AccessibilityActionType::press);
|
||||
|
||||
if (selector == @selector (accessibilityPerformShowMenu))
|
||||
return handler->getActions().contains (AccessibilityActionType::showMenu);
|
||||
|
||||
if (selector == @selector (accessibilityPerformRaise))
|
||||
return [[self accessibilityRole] isEqual: NSAccessibilityWindowRole];
|
||||
|
||||
if (selector == @selector (setAccessibilityFocused:))
|
||||
return currentState.isFocusable();
|
||||
}
|
||||
|
||||
if (selector == @selector (accessibilitySelectedChildren))
|
||||
return handlerRole == AccessibilityRole::popupMenu;
|
||||
|
||||
if (selector == @selector (accessibilityOrientation))
|
||||
return handlerRole == AccessibilityRole::scrollBar;
|
||||
|
||||
if (selector == @selector (isAccessibilityExpanded))
|
||||
return currentState.isExpandable();
|
||||
|
||||
return sendSuperclassMessage<BOOL> (self, @selector (isAccessibilitySelectorAllowed:), selector);
|
||||
}
|
||||
|
||||
return NO;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityChildrenInNavigationOrder), getAccessibilityChildren);
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static bool isSelectable (AccessibleState state) noexcept
|
||||
{
|
||||
return state.isSelectable() || state.isMultiSelectable();
|
||||
}
|
||||
|
||||
static NSArray* getSelectedChildren (NSArray* children)
|
||||
{
|
||||
NSMutableArray* selected = [[NSMutableArray new] autorelease];
|
||||
|
||||
for (id child in children)
|
||||
{
|
||||
if (auto* handler = getHandler (child))
|
||||
{
|
||||
const auto currentState = handler->getCurrentState();
|
||||
|
||||
if (isSelectable (currentState) && currentState.isSelected())
|
||||
[selected addObject: child];
|
||||
}
|
||||
}
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
static void setSelected (id item, bool selected)
|
||||
{
|
||||
auto* handler = getHandler (item);
|
||||
|
||||
if (handler == nullptr)
|
||||
return;
|
||||
|
||||
const auto currentState = handler->getCurrentState();
|
||||
|
||||
if (isSelectable (currentState))
|
||||
{
|
||||
if (currentState.isSelected() != selected)
|
||||
handler->getActions().invoke (AccessibilityActionType::toggle);
|
||||
}
|
||||
else if (currentState.isFocusable())
|
||||
{
|
||||
[item setAccessibilityFocused: selected];
|
||||
}
|
||||
}
|
||||
|
||||
static void setSelectedChildren (NSArray* children, NSArray* selected)
|
||||
{
|
||||
for (id child in children)
|
||||
setSelected (child, [selected containsObject: child]);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static id getAccessibilityWindow (id self, SEL)
|
||||
{
|
||||
return [[self accessibilityParent] accessibilityWindow];
|
||||
}
|
||||
|
||||
static NSArray* getAccessibilityChildren (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
auto children = handler->getChildren();
|
||||
|
||||
auto* accessibleChildren = [NSMutableArray arrayWithCapacity: (NSUInteger) children.size()];
|
||||
|
||||
for (auto* childHandler : children)
|
||||
[accessibleChildren addObject: static_cast<id> (childHandler->getNativeImplementation())];
|
||||
|
||||
if (id nativeChild = static_cast<id> (AccessibilityHandler::getNativeChildForComponent (handler->getComponent())))
|
||||
{
|
||||
// Having both native and non-native children would require implementing an
|
||||
// ordering. However, this situation doesn't occur with any of our current
|
||||
// use-cases.
|
||||
jassert ([accessibleChildren count] == 0);
|
||||
|
||||
if ([nativeChild isAccessibilityElement])
|
||||
[accessibleChildren addObject:nativeChild];
|
||||
else if (auto* childrenOfChild = [nativeChild accessibilityChildren]; childrenOfChild != nil)
|
||||
[accessibleChildren addObjectsFromArray:(NSArray* _Nonnull) childrenOfChild];
|
||||
}
|
||||
|
||||
return accessibleChildren;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityElement)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
API_AVAILABLE (macos (10.10))
|
||||
AccessibilityElement::Holder accessibilityElement;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
|
||||
{
|
||||
if (@available (macOS 10.10, *))
|
||||
return (AccessibilityNativeHandle*) nativeImpl->getAccessibilityElement();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool areAnyAccessibilityClientsActive()
|
||||
{
|
||||
const String voiceOverKeyString ("voiceOverOnOffKey");
|
||||
const String applicationIDString ("com.apple.universalaccess");
|
||||
|
||||
CFUniquePtr<CFStringRef> cfKey (voiceOverKeyString.toCFString());
|
||||
CFUniquePtr<CFStringRef> cfID (applicationIDString.toCFString());
|
||||
|
||||
CFUniquePtr<CFPropertyListRef> value (CFPreferencesCopyAppValue (cfKey.get(), cfID.get()));
|
||||
|
||||
if (value != nullptr)
|
||||
return CFBooleanGetValue ((CFBooleanRef) value.get());
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sendAccessibilityEvent (id accessibilityElement,
|
||||
NSAccessibilityNotificationName notification,
|
||||
NSDictionary* userInfo)
|
||||
{
|
||||
jassert (notification != NSAccessibilityNotificationName{});
|
||||
|
||||
NSAccessibilityPostNotificationWithUserInfo (accessibilityElement, notification, userInfo);
|
||||
}
|
||||
|
||||
static void sendHandlerNotification (const AccessibilityHandler& handler,
|
||||
NSAccessibilityNotificationName notification)
|
||||
{
|
||||
if (! areAnyAccessibilityClientsActive() || notification == NSAccessibilityNotificationName{})
|
||||
return;
|
||||
|
||||
if (@available (macOS 10.9, *))
|
||||
{
|
||||
if (id accessibilityElement = static_cast<id> (handler.getNativeImplementation()))
|
||||
{
|
||||
sendAccessibilityEvent (accessibilityElement, notification,
|
||||
(notification == NSAccessibilityLayoutChangedNotification
|
||||
? @{ NSAccessibilityUIElementsKey: @[ accessibilityElement ] }
|
||||
: nil));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static NSAccessibilityNotificationName layoutChangedNotification()
|
||||
{
|
||||
if (@available (macOS 10.9, *))
|
||||
return NSAccessibilityLayoutChangedNotification;
|
||||
|
||||
static NSString* layoutChangedString = @"AXLayoutChanged";
|
||||
return layoutChangedString;
|
||||
}
|
||||
|
||||
void detail::AccessibilityHelpers::notifyAccessibilityEvent (const AccessibilityHandler& handler, Event eventType)
|
||||
{
|
||||
auto notification = [eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case Event::elementCreated: return NSAccessibilityCreatedNotification;
|
||||
case Event::elementDestroyed: return NSAccessibilityUIElementDestroyedNotification;
|
||||
case Event::elementMovedOrResized: return layoutChangedNotification();
|
||||
case Event::focusChanged: return NSAccessibilityFocusedUIElementChangedNotification;
|
||||
case Event::windowOpened: return NSAccessibilityWindowCreatedNotification;
|
||||
case Event::windowClosed: break;
|
||||
}
|
||||
|
||||
return NSAccessibilityNotificationName{};
|
||||
}();
|
||||
|
||||
sendHandlerNotification (handler, notification);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
|
||||
{
|
||||
auto notification = [eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case AccessibilityEvent::textSelectionChanged: return NSAccessibilitySelectedTextChangedNotification;
|
||||
case AccessibilityEvent::rowSelectionChanged: return NSAccessibilitySelectedRowsChangedNotification;
|
||||
|
||||
case AccessibilityEvent::textChanged:
|
||||
case AccessibilityEvent::valueChanged: return NSAccessibilityValueChangedNotification;
|
||||
case AccessibilityEvent::titleChanged: return NSAccessibilityTitleChangedNotification;
|
||||
case AccessibilityEvent::structureChanged: return layoutChangedNotification();
|
||||
}
|
||||
|
||||
return NSAccessibilityNotificationName{};
|
||||
}();
|
||||
|
||||
sendHandlerNotification (*this, notification);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority)
|
||||
{
|
||||
if (! areAnyAccessibilityClientsActive())
|
||||
return;
|
||||
|
||||
if (@available (macOS 10.9, *))
|
||||
{
|
||||
auto nsPriority = [priority]
|
||||
{
|
||||
// The below doesn't get noticed by the @available check above
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability")
|
||||
|
||||
switch (priority)
|
||||
{
|
||||
case AnnouncementPriority::low: return NSAccessibilityPriorityLow;
|
||||
case AnnouncementPriority::medium: return NSAccessibilityPriorityMedium;
|
||||
case AnnouncementPriority::high: return NSAccessibilityPriorityHigh;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return NSAccessibilityPriorityLow;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}();
|
||||
|
||||
sendAccessibilityEvent (static_cast<id> ([NSApp mainWindow]),
|
||||
NSAccessibilityAnnouncementRequestedNotification,
|
||||
@{ NSAccessibilityAnnouncementKey: juceStringToNS (announcementString),
|
||||
NSAccessibilityPriorityKey: @(nsPriority) });
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+331
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
#define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
static bool isStartingUpOrShuttingDown()
|
||||
{
|
||||
if (auto* app = JUCEApplicationBase::getInstance())
|
||||
if (app->isInitialising())
|
||||
return true;
|
||||
|
||||
if (auto* mm = MessageManager::getInstanceWithoutCreating())
|
||||
if (mm->hasStopMessageBeenSent())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isHandlerValid (const AccessibilityHandler& handler)
|
||||
{
|
||||
if (auto* provider = handler.getNativeImplementation())
|
||||
return provider->isElementValid();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityHandler::AccessibilityNativeImpl
|
||||
{
|
||||
public:
|
||||
explicit AccessibilityNativeImpl (AccessibilityHandler& owner)
|
||||
: accessibilityElement (new AccessibilityNativeHandle (owner))
|
||||
{
|
||||
++providerCount;
|
||||
}
|
||||
|
||||
~AccessibilityNativeImpl()
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
|
||||
accessibilityElement->invalidateElement();
|
||||
--providerCount;
|
||||
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
{
|
||||
uiaWrapper->disconnectProvider (provider);
|
||||
|
||||
if (providerCount == 0 && JUCEApplicationBase::isStandaloneApp())
|
||||
uiaWrapper->disconnectAllProviders();
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
ComSmartPtr<AccessibilityNativeHandle> accessibilityElement;
|
||||
static int providerCount;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeImpl)
|
||||
};
|
||||
|
||||
int AccessibilityHandler::AccessibilityNativeImpl::providerCount = 0;
|
||||
|
||||
//==============================================================================
|
||||
AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
|
||||
{
|
||||
return nativeImpl->accessibilityElement;
|
||||
}
|
||||
|
||||
static bool areAnyAccessibilityClientsActive()
|
||||
{
|
||||
const auto areClientsListening = []
|
||||
{
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
return uiaWrapper->clientsAreListening() != 0;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto isScreenReaderRunning = []
|
||||
{
|
||||
BOOL isRunning = FALSE;
|
||||
SystemParametersInfo (SPI_GETSCREENREADER, 0, (PVOID) &isRunning, 0);
|
||||
|
||||
return isRunning != 0;
|
||||
};
|
||||
|
||||
return areClientsListening() || isScreenReaderRunning();
|
||||
}
|
||||
|
||||
template <typename Callback>
|
||||
void getProviderWithCheckedWrapper (const AccessibilityHandler& handler, Callback&& callback)
|
||||
{
|
||||
if (! areAnyAccessibilityClientsActive() || isStartingUpOrShuttingDown() || ! isHandlerValid (handler))
|
||||
return;
|
||||
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
handler.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
|
||||
callback (uiaWrapper, provider);
|
||||
}
|
||||
}
|
||||
|
||||
void sendAccessibilityAutomationEvent (const AccessibilityHandler& handler, EVENTID event)
|
||||
{
|
||||
jassert (event != EVENTID{});
|
||||
|
||||
getProviderWithCheckedWrapper (handler, [event] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
|
||||
{
|
||||
uiaWrapper->raiseAutomationEvent (provider, event);
|
||||
});
|
||||
}
|
||||
|
||||
void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler& handler, PROPERTYID property, VARIANT newValue)
|
||||
{
|
||||
jassert (property != PROPERTYID{});
|
||||
|
||||
getProviderWithCheckedWrapper (handler, [property, newValue] (WindowsUIAWrapper* uiaWrapper, ComSmartPtr<IRawElementProviderSimple>& provider)
|
||||
{
|
||||
VARIANT oldValue;
|
||||
VariantHelpers::clear (&oldValue);
|
||||
|
||||
uiaWrapper->raiseAutomationPropertyChangedEvent (provider, property, oldValue, newValue);
|
||||
});
|
||||
}
|
||||
|
||||
void detail::AccessibilityHelpers::notifyAccessibilityEvent (const AccessibilityHandler& handler, Event eventType)
|
||||
{
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
if (eventType == Event::elementCreated
|
||||
|| eventType == Event::elementDestroyed)
|
||||
{
|
||||
if (auto* parent = handler.getParent())
|
||||
sendAccessibilityAutomationEvent (*parent, UIA_LayoutInvalidatedEventId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType == Event::windowOpened
|
||||
|| eventType == Event::windowClosed)
|
||||
{
|
||||
if (auto* peer = handler.getComponent().getPeer())
|
||||
if ((peer->getStyleFlags() & ComponentPeer::windowHasTitleBar) == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
auto event = [eventType]() -> EVENTID
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case Event::focusChanged: return UIA_AutomationFocusChangedEventId;
|
||||
case Event::windowOpened: return UIA_Window_WindowOpenedEventId;
|
||||
case Event::windowClosed: return UIA_Window_WindowClosedEventId;
|
||||
case Event::elementCreated:
|
||||
case Event::elementDestroyed:
|
||||
case Event::elementMovedOrResized: break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}();
|
||||
|
||||
if (event != EVENTID{})
|
||||
sendAccessibilityAutomationEvent (handler, event);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
|
||||
{
|
||||
if (eventType == AccessibilityEvent::titleChanged)
|
||||
{
|
||||
VARIANT newValue;
|
||||
VariantHelpers::setString (getTitle(), &newValue);
|
||||
|
||||
sendAccessibilityPropertyChangedEvent (*this, UIA_NamePropertyId, newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType == AccessibilityEvent::valueChanged)
|
||||
{
|
||||
if (auto* valueInterface = getValueInterface())
|
||||
{
|
||||
const auto propertyType = getRole() == AccessibilityRole::slider ? UIA_RangeValueValuePropertyId
|
||||
: UIA_ValueValuePropertyId;
|
||||
|
||||
const auto value = getRole() == AccessibilityRole::slider
|
||||
? VariantHelpers::getWithValue (valueInterface->getCurrentValue())
|
||||
: VariantHelpers::getWithValue (valueInterface->getCurrentValueAsString());
|
||||
|
||||
sendAccessibilityPropertyChangedEvent (*this, propertyType, value);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto event = [eventType]() -> EVENTID
|
||||
{
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
switch (eventType)
|
||||
{
|
||||
case AccessibilityEvent::textSelectionChanged: return UIA_Text_TextSelectionChangedEventId;
|
||||
case AccessibilityEvent::textChanged: return UIA_Text_TextChangedEventId;
|
||||
case AccessibilityEvent::structureChanged: return UIA_StructureChangedEventId;
|
||||
case AccessibilityEvent::rowSelectionChanged: return UIA_SelectionItem_ElementSelectedEventId;
|
||||
case AccessibilityEvent::titleChanged:
|
||||
case AccessibilityEvent::valueChanged: break;
|
||||
}
|
||||
|
||||
return {};
|
||||
}();
|
||||
|
||||
if (event != EVENTID{})
|
||||
sendAccessibilityAutomationEvent (*this, event);
|
||||
}
|
||||
|
||||
struct SpVoiceWrapper final : public DeletedAtShutdown
|
||||
{
|
||||
SpVoiceWrapper()
|
||||
{
|
||||
[[maybe_unused]] auto hr = voice.CoCreateInstance (ComTypes::CLSID_SpVoice);
|
||||
|
||||
jassert (SUCCEEDED (hr));
|
||||
}
|
||||
|
||||
~SpVoiceWrapper() override
|
||||
{
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
ComSmartPtr<ISpVoice> voice;
|
||||
|
||||
JUCE_DECLARE_SINGLETON (SpVoiceWrapper, false)
|
||||
};
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (SpVoiceWrapper)
|
||||
|
||||
|
||||
void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority priority)
|
||||
{
|
||||
if (! areAnyAccessibilityClientsActive())
|
||||
return;
|
||||
|
||||
if (auto* sharedVoice = SpVoiceWrapper::getInstance())
|
||||
{
|
||||
auto voicePriority = [priority]
|
||||
{
|
||||
switch (priority)
|
||||
{
|
||||
case AnnouncementPriority::low: return SPVPRI_OVER;
|
||||
case AnnouncementPriority::medium: return SPVPRI_NORMAL;
|
||||
case AnnouncementPriority::high: return SPVPRI_ALERT;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return SPVPRI_OVER;
|
||||
}();
|
||||
|
||||
sharedVoice->voice->SetPriority (voicePriority);
|
||||
sharedVoice->voice->Speak (announcementString.toWideCharPointer(), SPF_ASYNC, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
namespace WindowsAccessibility
|
||||
{
|
||||
static long getUiaRootObjectId()
|
||||
{
|
||||
return static_cast<long> (UiaRootObjectId);
|
||||
}
|
||||
|
||||
static bool handleWmGetObject (AccessibilityHandler* handler, WPARAM wParam, LPARAM lParam, LRESULT* res)
|
||||
{
|
||||
if (isStartingUpOrShuttingDown() || (handler == nullptr || ! isHandlerValid (*handler)))
|
||||
return false;
|
||||
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstance())
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
|
||||
if (! uiaWrapper->isProviderDisconnecting (provider))
|
||||
*res = uiaWrapper->returnRawElementProvider ((HWND) handler->getComponent().getWindowHandle(), wParam, lParam, provider);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void revokeUIAMapEntriesForWindow (HWND hwnd)
|
||||
{
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
uiaWrapper->returnRawElementProvider (hwnd, 0, 0, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
JUCE_IMPLEMENT_SINGLETON (WindowsUIAWrapper)
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
||||
+500
@@ -0,0 +1,500 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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::ComTypes
|
||||
{
|
||||
|
||||
/*
|
||||
These interfaces would normally be included in the system platform headers.
|
||||
However, those headers are likely to be incomplete when building with
|
||||
MinGW. In order to allow building accessible applications under MinGW,
|
||||
we reproduce all necessary definitions here.
|
||||
*/
|
||||
|
||||
struct UiaPoint
|
||||
{
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
struct UiaRect
|
||||
{
|
||||
double left;
|
||||
double top;
|
||||
double width;
|
||||
double height;
|
||||
};
|
||||
|
||||
enum NavigateDirection
|
||||
{
|
||||
NavigateDirection_Parent = 0,
|
||||
NavigateDirection_NextSibling = 1,
|
||||
NavigateDirection_PreviousSibling = 2,
|
||||
NavigateDirection_FirstChild = 3,
|
||||
NavigateDirection_LastChild = 4
|
||||
};
|
||||
|
||||
enum ExpandCollapseState
|
||||
{
|
||||
ExpandCollapseState_Collapsed = 0,
|
||||
ExpandCollapseState_Expanded = 1,
|
||||
ExpandCollapseState_PartiallyExpanded = 2,
|
||||
ExpandCollapseState_LeafNode = 3
|
||||
};
|
||||
|
||||
enum TextPatternRangeEndpoint
|
||||
{
|
||||
TextPatternRangeEndpoint_Start = 0,
|
||||
TextPatternRangeEndpoint_End = 1
|
||||
};
|
||||
|
||||
enum TextUnit
|
||||
{
|
||||
TextUnit_Character = 0,
|
||||
TextUnit_Format = 1,
|
||||
TextUnit_Word = 2,
|
||||
TextUnit_Line = 3,
|
||||
TextUnit_Paragraph = 4,
|
||||
TextUnit_Page = 5,
|
||||
TextUnit_Document = 6
|
||||
};
|
||||
|
||||
enum SupportedTextSelection
|
||||
{
|
||||
SupportedTextSelection_None = 0,
|
||||
SupportedTextSelection_Single = 1,
|
||||
SupportedTextSelection_Multiple = 2
|
||||
};
|
||||
|
||||
enum CaretPosition
|
||||
{
|
||||
CaretPosition_Unknown = 0,
|
||||
CaretPosition_EndOfLine = 1,
|
||||
CaretPosition_BeginningOfLine = 2
|
||||
};
|
||||
|
||||
enum ToggleState
|
||||
{
|
||||
ToggleState_Off = 0,
|
||||
ToggleState_On = 1,
|
||||
ToggleState_Indeterminate = 2
|
||||
};
|
||||
|
||||
enum WindowVisualState
|
||||
{
|
||||
WindowVisualState_Normal = 0,
|
||||
WindowVisualState_Maximized = 1,
|
||||
WindowVisualState_Minimized = 2
|
||||
};
|
||||
|
||||
enum WindowInteractionState
|
||||
{
|
||||
WindowInteractionState_Running = 0,
|
||||
WindowInteractionState_Closing = 1,
|
||||
WindowInteractionState_ReadyForUserInteraction = 2,
|
||||
WindowInteractionState_BlockedByModalWindow = 3,
|
||||
WindowInteractionState_NotResponding = 4
|
||||
};
|
||||
|
||||
enum RowOrColumnMajor
|
||||
{
|
||||
RowOrColumnMajor_RowMajor = 0,
|
||||
RowOrColumnMajor_ColumnMajor = 1,
|
||||
RowOrColumnMajor_Indeterminate = 2
|
||||
};
|
||||
|
||||
enum ScrollAmount
|
||||
{
|
||||
ScrollAmount_LargeDecrement = 0,
|
||||
ScrollAmount_SmallDecrement = 1,
|
||||
ScrollAmount_NoAmount = 2,
|
||||
ScrollAmount_LargeIncrement = 3,
|
||||
ScrollAmount_SmallIncrement = 4
|
||||
};
|
||||
|
||||
namespace Constants
|
||||
{
|
||||
|
||||
#undef UIA_InvokePatternId
|
||||
#undef UIA_SelectionPatternId
|
||||
#undef UIA_ValuePatternId
|
||||
#undef UIA_RangeValuePatternId
|
||||
#undef UIA_ScrollPatternId
|
||||
#undef UIA_ExpandCollapsePatternId
|
||||
#undef UIA_GridPatternId
|
||||
#undef UIA_GridItemPatternId
|
||||
#undef UIA_WindowPatternId
|
||||
#undef UIA_SelectionItemPatternId
|
||||
#undef UIA_TablePatternId
|
||||
#undef UIA_TableItemPatternId
|
||||
#undef UIA_TextPatternId
|
||||
#undef UIA_TogglePatternId
|
||||
#undef UIA_TransformPatternId
|
||||
#undef UIA_ScrollItemPatternId
|
||||
#undef UIA_TextPattern2Id
|
||||
#undef UIA_StructureChangedEventId
|
||||
#undef UIA_MenuOpenedEventId
|
||||
#undef UIA_AutomationFocusChangedEventId
|
||||
#undef UIA_MenuClosedEventId
|
||||
#undef UIA_LayoutInvalidatedEventId
|
||||
#undef UIA_Invoke_InvokedEventId
|
||||
#undef UIA_SelectionItem_ElementSelectedEventId
|
||||
#undef UIA_Text_TextSelectionChangedEventId
|
||||
#undef UIA_Text_TextChangedEventId
|
||||
#undef UIA_Window_WindowOpenedEventId
|
||||
#undef UIA_Window_WindowClosedEventId
|
||||
#undef UIA_IsPeripheralPropertyId
|
||||
#undef UIA_FullDescriptionPropertyId
|
||||
#undef UIA_IsDialogPropertyId
|
||||
#undef UIA_IsReadOnlyAttributeId
|
||||
#undef UIA_CaretPositionAttributeId
|
||||
#undef UIA_ButtonControlTypeId
|
||||
#undef UIA_CheckBoxControlTypeId
|
||||
#undef UIA_ComboBoxControlTypeId
|
||||
#undef UIA_EditControlTypeId
|
||||
#undef UIA_HyperlinkControlTypeId
|
||||
#undef UIA_ImageControlTypeId
|
||||
#undef UIA_ListItemControlTypeId
|
||||
#undef UIA_ListControlTypeId
|
||||
#undef UIA_MenuBarControlTypeId
|
||||
#undef UIA_MenuItemControlTypeId
|
||||
#undef UIA_ProgressBarControlTypeId
|
||||
#undef UIA_RadioButtonControlTypeId
|
||||
#undef UIA_ScrollBarControlTypeId
|
||||
#undef UIA_SliderControlTypeId
|
||||
#undef UIA_TextControlTypeId
|
||||
#undef UIA_ToolTipControlTypeId
|
||||
#undef UIA_TreeControlTypeId
|
||||
#undef UIA_TreeItemControlTypeId
|
||||
#undef UIA_CustomControlTypeId
|
||||
#undef UIA_GroupControlTypeId
|
||||
#undef UIA_DataItemControlTypeId
|
||||
#undef UIA_WindowControlTypeId
|
||||
#undef UIA_HeaderControlTypeId
|
||||
#undef UIA_HeaderItemControlTypeId
|
||||
#undef UIA_TableControlTypeId
|
||||
|
||||
const long UIA_InvokePatternId = 10000;
|
||||
const long UIA_SelectionPatternId = 10001;
|
||||
const long UIA_ValuePatternId = 10002;
|
||||
const long UIA_RangeValuePatternId = 10003;
|
||||
const long UIA_ScrollPatternId = 10004;
|
||||
const long UIA_ExpandCollapsePatternId = 10005;
|
||||
const long UIA_GridPatternId = 10006;
|
||||
const long UIA_GridItemPatternId = 10007;
|
||||
const long UIA_WindowPatternId = 10009;
|
||||
const long UIA_SelectionItemPatternId = 10010;
|
||||
const long UIA_TablePatternId = 10012;
|
||||
const long UIA_TableItemPatternId = 10013;
|
||||
const long UIA_TextPatternId = 10014;
|
||||
const long UIA_TogglePatternId = 10015;
|
||||
const long UIA_TransformPatternId = 10016;
|
||||
const long UIA_ScrollItemPatternId = 10017;
|
||||
const long UIA_TextPattern2Id = 10024;
|
||||
const long UIA_StructureChangedEventId = 20002;
|
||||
const long UIA_MenuOpenedEventId = 20003;
|
||||
const long UIA_AutomationFocusChangedEventId = 20005;
|
||||
const long UIA_MenuClosedEventId = 20007;
|
||||
const long UIA_LayoutInvalidatedEventId = 20008;
|
||||
const long UIA_Invoke_InvokedEventId = 20009;
|
||||
const long UIA_SelectionItem_ElementSelectedEventId = 20012;
|
||||
const long UIA_Text_TextSelectionChangedEventId = 20014;
|
||||
const long UIA_Text_TextChangedEventId = 20015;
|
||||
const long UIA_Window_WindowOpenedEventId = 20016;
|
||||
const long UIA_Window_WindowClosedEventId = 20017;
|
||||
const long UIA_IsPeripheralPropertyId = 30150;
|
||||
const long UIA_FullDescriptionPropertyId = 30159;
|
||||
const long UIA_IsDialogPropertyId = 30174;
|
||||
const long UIA_IsReadOnlyAttributeId = 40015;
|
||||
const long UIA_CaretPositionAttributeId = 40038;
|
||||
const long UIA_ButtonControlTypeId = 50000;
|
||||
const long UIA_CheckBoxControlTypeId = 50002;
|
||||
const long UIA_ComboBoxControlTypeId = 50003;
|
||||
const long UIA_EditControlTypeId = 50004;
|
||||
const long UIA_HyperlinkControlTypeId = 50005;
|
||||
const long UIA_ImageControlTypeId = 50006;
|
||||
const long UIA_ListItemControlTypeId = 50007;
|
||||
const long UIA_ListControlTypeId = 50008;
|
||||
const long UIA_MenuBarControlTypeId = 50010;
|
||||
const long UIA_MenuItemControlTypeId = 50011;
|
||||
const long UIA_ProgressBarControlTypeId = 50012;
|
||||
const long UIA_RadioButtonControlTypeId = 50013;
|
||||
const long UIA_ScrollBarControlTypeId = 50014;
|
||||
const long UIA_SliderControlTypeId = 50015;
|
||||
const long UIA_TextControlTypeId = 50020;
|
||||
const long UIA_ToolTipControlTypeId = 50022;
|
||||
const long UIA_TreeControlTypeId = 50023;
|
||||
const long UIA_TreeItemControlTypeId = 50024;
|
||||
const long UIA_CustomControlTypeId = 50025;
|
||||
const long UIA_GroupControlTypeId = 50026;
|
||||
const long UIA_DataItemControlTypeId = 50029;
|
||||
const long UIA_WindowControlTypeId = 50032;
|
||||
const long UIA_HeaderControlTypeId = 50034;
|
||||
const long UIA_HeaderItemControlTypeId = 50035;
|
||||
const long UIA_TableControlTypeId = 50036;
|
||||
|
||||
} // namespace Constants
|
||||
|
||||
interface IRawElementProviderFragmentRoot;
|
||||
interface IRawElementProviderFragment;
|
||||
|
||||
JUCE_COMCLASS (IRawElementProviderFragmentRoot, "620ce2a5-ab8f-40a9-86cb-de3c75599b58") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL ElementProviderFromPoint (double x, double y, __RPC__deref_out_opt IRawElementProviderFragment** pRetVal) = 0;
|
||||
JUCE_COMCALL GetFocus (__RPC__deref_out_opt IRawElementProviderFragment * *pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IRawElementProviderFragment, "f7063da8-8359-439c-9297-bbc5299a7d87") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Navigate (NavigateDirection direction, __RPC__deref_out_opt IRawElementProviderFragment** pRetVal) = 0;
|
||||
JUCE_COMCALL GetRuntimeId (__RPC__deref_out_opt SAFEARRAY * *pRetVal) = 0;
|
||||
JUCE_COMCALL get_BoundingRectangle (__RPC__out UiaRect * pRetVal) = 0;
|
||||
JUCE_COMCALL GetEmbeddedFragmentRoots (__RPC__deref_out_opt SAFEARRAY * *pRetVal) = 0;
|
||||
JUCE_COMCALL SetFocus() = 0;
|
||||
JUCE_COMCALL get_FragmentRoot (__RPC__deref_out_opt IRawElementProviderFragmentRoot * *pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IRawElementProviderHwndOverride, "1d5df27c-8947-4425-b8d9-79787bb460b8") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetOverrideProviderForHwnd (__RPC__in HWND hwnd,
|
||||
__RPC__deref_out_opt IRawElementProviderSimple** pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IExpandCollapseProvider, "d847d3a5-cab0-4a98-8c32-ecb45c59ad24") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Expand() = 0;
|
||||
JUCE_COMCALL Collapse() = 0;
|
||||
JUCE_COMCALL get_ExpandCollapseState (__RPC__out ExpandCollapseState * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IGridItemProvider, "d02541f1-fb81-4d64-ae32-f520f8a6dbd1") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL get_Row (__RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL get_Column (__RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL get_RowSpan (__RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL get_ColumnSpan (__RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL get_ContainingGrid (__RPC__deref_out_opt IRawElementProviderSimple * *pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IGridProvider, "b17d6187-0907-464b-a168-0ef17a1572b1") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetItem (int row, int column, __RPC__deref_out_opt IRawElementProviderSimple** pRetVal) = 0;
|
||||
JUCE_COMCALL get_RowCount (__RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL get_ColumnCount (__RPC__out int* pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITableItemProvider, "b9734fa6-771f-4d78-9c90-2517999349cd") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetRowHeaderItems (SAFEARRAY** pRetVal) = 0;
|
||||
JUCE_COMCALL GetColumnHeaderItems (SAFEARRAY** pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITableProvider, "9c860395-97b3-490a-b52a-858cc22af166") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetRowHeaders (SAFEARRAY** pRetVal) = 0;
|
||||
JUCE_COMCALL GetColumnHeaders (SAFEARRAY** pRetVal) = 0;
|
||||
JUCE_COMCALL get_RowOrColumnMajor (RowOrColumnMajor* pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IInvokeProvider, "54fcb24b-e18e-47a2-b4d3-eccbe77599a2") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Invoke() = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IRangeValueProvider, "36dc7aef-33e6-4691-afe1-2be7274b3d33") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL SetValue (double val) = 0;
|
||||
JUCE_COMCALL get_Value (__RPC__out double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_IsReadOnly (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_Maximum (__RPC__out double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_Minimum (__RPC__out double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_LargeChange (__RPC__out double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_SmallChange (__RPC__out double* pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ISelectionProvider, "fb8b03af-3bdf-48d4-bd36-1a65793be168") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetSelection (__RPC__deref_out_opt SAFEARRAY * *pRetVal) = 0;
|
||||
JUCE_COMCALL get_CanSelectMultiple (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_IsSelectionRequired (__RPC__out BOOL * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ISelectionProvider2, "14f68475-ee1c-44f6-a869-d239381f0fe7") : public ISelectionProvider
|
||||
{
|
||||
JUCE_COMCALL get_FirstSelectedItem (IRawElementProviderSimple * *retVal) = 0;
|
||||
JUCE_COMCALL get_LastSelectedItem (IRawElementProviderSimple * *retVal) = 0;
|
||||
JUCE_COMCALL get_CurrentSelectedItem (IRawElementProviderSimple * *retVal) = 0;
|
||||
JUCE_COMCALL get_ItemCount (int* retVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ISelectionItemProvider, "2acad808-b2d4-452d-a407-91ff1ad167b2") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Select() = 0;
|
||||
JUCE_COMCALL AddToSelection() = 0;
|
||||
JUCE_COMCALL RemoveFromSelection() = 0;
|
||||
JUCE_COMCALL get_IsSelected (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_SelectionContainer (__RPC__deref_out_opt IRawElementProviderSimple * *pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITextRangeProvider, "5347ad7b-c355-46f8-aff5-909033582f63") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Clone (__RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
JUCE_COMCALL Compare (__RPC__in_opt ITextRangeProvider * range, __RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL CompareEndpoints (TextPatternRangeEndpoint endpoint, __RPC__in_opt ITextRangeProvider * targetRange, TextPatternRangeEndpoint targetEndpoint, __RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL ExpandToEnclosingUnit (TextUnit unit) = 0;
|
||||
JUCE_COMCALL FindAttribute (TEXTATTRIBUTEID attributeId, VARIANT val, BOOL backward, __RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
JUCE_COMCALL FindText (__RPC__in BSTR text, BOOL backward, BOOL ignoreCase, __RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
JUCE_COMCALL GetAttributeValue (TEXTATTRIBUTEID attributeId, __RPC__out VARIANT * pRetVal) = 0;
|
||||
JUCE_COMCALL GetBoundingRectangles (__RPC__deref_out_opt SAFEARRAY * *pRetVal) = 0;
|
||||
JUCE_COMCALL GetEnclosingElement (__RPC__deref_out_opt IRawElementProviderSimple * *pRetVal) = 0;
|
||||
JUCE_COMCALL GetText (int maxLength, __RPC__deref_out_opt BSTR* pRetVal) = 0;
|
||||
JUCE_COMCALL Move (TextUnit unit, int count, __RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL MoveEndpointByUnit (TextPatternRangeEndpoint endpoint, TextUnit unit, int count, __RPC__out int* pRetVal) = 0;
|
||||
JUCE_COMCALL MoveEndpointByRange (TextPatternRangeEndpoint endpoint, __RPC__in_opt ITextRangeProvider * targetRange, TextPatternRangeEndpoint targetEndpoint) = 0;
|
||||
JUCE_COMCALL Select() = 0;
|
||||
JUCE_COMCALL AddToSelection() = 0;
|
||||
JUCE_COMCALL RemoveFromSelection() = 0;
|
||||
JUCE_COMCALL ScrollIntoView (BOOL alignToTop) = 0;
|
||||
JUCE_COMCALL GetChildren (__RPC__deref_out_opt SAFEARRAY * *pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITextProvider, "3589c92c-63f3-4367-99bb-ada653b77cf2") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetSelection (__RPC__deref_out_opt SAFEARRAY * *pRetVal) = 0;
|
||||
JUCE_COMCALL GetVisibleRanges (__RPC__deref_out_opt SAFEARRAY * *pRetVal) = 0;
|
||||
JUCE_COMCALL RangeFromChild (__RPC__in_opt IRawElementProviderSimple * childElement, __RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
JUCE_COMCALL RangeFromPoint (UiaPoint point, __RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
JUCE_COMCALL get_DocumentRange (__RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
JUCE_COMCALL get_SupportedTextSelection (__RPC__out SupportedTextSelection * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITextProvider2, "0dc5e6ed-3e16-4bf1-8f9a-a979878bc195") : public ITextProvider
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL RangeFromAnnotation (__RPC__in_opt IRawElementProviderSimple * annotationElement, __RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
JUCE_COMCALL GetCaretRange (__RPC__out BOOL * isActive, __RPC__deref_out_opt ITextRangeProvider * *pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IToggleProvider, "56d00bd0-c4f4-433c-a836-1a52a57e0892") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Toggle() = 0;
|
||||
JUCE_COMCALL get_ToggleState (__RPC__out ToggleState * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITransformProvider, "6829ddc4-4f91-4ffa-b86f-bd3e2987cb4c") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Move (double x, double y) = 0;
|
||||
JUCE_COMCALL Resize (double width, double height) = 0;
|
||||
JUCE_COMCALL Rotate (double degrees) = 0;
|
||||
JUCE_COMCALL get_CanMove (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_CanResize (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_CanRotate (__RPC__out BOOL * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IValueProvider, "c7935180-6fb3-4201-b174-7df73adbf64a") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL SetValue (__RPC__in LPCWSTR val) = 0;
|
||||
JUCE_COMCALL get_Value (__RPC__deref_out_opt BSTR * pRetVal) = 0;
|
||||
JUCE_COMCALL get_IsReadOnly (__RPC__out BOOL * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IWindowProvider, "987df77b-db06-4d77-8f8a-86a9c3bb90b9") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL SetVisualState (WindowVisualState state) = 0;
|
||||
JUCE_COMCALL Close() = 0;
|
||||
JUCE_COMCALL WaitForInputIdle (int milliseconds, __RPC__out BOOL* pRetVal) = 0;
|
||||
JUCE_COMCALL get_CanMaximize (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_CanMinimize (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_IsModal (__RPC__out BOOL * pRetVal) = 0;
|
||||
JUCE_COMCALL get_WindowVisualState (__RPC__out WindowVisualState * pRetVal) = 0;
|
||||
JUCE_COMCALL get_WindowInteractionState (__RPC__out WindowInteractionState * pRetVal) = 0;
|
||||
JUCE_COMCALL get_IsTopmost (__RPC__out BOOL * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IScrollProvider, "b38b8077-1fc3-42a5-8cae-d40c2215055a") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Scroll (ScrollAmount horizontalAmount, ScrollAmount verticalAmount) = 0;
|
||||
JUCE_COMCALL SetScrollPercent (double horizontalPercent,double verticalPercent) = 0;
|
||||
JUCE_COMCALL get_HorizontalScrollPercent (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_VerticalScrollPercent (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_HorizontalViewSize (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_VerticalViewSize (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_HorizontallyScrollable (BOOL* pRetVal) = 0;
|
||||
JUCE_COMCALL get_VerticallyScrollable (BOOL* pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IScrollItemProvider, "2360c714-4bf1-4b26-ba65-9b21316127eb") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL ScrollIntoView() = 0;
|
||||
};
|
||||
|
||||
constexpr CLSID CLSID_SpVoice { 0x96749377, 0x3391, 0x11D2, { 0x9E, 0xE3, 0x00, 0xC0, 0x4F, 0x79, 0x73, 0x96 } };
|
||||
|
||||
} // namespace juce::ComTypes
|
||||
|
||||
#ifdef __CRT_UUID_DECL
|
||||
__CRT_UUID_DECL (juce::ComTypes::IRawElementProviderFragmentRoot, 0x620ce2a5, 0xab8f, 0x40a9, 0x86, 0xcb, 0xde, 0x3c, 0x75, 0x59, 0x9b, 0x58)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IRawElementProviderFragment, 0xf7063da8, 0x8359, 0x439c, 0x92, 0x97, 0xbb, 0xc5, 0x29, 0x9a, 0x7d, 0x87)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IRawElementProviderHwndOverride, 0x1d5df27c, 0x8947, 0x4425, 0xb8, 0xd9, 0x79, 0x78, 0x7b, 0xb4, 0x60, 0xb8)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IExpandCollapseProvider, 0xd847d3a5, 0xcab0, 0x4a98, 0x8c, 0x32, 0xec, 0xb4, 0x5c, 0x59, 0xad, 0x24)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IGridItemProvider, 0xd02541f1, 0xfb81, 0x4d64, 0xae, 0x32, 0xf5, 0x20, 0xf8, 0xa6, 0xdb, 0xd1)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IGridProvider, 0xb17d6187, 0x0907, 0x464b, 0xa1, 0x68, 0x0e, 0xf1, 0x7a, 0x15, 0x72, 0xb1)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IInvokeProvider, 0x54fcb24b, 0xe18e, 0x47a2, 0xb4, 0xd3, 0xec, 0xcb, 0xe7, 0x75, 0x99, 0xa2)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IRangeValueProvider, 0x36dc7aef, 0x33e6, 0x4691, 0xaf, 0xe1, 0x2b, 0xe7, 0x27, 0x4b, 0x3d, 0x33)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ISelectionProvider, 0xfb8b03af, 0x3bdf, 0x48d4, 0xbd, 0x36, 0x1a, 0x65, 0x79, 0x3b, 0xe1, 0x68)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ISelectionProvider2, 0x14f68475, 0xee1c, 0x44f6, 0xa8, 0x69, 0xd2, 0x39, 0x38, 0x1f, 0x0f, 0xe7)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ISelectionItemProvider, 0x2acad808, 0xb2d4, 0x452d, 0xa4, 0x07, 0x91, 0xff, 0x1a, 0xd1, 0x67, 0xb2)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITextRangeProvider, 0x5347ad7b, 0xc355, 0x46f8, 0xaf, 0xf5, 0x90, 0x90, 0x33, 0x58, 0x2f, 0x63)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITextProvider, 0x3589c92c, 0x63f3, 0x4367, 0x99, 0xbb, 0xad, 0xa6, 0x53, 0xb7, 0x7c, 0xf2)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITextProvider2, 0x0dc5e6ed, 0x3e16, 0x4bf1, 0x8f, 0x9a, 0xa9, 0x79, 0x87, 0x8b, 0xc1, 0x95)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IToggleProvider, 0x56d00bd0, 0xc4f4, 0x433c, 0xa8, 0x36, 0x1a, 0x52, 0xa5, 0x7e, 0x08, 0x92)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITransformProvider, 0x6829ddc4, 0x4f91, 0x4ffa, 0xb8, 0x6f, 0xbd, 0x3e, 0x29, 0x87, 0xcb, 0x4c)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IValueProvider, 0xc7935180, 0x6fb3, 0x4201, 0xb1, 0x74, 0x7d, 0xf7, 0x3a, 0xdb, 0xf6, 0x4a)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IWindowProvider, 0x987df77b, 0xdb06, 0x4d77, 0x8f, 0x8a, 0x86, 0xa9, 0xc3, 0xbb, 0x90, 0xb9)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITableItemProvider, 0xb9734fa6, 0x771f, 0x4d78, 0x9c, 0x90, 0x25, 0x17, 0x99, 0x93, 0x49, 0xcd)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITableProvider, 0x9c860395, 0x97b3, 0x490a, 0xb5, 0x2a, 0x85, 0x8c, 0xc2, 0x2a, 0xf1, 0x66)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IScrollProvider, 0xb38b8077, 0x1fc3, 0x42a5, 0x8c, 0xae, 0xd4, 0x0c, 0x22, 0x15, 0x05, 0x5a)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IScrollItemProvider, 0x2360c714, 0x4bf1, 0x4b26, 0xba, 0x65, 0x9b, 0x21, 0x31, 0x61, 0x27, 0xeb)
|
||||
#endif
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAExpandCollapseProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IExpandCollapseProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT Expand() override
|
||||
{
|
||||
return invokeShowMenu();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Collapse() override
|
||||
{
|
||||
return invokeShowMenu();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ExpandCollapseState (ComTypes::ExpandCollapseState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = getHandler().getCurrentState().isExpanded()
|
||||
? ComTypes::ExpandCollapseState_Expanded
|
||||
: ComTypes::ExpandCollapseState_Collapsed;
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_COMRESULT invokeShowMenu()
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (handler.getActions().invoke (AccessibilityActionType::showMenu))
|
||||
{
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
sendAccessibilityAutomationEvent (handler, handler.getCurrentState().isExpanded()
|
||||
? UIA_MenuOpenedEventId
|
||||
: UIA_MenuClosedEventId);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAExpandCollapseProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAGridItemProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IGridItemProvider, ComTypes::ITableItemProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT get_Row (int* pRetVal) override
|
||||
{
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getRowSpan,
|
||||
&AccessibilityTableInterface::Span::begin);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_Column (int* pRetVal) override
|
||||
{
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getColumnSpan,
|
||||
&AccessibilityTableInterface::Span::begin);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_RowSpan (int* pRetVal) override
|
||||
{
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getRowSpan,
|
||||
&AccessibilityTableInterface::Span::num);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ColumnSpan (int* pRetVal) override
|
||||
{
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getColumnSpan,
|
||||
&AccessibilityTableInterface::Span::num);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ContainingGrid (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityHandler& tableHandler)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
tableHandler.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetRowHeaderItems (SAFEARRAY**) override
|
||||
{
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetColumnHeaderItems (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityHandler& tableHandler)
|
||||
{
|
||||
if (auto* tableInterface = tableHandler.getTableInterface())
|
||||
{
|
||||
if (const auto column = tableInterface->getColumnSpan (getHandler()))
|
||||
{
|
||||
if (auto* header = tableInterface->getHeaderHandler())
|
||||
{
|
||||
const auto children = header->getChildren();
|
||||
|
||||
if (isPositiveAndBelow (column->begin, children.size()))
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
|
||||
if (auto* child = children[(size_t) column->begin])
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
if (child->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress())) == S_OK && provider != nullptr)
|
||||
{
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, 1);
|
||||
LONG index = 0;
|
||||
const auto hr = SafeArrayPutElement (*pRetVal, &index, provider);
|
||||
|
||||
return ! FAILED (hr);
|
||||
}
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
private:
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withTableInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* handler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (&getHandler(), &AccessibilityHandler::getTableInterface))
|
||||
if (handler->getTableInterface() != nullptr && callback (*handler))
|
||||
return S_OK;
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT withTableSpan (int* pRetVal,
|
||||
Optional<AccessibilityTableInterface::Span> (AccessibilityTableInterface::* getSpan) (const AccessibilityHandler&) const,
|
||||
int AccessibilityTableInterface::Span::* spanMember) const
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityHandler& handler)
|
||||
{
|
||||
if (const auto span = ((handler.getTableInterface())->*getSpan) (getHandler()))
|
||||
{
|
||||
*pRetVal = (*span).*spanMember;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAGridItemProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAGridProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IGridProvider, ComTypes::ITableProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT GetItem (int row, int column, IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityTableInterface& tableInterface)
|
||||
{
|
||||
if (! isPositiveAndBelow (row, tableInterface.getNumRows())
|
||||
|| ! isPositiveAndBelow (column, tableInterface.getNumColumns()))
|
||||
return E_INVALIDARG;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (auto* cellHandler = tableInterface.getCellHandler (row, column))
|
||||
{
|
||||
cellHandler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (auto* rowHandler = tableInterface.getRowHandler (row))
|
||||
{
|
||||
rowHandler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return E_FAIL;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_RowCount (int* pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityTableInterface& tableInterface)
|
||||
{
|
||||
*pRetVal = tableInterface.getNumRows();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ColumnCount (int* pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityTableInterface& tableInterface)
|
||||
{
|
||||
*pRetVal = tableInterface.getNumColumns();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetRowHeaders (SAFEARRAY**) override
|
||||
{
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetColumnHeaders (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityTableInterface& tableInterface)
|
||||
{
|
||||
if (auto* header = tableInterface.getHeaderHandler())
|
||||
{
|
||||
const auto children = header->getChildren();
|
||||
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, (ULONG) children.size());
|
||||
|
||||
LONG index = 0;
|
||||
|
||||
for (const auto& child : children)
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
if (child != nullptr)
|
||||
child->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (provider == nullptr)
|
||||
return E_FAIL;
|
||||
|
||||
const auto hr = SafeArrayPutElement (*pRetVal, &index, provider);
|
||||
|
||||
if (FAILED (hr))
|
||||
return E_FAIL;
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_RowOrColumnMajor (ComTypes::RowOrColumnMajor* pRetVal) override
|
||||
{
|
||||
*pRetVal = ComTypes::RowOrColumnMajor_RowMajor;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withTableInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* tableHandler = detail::AccessibilityHelpers::getEnclosingHandlerWithInterface (&getHandler(), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
return callback (*tableInterface);
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAGridProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
namespace VariantHelpers
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
template <typename Fn, typename ValueType>
|
||||
inline VARIANT getWithValueGeneric (Fn&& setter, ValueType value)
|
||||
{
|
||||
VARIANT result{};
|
||||
setter (value, &result);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
inline void clear (VARIANT* variant)
|
||||
{
|
||||
variant->vt = VT_EMPTY;
|
||||
}
|
||||
|
||||
inline void setInt (int value, VARIANT* variant)
|
||||
{
|
||||
variant->vt = VT_I4;
|
||||
variant->lVal = value;
|
||||
}
|
||||
|
||||
inline void setBool (bool value, VARIANT* variant)
|
||||
{
|
||||
variant->vt = VT_BOOL;
|
||||
variant->boolVal = value ? -1 : 0;
|
||||
}
|
||||
|
||||
inline void setString (const String& value, VARIANT* variant)
|
||||
{
|
||||
variant->vt = VT_BSTR;
|
||||
variant->bstrVal = SysAllocString ((const OLECHAR*) value.toWideCharPointer());
|
||||
}
|
||||
|
||||
inline void setDouble (double value, VARIANT* variant)
|
||||
{
|
||||
variant->vt = VT_R8;
|
||||
variant->dblVal = value;
|
||||
}
|
||||
|
||||
inline VARIANT getWithValue (double value) { return Detail::getWithValueGeneric (&setDouble, value); }
|
||||
inline VARIANT getWithValue (const String& value) { return Detail::getWithValueGeneric (&setString, value); }
|
||||
}
|
||||
|
||||
inline JUCE_COMRESULT addHandlersToArray (const std::vector<const AccessibilityHandler*>& handlers, SAFEARRAY** pRetVal)
|
||||
{
|
||||
auto numHandlers = handlers.size();
|
||||
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, (ULONG) numHandlers);
|
||||
|
||||
if (pRetVal != nullptr)
|
||||
{
|
||||
for (LONG i = 0; i < (LONG) numHandlers; ++i)
|
||||
{
|
||||
auto* handler = handlers[(size_t) i];
|
||||
|
||||
if (handler == nullptr)
|
||||
continue;
|
||||
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
auto hr = SafeArrayPutElement (*pRetVal, &i, provider);
|
||||
|
||||
if (FAILED (hr))
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
template <typename Value, typename Object, typename Callback>
|
||||
inline JUCE_COMRESULT withCheckedComArgs (Value* pRetVal, Object& handle, Callback&& callback)
|
||||
{
|
||||
if (pRetVal == nullptr)
|
||||
return E_INVALIDARG;
|
||||
|
||||
*pRetVal = Value{};
|
||||
|
||||
if (! handle.isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
return callback();
|
||||
}
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAInvokeProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IInvokeProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT Invoke() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (handler.getActions().invoke (AccessibilityActionType::press))
|
||||
{
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
if (isElementValid())
|
||||
sendAccessibilityAutomationEvent (handler, UIA_Invoke_InvokedEventId);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAInvokeProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAProviderBase
|
||||
{
|
||||
public:
|
||||
explicit UIAProviderBase (AccessibilityNativeHandle* nativeHandleIn)
|
||||
: nativeHandle (nativeHandleIn)
|
||||
{
|
||||
}
|
||||
|
||||
bool isElementValid() const
|
||||
{
|
||||
if (nativeHandle != nullptr)
|
||||
return nativeHandle->isElementValid();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const AccessibilityHandler& getHandler() const
|
||||
{
|
||||
return nativeHandle->getHandler();
|
||||
}
|
||||
|
||||
private:
|
||||
ComSmartPtr<AccessibilityNativeHandle> nativeHandle;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAProviderBase)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
void sendAccessibilityAutomationEvent (const AccessibilityHandler&, EVENTID);
|
||||
void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler&, PROPERTYID, VARIANT);
|
||||
} // namespace juce
|
||||
|
||||
#include "juce_UIAProviderBase_windows.h"
|
||||
#include "juce_UIAExpandCollapseProvider_windows.h"
|
||||
#include "juce_UIAGridItemProvider_windows.h"
|
||||
#include "juce_UIAGridProvider_windows.h"
|
||||
#include "juce_UIAInvokeProvider_windows.h"
|
||||
#include "juce_UIARangeValueProvider_windows.h"
|
||||
#include "juce_UIASelectionProvider_windows.h"
|
||||
#include "juce_UIATextProvider_windows.h"
|
||||
#include "juce_UIAToggleProvider_windows.h"
|
||||
#include "juce_UIATransformProvider_windows.h"
|
||||
#include "juce_UIAValueProvider_windows.h"
|
||||
#include "juce_UIAWindowProvider_windows.h"
|
||||
Vendored
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIARangeValueProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IRangeValueProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT SetValue (double val) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (auto* valueInterface = handler.getValueInterface())
|
||||
{
|
||||
auto range = valueInterface->getRange();
|
||||
|
||||
if (range.isValid())
|
||||
{
|
||||
if (val < range.getMinimumValue() || val > range.getMaximumValue())
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (! valueInterface->isReadOnly())
|
||||
{
|
||||
valueInterface->setValue (val);
|
||||
|
||||
VARIANT newValue;
|
||||
VariantHelpers::setDouble (valueInterface->getCurrentValue(), &newValue);
|
||||
sendAccessibilityPropertyChangedEvent (handler, UIA_RangeValueValuePropertyId, newValue);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_Value (double* pRetVal) override
|
||||
{
|
||||
return withValueInterface (pRetVal, [] (const AccessibilityValueInterface& valueInterface)
|
||||
{
|
||||
return valueInterface.getCurrentValue();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_IsReadOnly (BOOL* pRetVal) override
|
||||
{
|
||||
return withValueInterface (pRetVal, [] (const AccessibilityValueInterface& valueInterface)
|
||||
{
|
||||
return valueInterface.isReadOnly();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_Maximum (double* pRetVal) override
|
||||
{
|
||||
return withValueInterface (pRetVal, [] (const AccessibilityValueInterface& valueInterface)
|
||||
{
|
||||
return valueInterface.getRange().getMaximumValue();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_Minimum (double* pRetVal) override
|
||||
{
|
||||
return withValueInterface (pRetVal, [] (const AccessibilityValueInterface& valueInterface)
|
||||
{
|
||||
return valueInterface.getRange().getMinimumValue();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_LargeChange (double* pRetVal) override
|
||||
{
|
||||
return get_SmallChange (pRetVal);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_SmallChange (double* pRetVal) override
|
||||
{
|
||||
return withValueInterface (pRetVal, [] (const AccessibilityValueInterface& valueInterface)
|
||||
{
|
||||
return valueInterface.getRange().getInterval();
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withValueInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* valueInterface = getHandler().getValueInterface())
|
||||
{
|
||||
if (valueInterface->getRange().isValid())
|
||||
{
|
||||
*pRetVal = callback (*valueInterface);
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIARangeValueProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+247
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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")
|
||||
|
||||
//==============================================================================
|
||||
class UIASelectionItemProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::ISelectionItemProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIASelectionItemProvider (AccessibilityNativeHandle* handle)
|
||||
: UIAProviderBase (handle),
|
||||
isRadioButton (getHandler().getRole() == AccessibilityRole::radioButton)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AddToSelection() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (isRadioButton)
|
||||
{
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
handler.getActions().invoke (AccessibilityActionType::press);
|
||||
sendAccessibilityAutomationEvent (handler, UIA_SelectionItem_ElementSelectedEventId);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
handler.getActions().invoke (AccessibilityActionType::toggle);
|
||||
handler.getActions().invoke (AccessibilityActionType::press);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_IsSelected (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
const auto state = getHandler().getCurrentState();
|
||||
*pRetVal = isRadioButton ? state.isChecked() : state.isSelected();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_SelectionContainer (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (! isRadioButton)
|
||||
if (auto* parent = getHandler().getParent())
|
||||
parent->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT RemoveFromSelection() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (! isRadioButton)
|
||||
{
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (handler.getCurrentState().isSelected())
|
||||
getHandler().getActions().invoke (AccessibilityActionType::toggle);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Select() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
AddToSelection();
|
||||
|
||||
if (isElementValid() && ! isRadioButton)
|
||||
{
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (auto* parent = handler.getParent())
|
||||
for (auto* child : parent->getChildren())
|
||||
if (child != &handler && child->getCurrentState().isSelected())
|
||||
child->getActions().invoke (AccessibilityActionType::toggle);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
const bool isRadioButton;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIASelectionItemProvider)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class UIASelectionProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::ISelectionProvider2>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT QueryInterface (REFIID iid, void** result) override
|
||||
{
|
||||
if (iid == __uuidof (IUnknown) || iid == __uuidof (ComTypes::ISelectionProvider))
|
||||
return castToType<ComTypes::ISelectionProvider> (result);
|
||||
|
||||
if (iid == __uuidof (ComTypes::ISelectionProvider2))
|
||||
return castToType<ComTypes::ISelectionProvider2> (result);
|
||||
|
||||
*result = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT get_CanSelectMultiple (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = isMultiSelectable();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_IsSelectionRequired (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = getSelectedChildren().size() > 0 && ! isMultiSelectable();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetSelection (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
return addHandlersToArray (getSelectedChildren(), pRetVal);
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT get_FirstSelectedItem (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto selectedChildren = getSelectedChildren();
|
||||
|
||||
if (! selectedChildren.empty())
|
||||
selectedChildren.front()->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_LastSelectedItem (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto selectedChildren = getSelectedChildren();
|
||||
|
||||
if (! selectedChildren.empty())
|
||||
selectedChildren.back()->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_CurrentSelectedItem (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
get_FirstSelectedItem (pRetVal);
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ItemCount (int* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = (int) getSelectedChildren().size();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
bool isMultiSelectable() const noexcept
|
||||
{
|
||||
return getHandler().getCurrentState().isMultiSelectable();
|
||||
}
|
||||
|
||||
std::vector<const AccessibilityHandler*> getSelectedChildren() const
|
||||
{
|
||||
std::vector<const AccessibilityHandler*> selectedHandlers;
|
||||
|
||||
for (auto* child : getHandler().getComponent().getChildren())
|
||||
if (auto* handler = child->getAccessibilityHandler())
|
||||
if (handler->getCurrentState().isSelected())
|
||||
selectedHandlers.push_back (handler);
|
||||
|
||||
return selectedHandlers;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIASelectionProvider)
|
||||
};
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+619
@@ -0,0 +1,619 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIATextProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::ITextProvider2>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT QueryInterface (REFIID iid, void** result) override
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (iid == __uuidof (IUnknown) || iid == __uuidof (ComTypes::ITextProvider))
|
||||
return castToType<ComTypes::ITextProvider> (result);
|
||||
|
||||
if (iid == __uuidof (ComTypes::ITextProvider2))
|
||||
return castToType<ComTypes::ITextProvider2> (result);
|
||||
|
||||
*result = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT get_DocumentRange (ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
*pRetVal = new UIATextRangeProvider (*this, { 0, textInterface.getTotalNumCharacters() });
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_SupportedTextSelection (ComTypes::SupportedTextSelection* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = ComTypes::SupportedTextSelection_Single;
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetSelection (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, 1);
|
||||
|
||||
if (pRetVal != nullptr)
|
||||
{
|
||||
auto selection = textInterface.getSelection();
|
||||
auto hasSelection = ! selection.isEmpty();
|
||||
auto cursorPos = textInterface.getTextInsertionOffset();
|
||||
|
||||
auto* rangeProvider = new UIATextRangeProvider (*this,
|
||||
{ hasSelection ? selection.getStart() : cursorPos,
|
||||
hasSelection ? selection.getEnd() : cursorPos });
|
||||
|
||||
LONG pos = 0;
|
||||
auto hr = SafeArrayPutElement (*pRetVal, &pos, static_cast<IUnknown*> (rangeProvider));
|
||||
|
||||
if (FAILED (hr))
|
||||
return E_FAIL;
|
||||
|
||||
rangeProvider->Release();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetVisibleRanges (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, 1);
|
||||
|
||||
if (pRetVal != nullptr)
|
||||
{
|
||||
auto* rangeProvider = new UIATextRangeProvider (*this, { 0, textInterface.getTotalNumCharacters() });
|
||||
|
||||
LONG pos = 0;
|
||||
auto hr = SafeArrayPutElement (*pRetVal, &pos, static_cast<IUnknown*> (rangeProvider));
|
||||
|
||||
if (FAILED (hr))
|
||||
return E_FAIL;
|
||||
|
||||
rangeProvider->Release();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT RangeFromChild (IRawElementProviderSimple*, ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, []
|
||||
{
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT RangeFromPoint (ComTypes::UiaPoint point, ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
auto offset = textInterface.getOffsetAtPoint ({ roundToInt (point.x), roundToInt (point.y) });
|
||||
|
||||
if (offset > 0)
|
||||
*pRetVal = new UIATextRangeProvider (*this, { offset, offset });
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT GetCaretRange (BOOL* isActive, ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
*isActive = getHandler().hasFocus (false);
|
||||
|
||||
auto cursorPos = textInterface.getTextInsertionOffset();
|
||||
*pRetVal = new UIATextRangeProvider (*this, { cursorPos, cursorPos });
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT RangeFromAnnotation (IRawElementProviderSimple*, ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, []
|
||||
{
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withTextInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* textInterface = getHandler().getTextInterface())
|
||||
return callback (*textInterface);
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class UIATextRangeProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::ITextRangeProvider>
|
||||
{
|
||||
public:
|
||||
UIATextRangeProvider (UIATextProvider& textProvider, Range<int> range)
|
||||
: UIAProviderBase (textProvider.getHandler().getNativeImplementation()),
|
||||
owner (&textProvider),
|
||||
selectionRange (range)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
Range<int> getSelectionRange() const noexcept { return selectionRange; }
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AddToSelection() override
|
||||
{
|
||||
return Select();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Clone (ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = new UIATextRangeProvider (*owner, selectionRange);
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Compare (ComTypes::ITextRangeProvider* range, BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = (selectionRange == static_cast<UIATextRangeProvider*> (range)->getSelectionRange());
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT CompareEndpoints (ComTypes::TextPatternRangeEndpoint endpoint,
|
||||
ComTypes::ITextRangeProvider* targetRange,
|
||||
ComTypes::TextPatternRangeEndpoint targetEndpoint,
|
||||
int* pRetVal) override
|
||||
{
|
||||
if (targetRange == nullptr)
|
||||
return E_INVALIDARG;
|
||||
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto offset = (endpoint == ComTypes::TextPatternRangeEndpoint_Start ? selectionRange.getStart()
|
||||
: selectionRange.getEnd());
|
||||
|
||||
auto otherRange = static_cast<UIATextRangeProvider*> (targetRange)->getSelectionRange();
|
||||
auto otherOffset = (targetEndpoint == ComTypes::TextPatternRangeEndpoint_Start ? otherRange.getStart()
|
||||
: otherRange.getEnd());
|
||||
|
||||
*pRetVal = offset - otherOffset;
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT ExpandToEnclosingUnit (ComTypes::TextUnit unit) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* textInterface = owner->getHandler().getTextInterface())
|
||||
{
|
||||
using ATH = AccessibilityTextHelpers;
|
||||
|
||||
const auto boundaryType = getBoundaryType (unit);
|
||||
const auto start = ATH::findTextBoundary (*textInterface,
|
||||
selectionRange.getStart(),
|
||||
boundaryType,
|
||||
ATH::Direction::backwards,
|
||||
ATH::IncludeThisBoundary::yes,
|
||||
ATH::IncludeWhitespaceAfterWords::no);
|
||||
|
||||
const auto end = ATH::findTextBoundary (*textInterface,
|
||||
start,
|
||||
boundaryType,
|
||||
ATH::Direction::forwards,
|
||||
ATH::IncludeThisBoundary::no,
|
||||
ATH::IncludeWhitespaceAfterWords::yes);
|
||||
|
||||
selectionRange = Range<int> (start, end);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT FindAttribute (TEXTATTRIBUTEID, VARIANT, BOOL, ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, []
|
||||
{
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT FindText (BSTR text, BOOL backward, BOOL ignoreCase,
|
||||
ComTypes::ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
auto selectionText = textInterface.getText (selectionRange);
|
||||
String textToSearchFor (text);
|
||||
|
||||
auto offset = (backward ? (ignoreCase ? selectionText.lastIndexOfIgnoreCase (textToSearchFor) : selectionText.lastIndexOf (textToSearchFor))
|
||||
: (ignoreCase ? selectionText.indexOfIgnoreCase (textToSearchFor) : selectionText.indexOf (textToSearchFor)));
|
||||
|
||||
if (offset != -1)
|
||||
*pRetVal = new UIATextRangeProvider (*owner, { offset, offset + textToSearchFor.length() });
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetAttributeValue (TEXTATTRIBUTEID attributeId, VARIANT* pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
VariantHelpers::clear (pRetVal);
|
||||
|
||||
using namespace ComTypes::Constants;
|
||||
|
||||
switch (attributeId)
|
||||
{
|
||||
case UIA_IsReadOnlyAttributeId:
|
||||
{
|
||||
VariantHelpers::setBool (textInterface.isReadOnly(), pRetVal);
|
||||
break;
|
||||
}
|
||||
|
||||
case UIA_CaretPositionAttributeId:
|
||||
{
|
||||
auto cursorPos = textInterface.getTextInsertionOffset();
|
||||
|
||||
auto caretPos = [&]
|
||||
{
|
||||
if (cursorPos == 0)
|
||||
return ComTypes::CaretPosition_BeginningOfLine;
|
||||
|
||||
if (cursorPos == textInterface.getTotalNumCharacters())
|
||||
return ComTypes::CaretPosition_EndOfLine;
|
||||
|
||||
return ComTypes::CaretPosition_Unknown;
|
||||
}();
|
||||
|
||||
VariantHelpers::setInt (caretPos, pRetVal);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetBoundingRectangles (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
auto rectangleList = textInterface.getTextBounds (selectionRange);
|
||||
auto numRectangles = rectangleList.getNumRectangles();
|
||||
|
||||
*pRetVal = SafeArrayCreateVector (VT_R8, 0, 4 * (ULONG) numRectangles);
|
||||
|
||||
if (*pRetVal == nullptr)
|
||||
return E_FAIL;
|
||||
|
||||
if (numRectangles > 0)
|
||||
{
|
||||
double* doubleArr = nullptr;
|
||||
|
||||
if (FAILED (SafeArrayAccessData (*pRetVal, reinterpret_cast<void**> (&doubleArr))))
|
||||
{
|
||||
SafeArrayDestroy (*pRetVal);
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < numRectangles; ++i)
|
||||
{
|
||||
auto r = Desktop::getInstance().getDisplays().logicalToPhysical (rectangleList.getRectangle (i));
|
||||
|
||||
doubleArr[i * 4] = r.getX();
|
||||
doubleArr[i * 4 + 1] = r.getY();
|
||||
doubleArr[i * 4 + 2] = r.getWidth();
|
||||
doubleArr[i * 4 + 3] = r.getHeight();
|
||||
}
|
||||
|
||||
if (FAILED (SafeArrayUnaccessData (*pRetVal)))
|
||||
{
|
||||
SafeArrayDestroy (*pRetVal);
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetChildren (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, 0);
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetEnclosingElement (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
getHandler().getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
return S_OK;
|
||||
});
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetText (int maxLength, BSTR* pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
auto text = textInterface.getText (selectionRange);
|
||||
|
||||
if (maxLength >= 0 && text.length() > maxLength)
|
||||
text = text.substring (0, maxLength);
|
||||
|
||||
*pRetVal = SysAllocString ((const OLECHAR*) text.toWideCharPointer());
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Move (ComTypes::TextUnit unit, int count, int* pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
using ATH = AccessibilityTextHelpers;
|
||||
|
||||
const auto boundaryType = getBoundaryType (unit);
|
||||
const auto previousUnitBoundary = ATH::findTextBoundary (textInterface,
|
||||
selectionRange.getStart(),
|
||||
boundaryType,
|
||||
ATH::Direction::backwards,
|
||||
ATH::IncludeThisBoundary::yes,
|
||||
ATH::IncludeWhitespaceAfterWords::no);
|
||||
|
||||
auto numMoved = 0;
|
||||
auto movedEndpoint = previousUnitBoundary;
|
||||
|
||||
for (; numMoved < std::abs (count); ++numMoved)
|
||||
{
|
||||
const auto nextEndpoint = ATH::findTextBoundary (textInterface,
|
||||
movedEndpoint,
|
||||
boundaryType,
|
||||
count > 0 ? ATH::Direction::forwards : ATH::Direction::backwards,
|
||||
ATH::IncludeThisBoundary::no,
|
||||
count > 0 ? ATH::IncludeWhitespaceAfterWords::yes : ATH::IncludeWhitespaceAfterWords::no);
|
||||
|
||||
if (nextEndpoint == movedEndpoint)
|
||||
break;
|
||||
|
||||
movedEndpoint = nextEndpoint;
|
||||
}
|
||||
|
||||
*pRetVal = numMoved;
|
||||
|
||||
ExpandToEnclosingUnit (unit);
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT MoveEndpointByRange (ComTypes::TextPatternRangeEndpoint endpoint,
|
||||
ComTypes::ITextRangeProvider* targetRange,
|
||||
ComTypes::TextPatternRangeEndpoint targetEndpoint) override
|
||||
{
|
||||
if (targetRange == nullptr)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (owner->getHandler().getTextInterface() != nullptr)
|
||||
{
|
||||
auto otherRange = static_cast<UIATextRangeProvider*> (targetRange)->getSelectionRange();
|
||||
auto targetPoint = (targetEndpoint == ComTypes::TextPatternRangeEndpoint_Start ? otherRange.getStart()
|
||||
: otherRange.getEnd());
|
||||
|
||||
setEndpointChecked (endpoint, targetPoint);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT MoveEndpointByUnit (ComTypes::TextPatternRangeEndpoint endpoint,
|
||||
ComTypes::TextUnit unit,
|
||||
int count,
|
||||
int* pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
if (count == 0 || textInterface.getTotalNumCharacters() == 0)
|
||||
return S_OK;
|
||||
|
||||
const auto endpointToMove = (endpoint == ComTypes::TextPatternRangeEndpoint_Start ? selectionRange.getStart()
|
||||
: selectionRange.getEnd());
|
||||
|
||||
using ATH = AccessibilityTextHelpers;
|
||||
|
||||
const auto direction = (count > 0 ? ATH::Direction::forwards
|
||||
: ATH::Direction::backwards);
|
||||
|
||||
const auto boundaryType = getBoundaryType (unit);
|
||||
auto movedEndpoint = endpointToMove;
|
||||
|
||||
int numMoved = 0;
|
||||
for (; numMoved < std::abs (count); ++numMoved)
|
||||
{
|
||||
auto nextEndpoint = ATH::findTextBoundary (textInterface,
|
||||
movedEndpoint,
|
||||
boundaryType,
|
||||
direction,
|
||||
ATH::IncludeThisBoundary::no,
|
||||
direction == ATH::Direction::forwards ? ATH::IncludeWhitespaceAfterWords::yes
|
||||
: ATH::IncludeWhitespaceAfterWords::no);
|
||||
|
||||
if (nextEndpoint == movedEndpoint)
|
||||
break;
|
||||
|
||||
movedEndpoint = nextEndpoint;
|
||||
}
|
||||
|
||||
*pRetVal = numMoved;
|
||||
|
||||
setEndpointChecked (endpoint, movedEndpoint);
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT RemoveFromSelection() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* textInterface = owner->getHandler().getTextInterface())
|
||||
{
|
||||
textInterface->setSelection ({});
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT ScrollIntoView (BOOL) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Select() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* textInterface = owner->getHandler().getTextInterface())
|
||||
{
|
||||
textInterface->setSelection ({});
|
||||
textInterface->setSelection (selectionRange);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
private:
|
||||
static AccessibilityTextHelpers::BoundaryType getBoundaryType (ComTypes::TextUnit unit)
|
||||
{
|
||||
switch (unit)
|
||||
{
|
||||
case ComTypes::TextUnit_Character:
|
||||
return AccessibilityTextHelpers::BoundaryType::character;
|
||||
|
||||
case ComTypes::TextUnit_Format:
|
||||
case ComTypes::TextUnit_Word:
|
||||
return AccessibilityTextHelpers::BoundaryType::word;
|
||||
|
||||
case ComTypes::TextUnit_Line:
|
||||
return AccessibilityTextHelpers::BoundaryType::line;
|
||||
|
||||
case ComTypes::TextUnit_Paragraph:
|
||||
case ComTypes::TextUnit_Page:
|
||||
case ComTypes::TextUnit_Document:
|
||||
return AccessibilityTextHelpers::BoundaryType::document;
|
||||
};
|
||||
|
||||
jassertfalse;
|
||||
return AccessibilityTextHelpers::BoundaryType::character;
|
||||
}
|
||||
|
||||
void setEndpointChecked (ComTypes::TextPatternRangeEndpoint endpoint, int newEndpoint)
|
||||
{
|
||||
if (endpoint == ComTypes::TextPatternRangeEndpoint_Start)
|
||||
{
|
||||
if (selectionRange.getEnd() < newEndpoint)
|
||||
selectionRange.setEnd (newEndpoint);
|
||||
|
||||
selectionRange.setStart (newEndpoint);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectionRange.getStart() > newEndpoint)
|
||||
selectionRange.setStart (newEndpoint);
|
||||
|
||||
selectionRange.setEnd (newEndpoint);
|
||||
}
|
||||
}
|
||||
|
||||
ComSmartPtr<UIATextProvider> owner;
|
||||
Range<int> selectionRange;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIATextRangeProvider)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIATextProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAToggleProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IToggleProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT Toggle() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (handler.getActions().invoke (AccessibilityActionType::toggle)
|
||||
|| handler.getActions().invoke (AccessibilityActionType::press))
|
||||
{
|
||||
VARIANT newValue;
|
||||
VariantHelpers::setInt (getCurrentToggleState(), &newValue);
|
||||
|
||||
sendAccessibilityPropertyChangedEvent (handler, UIA_ToggleToggleStatePropertyId, newValue);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ToggleState (ComTypes::ToggleState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = getCurrentToggleState();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ComTypes::ToggleState getCurrentToggleState() const
|
||||
{
|
||||
return getHandler().getCurrentState().isChecked() ? ComTypes::ToggleState_On
|
||||
: ComTypes::ToggleState_Off;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAToggleProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+122
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIATransformProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::ITransformProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT Move (double x, double y) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
RECT rect;
|
||||
GetWindowRect ((HWND) peer->getNativeHandle(), &rect);
|
||||
|
||||
rect.left = roundToInt (x);
|
||||
rect.top = roundToInt (y);
|
||||
|
||||
auto bounds = Rectangle<int>::leftTopRightBottom (rect.left, rect.top, rect.right, rect.bottom);
|
||||
|
||||
peer->setBounds (Desktop::getInstance().getDisplays().physicalToLogical (bounds),
|
||||
peer->isFullScreen());
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Resize (double width, double height) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
auto scale = peer->getPlatformScaleFactor();
|
||||
|
||||
peer->getComponent().setSize (roundToInt (width / scale),
|
||||
roundToInt (height / scale));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Rotate (double) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_CanMove (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = true;
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_CanResize (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
*pRetVal = ((peer->getStyleFlags() & ComponentPeer::windowIsResizable) != 0);
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_CanRotate (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = false;
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ComponentPeer* getPeer() const
|
||||
{
|
||||
return getHandler().getComponent().getPeer();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIATransformProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+83
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAValueProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IValueProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT SetValue (LPCWSTR val) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
auto& valueInterface = *handler.getValueInterface();
|
||||
|
||||
if (valueInterface.isReadOnly())
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
|
||||
valueInterface.setValueAsString (String (val));
|
||||
|
||||
VARIANT newValue;
|
||||
VariantHelpers::setString (valueInterface.getCurrentValueAsString(), &newValue);
|
||||
|
||||
sendAccessibilityPropertyChangedEvent (handler, UIA_ValueValuePropertyId, newValue);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_Value (BSTR* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto currentValueString = getHandler().getValueInterface()->getCurrentValueAsString();
|
||||
|
||||
*pRetVal = SysAllocString ((const OLECHAR*) currentValueString.toWideCharPointer());
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_IsReadOnly (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = getHandler().getValueInterface()->isReadOnly();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAValueProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAWindowProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IWindowProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT SetVisualState (ComTypes::WindowVisualState state) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case ComTypes::WindowVisualState_Maximized:
|
||||
peer->setFullScreen (true);
|
||||
break;
|
||||
|
||||
case ComTypes::WindowVisualState_Minimized:
|
||||
peer->setMinimised (true);
|
||||
break;
|
||||
|
||||
case ComTypes::WindowVisualState_Normal:
|
||||
peer->setFullScreen (false);
|
||||
peer->setMinimised (false);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Close() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
peer->handleUserClosingWindow();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT WaitForInputIdle (int, BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, []
|
||||
{
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_CanMaximize (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
*pRetVal = (peer->getStyleFlags() & ComponentPeer::windowHasMaximiseButton) != 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_CanMinimize (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
*pRetVal = (peer->getStyleFlags() & ComponentPeer::windowHasMinimiseButton) != 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_IsModal (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
*pRetVal = peer->getComponent().isCurrentlyModal();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_WindowVisualState (ComTypes::WindowVisualState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
if (peer->isFullScreen())
|
||||
*pRetVal = ComTypes::WindowVisualState_Maximized;
|
||||
else if (peer->isMinimised())
|
||||
*pRetVal = ComTypes::WindowVisualState_Minimized;
|
||||
else
|
||||
*pRetVal = ComTypes::WindowVisualState_Normal;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_WindowInteractionState (ComTypes::WindowInteractionState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
*pRetVal = peer->getComponent().isCurrentlyBlockedByAnotherModalComponent()
|
||||
? ComTypes::WindowInteractionState::WindowInteractionState_BlockedByModalWindow
|
||||
: ComTypes::WindowInteractionState::WindowInteractionState_Running;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_IsTopmost (BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
*pRetVal = peer->isFocused();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ComponentPeer* getPeer() const
|
||||
{
|
||||
return getHandler().getComponent().getPeer();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAWindowProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Vendored
+160
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2022 - 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 7 End-User License
|
||||
Agreement and JUCE Privacy Policy.
|
||||
|
||||
End User License Agreement: www.juce.com/juce-7-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
|
||||
{
|
||||
|
||||
class WindowsUIAWrapper : public DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
bool isLoaded() const noexcept
|
||||
{
|
||||
return uiaReturnRawElementProvider != nullptr
|
||||
&& uiaHostProviderFromHwnd != nullptr
|
||||
&& uiaRaiseAutomationPropertyChangedEvent != nullptr
|
||||
&& uiaRaiseAutomationEvent != nullptr
|
||||
&& uiaClientsAreListening != nullptr
|
||||
&& uiaDisconnectProvider != nullptr
|
||||
&& uiaDisconnectAllProviders != nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
LRESULT returnRawElementProvider (HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple* provider)
|
||||
{
|
||||
return uiaReturnRawElementProvider != nullptr ? uiaReturnRawElementProvider (hwnd, wParam, lParam, provider)
|
||||
: (LRESULT) nullptr;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT hostProviderFromHwnd (HWND hwnd, IRawElementProviderSimple** provider)
|
||||
{
|
||||
return uiaHostProviderFromHwnd != nullptr ? uiaHostProviderFromHwnd (hwnd, provider)
|
||||
: (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT raiseAutomationPropertyChangedEvent (IRawElementProviderSimple* provider, PROPERTYID propID, VARIANT oldValue, VARIANT newValue)
|
||||
{
|
||||
return uiaRaiseAutomationPropertyChangedEvent != nullptr ? uiaRaiseAutomationPropertyChangedEvent (provider, propID, oldValue, newValue)
|
||||
: (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT raiseAutomationEvent (IRawElementProviderSimple* provider, EVENTID eventID)
|
||||
{
|
||||
return uiaRaiseAutomationEvent != nullptr ? uiaRaiseAutomationEvent (provider, eventID)
|
||||
: (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
BOOL clientsAreListening()
|
||||
{
|
||||
return uiaClientsAreListening != nullptr ? uiaClientsAreListening()
|
||||
: false;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT disconnectProvider (IRawElementProviderSimple* provider)
|
||||
{
|
||||
if (uiaDisconnectProvider != nullptr)
|
||||
{
|
||||
const ScopedValueSetter<IRawElementProviderSimple*> disconnectingProviderSetter (disconnectingProvider, provider);
|
||||
return uiaDisconnectProvider (provider);
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT disconnectAllProviders()
|
||||
{
|
||||
if (uiaDisconnectAllProviders != nullptr)
|
||||
{
|
||||
const ScopedValueSetter<bool> disconnectingAllProvidersSetter (disconnectingAllProviders, true);
|
||||
return uiaDisconnectAllProviders();
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
bool isProviderDisconnecting (IRawElementProviderSimple* provider)
|
||||
{
|
||||
return disconnectingProvider == provider || disconnectingAllProviders;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (WindowsUIAWrapper)
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
WindowsUIAWrapper()
|
||||
{
|
||||
// force UIA COM library initialisation here to prevent an exception when calling methods from SendMessage()
|
||||
if (isLoaded())
|
||||
returnRawElementProvider (nullptr, 0, 0, nullptr);
|
||||
else
|
||||
jassertfalse; // UIAutomationCore could not be loaded!
|
||||
}
|
||||
|
||||
~WindowsUIAWrapper()
|
||||
{
|
||||
disconnectAllProviders();
|
||||
|
||||
if (uiaHandle != nullptr)
|
||||
::FreeLibrary (uiaHandle);
|
||||
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
template <typename FuncType>
|
||||
static FuncType getUiaFunction (HMODULE module, LPCSTR funcName)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wcast-function-type")
|
||||
return (FuncType) GetProcAddress (module, funcName);
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
using UiaReturnRawElementProviderFunc = LRESULT (WINAPI*) (HWND, WPARAM, LPARAM, IRawElementProviderSimple*);
|
||||
using UiaHostProviderFromHwndFunc = HRESULT (WINAPI*) (HWND, IRawElementProviderSimple**);
|
||||
using UiaRaiseAutomationPropertyChangedEventFunc = HRESULT (WINAPI*) (IRawElementProviderSimple*, PROPERTYID, VARIANT, VARIANT);
|
||||
using UiaRaiseAutomationEventFunc = HRESULT (WINAPI*) (IRawElementProviderSimple*, EVENTID);
|
||||
using UiaClientsAreListeningFunc = BOOL (WINAPI*) ();
|
||||
using UiaDisconnectProviderFunc = HRESULT (WINAPI*) (IRawElementProviderSimple*);
|
||||
using UiaDisconnectAllProvidersFunc = HRESULT (WINAPI*) ();
|
||||
|
||||
HMODULE uiaHandle = ::LoadLibraryA ("UIAutomationCore.dll");
|
||||
UiaReturnRawElementProviderFunc uiaReturnRawElementProvider = getUiaFunction<UiaReturnRawElementProviderFunc> (uiaHandle, "UiaReturnRawElementProvider");
|
||||
UiaHostProviderFromHwndFunc uiaHostProviderFromHwnd = getUiaFunction<UiaHostProviderFromHwndFunc> (uiaHandle, "UiaHostProviderFromHwnd");
|
||||
UiaRaiseAutomationPropertyChangedEventFunc uiaRaiseAutomationPropertyChangedEvent = getUiaFunction<UiaRaiseAutomationPropertyChangedEventFunc> (uiaHandle, "UiaRaiseAutomationPropertyChangedEvent");
|
||||
UiaRaiseAutomationEventFunc uiaRaiseAutomationEvent = getUiaFunction<UiaRaiseAutomationEventFunc> (uiaHandle, "UiaRaiseAutomationEvent");
|
||||
UiaClientsAreListeningFunc uiaClientsAreListening = getUiaFunction<UiaClientsAreListeningFunc> (uiaHandle, "UiaClientsAreListening");
|
||||
UiaDisconnectProviderFunc uiaDisconnectProvider = getUiaFunction<UiaDisconnectProviderFunc> (uiaHandle, "UiaDisconnectProvider");
|
||||
UiaDisconnectAllProvidersFunc uiaDisconnectAllProviders = getUiaFunction<UiaDisconnectAllProvidersFunc> (uiaHandle, "UiaDisconnectAllProviders");
|
||||
|
||||
IRawElementProviderSimple* disconnectingProvider = nullptr;
|
||||
bool disconnectingAllProviders = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (WindowsUIAWrapper)
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
Reference in New Issue
Block a user