git subrepo clone --branch=sono6good https://github.com/essej/JUCE.git deps/juce
subrepo: subdir: "deps/juce" merged: "b13f9084e" upstream: origin: "https://github.com/essej/JUCE.git" branch: "sono6good" commit: "b13f9084e" git-subrepo: version: "0.4.3" origin: "https://github.com/ingydotnet/git-subrepo.git" commit: "2f68596"
This commit is contained in:
111
deps/juce/modules/juce_gui_basics/native/accessibility/juce_AccessibilityTextHelpers.h
vendored
Normal file
111
deps/juce/modules/juce_gui_basics/native/accessibility/juce_AccessibilityTextHelpers.h
vendored
Normal file
@ -0,0 +1,111 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace AccessibilityTextHelpers
|
||||
{
|
||||
enum class BoundaryType
|
||||
{
|
||||
character,
|
||||
word,
|
||||
line,
|
||||
document
|
||||
};
|
||||
|
||||
enum class Direction
|
||||
{
|
||||
forwards,
|
||||
backwards
|
||||
};
|
||||
|
||||
static int findTextBoundary (const AccessibilityTextInterface& textInterface,
|
||||
int currentPosition,
|
||||
BoundaryType boundary,
|
||||
Direction direction)
|
||||
{
|
||||
const auto numCharacters = textInterface.getTotalNumCharacters();
|
||||
const auto isForwards = (direction == Direction::forwards);
|
||||
|
||||
auto offsetWithDirecton = [isForwards] (int input) { return isForwards ? input : -input; };
|
||||
|
||||
switch (boundary)
|
||||
{
|
||||
case BoundaryType::character:
|
||||
return jlimit (0, numCharacters, currentPosition + offsetWithDirecton (1));
|
||||
|
||||
case BoundaryType::word:
|
||||
case BoundaryType::line:
|
||||
{
|
||||
const auto text = [&]() -> String
|
||||
{
|
||||
if (isForwards)
|
||||
return textInterface.getText ({ currentPosition, textInterface.getTotalNumCharacters() });
|
||||
|
||||
const auto str = textInterface.getText ({ 0, currentPosition });
|
||||
|
||||
auto start = str.getCharPointer();
|
||||
auto end = start.findTerminatingNull();
|
||||
const auto size = getAddressDifference (end.getAddress(), start.getAddress());
|
||||
|
||||
String reversed;
|
||||
|
||||
if (size > 0)
|
||||
{
|
||||
reversed.preallocateBytes ((size_t) size);
|
||||
|
||||
auto destPtr = reversed.getCharPointer();
|
||||
|
||||
for (;;)
|
||||
{
|
||||
destPtr.write (*--end);
|
||||
|
||||
if (end == start)
|
||||
break;
|
||||
}
|
||||
|
||||
destPtr.writeNull();
|
||||
}
|
||||
|
||||
return reversed;
|
||||
}();
|
||||
|
||||
auto tokens = (boundary == BoundaryType::line ? StringArray::fromLines (text)
|
||||
: StringArray::fromTokens (text, false));
|
||||
|
||||
return currentPosition + offsetWithDirecton (tokens[0].length());
|
||||
}
|
||||
|
||||
case BoundaryType::document:
|
||||
return isForwards ? numCharacters : 0;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace juce
|
918
deps/juce/modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp
vendored
Normal file
918
deps/juce/modules/juce_gui_basics/native/accessibility/juce_android_Accessibility.cpp
vendored
Normal file
@ -0,0 +1,918 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (constructor, "<init>", "()V") \
|
||||
METHOD (setSource, "setSource", "(Landroid/view/View;I)V") \
|
||||
METHOD (addChild, "addChild", "(Landroid/view/View;I)V") \
|
||||
METHOD (setParent, "setParent", "(Landroid/view/View;)V") \
|
||||
METHOD (setVirtualParent, "setParent", "(Landroid/view/View;I)V") \
|
||||
METHOD (setBoundsInScreen, "setBoundsInScreen", "(Landroid/graphics/Rect;)V") \
|
||||
METHOD (setBoundsInParent, "setBoundsInParent", "(Landroid/graphics/Rect;)V") \
|
||||
METHOD (setPackageName, "setPackageName", "(Ljava/lang/CharSequence;)V") \
|
||||
METHOD (setClassName, "setClassName", "(Ljava/lang/CharSequence;)V") \
|
||||
METHOD (setContentDescription, "setContentDescription", "(Ljava/lang/CharSequence;)V") \
|
||||
METHOD (setCheckable, "setCheckable", "(Z)V") \
|
||||
METHOD (setChecked, "setChecked", "(Z)V") \
|
||||
METHOD (setClickable, "setClickable", "(Z)V") \
|
||||
METHOD (setEnabled, "setEnabled", "(Z)V") \
|
||||
METHOD (setFocusable, "setFocusable", "(Z)V") \
|
||||
METHOD (setFocused, "setFocused", "(Z)V") \
|
||||
METHOD (setPassword, "setPassword", "(Z)V") \
|
||||
METHOD (setSelected, "setSelected", "(Z)V") \
|
||||
METHOD (setVisibleToUser, "setVisibleToUser", "(Z)V") \
|
||||
METHOD (setAccessibilityFocused, "setAccessibilityFocused", "(Z)V") \
|
||||
METHOD (setText, "setText", "(Ljava/lang/CharSequence;)V") \
|
||||
METHOD (setMovementGranularities, "setMovementGranularities", "(I)V") \
|
||||
METHOD (addAction, "addAction", "(I)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidAccessibilityNodeInfo, "android/view/accessibility/AccessibilityNodeInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (obtain, "obtain", "(I)Landroid/view/accessibility/AccessibilityEvent;") \
|
||||
METHOD (setPackageName, "setPackageName", "(Ljava/lang/CharSequence;)V") \
|
||||
METHOD (setSource, "setSource", "(Landroid/view/View;I)V") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidAccessibilityEvent, "android/view/accessibility/AccessibilityEvent")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (isEnabled, "isEnabled", "()Z") \
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidAccessibilityManager, "android/view/accessibility/AccessibilityManager")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr int HOST_VIEW_ID = -1;
|
||||
|
||||
constexpr int TYPE_VIEW_CLICKED = 0x00000001,
|
||||
TYPE_VIEW_SELECTED = 0x00000004,
|
||||
TYPE_VIEW_ACCESSIBILITY_FOCUSED = 0x00008000,
|
||||
TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED = 0x00010000,
|
||||
TYPE_WINDOW_CONTENT_CHANGED = 0x00000800,
|
||||
TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000,
|
||||
TYPE_VIEW_TEXT_CHANGED = 0x00000010;
|
||||
|
||||
constexpr int CONTENT_CHANGE_TYPE_SUBTREE = 0x00000001,
|
||||
CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 0x00000004;
|
||||
|
||||
constexpr int ACTION_ACCESSIBILITY_FOCUS = 0x00000040,
|
||||
ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080,
|
||||
ACTION_CLEAR_FOCUS = 0x00000002,
|
||||
ACTION_CLEAR_SELECTION = 0x00000008,
|
||||
ACTION_CLICK = 0x00000010,
|
||||
ACTION_COLLAPSE = 0x00080000,
|
||||
ACTION_EXPAND = 0x00040000,
|
||||
ACTION_FOCUS = 0x00000001,
|
||||
ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100,
|
||||
ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200,
|
||||
ACTION_SCROLL_BACKWARD = 0x00002000,
|
||||
ACTION_SCROLL_FORWARD = 0x00001000,
|
||||
ACTION_SELECT = 0x00000004,
|
||||
ACTION_SET_SELECTION = 0x00020000,
|
||||
ACTION_SET_TEXT = 0x00200000;
|
||||
|
||||
constexpr int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001,
|
||||
MOVEMENT_GRANULARITY_LINE = 0x00000004,
|
||||
MOVEMENT_GRANULARITY_PAGE = 0x00000010,
|
||||
MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008,
|
||||
MOVEMENT_GRANULARITY_WORD = 0x00000002,
|
||||
ALL_GRANULARITIES = MOVEMENT_GRANULARITY_CHARACTER
|
||||
| MOVEMENT_GRANULARITY_LINE
|
||||
| MOVEMENT_GRANULARITY_PAGE
|
||||
| MOVEMENT_GRANULARITY_PARAGRAPH
|
||||
| MOVEMENT_GRANULARITY_WORD;
|
||||
|
||||
constexpr int ACCESSIBILITY_LIVE_REGION_POLITE = 0x00000001;
|
||||
}
|
||||
|
||||
static jmethodID nodeInfoSetEditable = nullptr;
|
||||
static jmethodID nodeInfoSetTextSelection = nullptr;
|
||||
static jmethodID nodeInfoSetLiveRegion = nullptr;
|
||||
static jmethodID accessibilityEventSetContentChangeTypes = nullptr;
|
||||
|
||||
static void loadSDKDependentMethods()
|
||||
{
|
||||
static bool hasChecked = false;
|
||||
|
||||
if (! hasChecked)
|
||||
{
|
||||
hasChecked = true;
|
||||
|
||||
auto* env = getEnv();
|
||||
const auto sdkVersion = getAndroidSDKVersion();
|
||||
|
||||
if (sdkVersion >= 18)
|
||||
{
|
||||
nodeInfoSetEditable = env->GetMethodID (AndroidAccessibilityNodeInfo, "setEditable", "(Z)V");
|
||||
nodeInfoSetTextSelection = env->GetMethodID (AndroidAccessibilityNodeInfo, "setTextSelection", "(II)V");
|
||||
}
|
||||
|
||||
if (sdkVersion >= 19)
|
||||
{
|
||||
nodeInfoSetLiveRegion = env->GetMethodID (AndroidAccessibilityNodeInfo, "setLiveRegion", "(I)V");
|
||||
accessibilityEventSetContentChangeTypes = env->GetMethodID (AndroidAccessibilityEvent, "setContentChangeTypes", "(I)V");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr auto getClassName (AccessibilityRole role)
|
||||
{
|
||||
switch (role)
|
||||
{
|
||||
case AccessibilityRole::editableText: return "android.widget.EditText";
|
||||
case AccessibilityRole::toggleButton: return "android.widget.CheckBox";
|
||||
case AccessibilityRole::radioButton: return "android.widget.RadioButton";
|
||||
case AccessibilityRole::image: return "android.widget.ImageView";
|
||||
case AccessibilityRole::popupMenu: return "android.widget.PopupMenu";
|
||||
case AccessibilityRole::comboBox: return "android.widget.Spinner";
|
||||
case AccessibilityRole::tree: return "android.widget.ExpandableListView";
|
||||
case AccessibilityRole::list: return "android.widget.ListView";
|
||||
case AccessibilityRole::table: return "android.widget.TableLayout";
|
||||
case AccessibilityRole::progressBar: return "android.widget.ProgressBar";
|
||||
|
||||
case AccessibilityRole::scrollBar:
|
||||
case AccessibilityRole::slider: return "android.widget.SeekBar";
|
||||
|
||||
case AccessibilityRole::hyperlink:
|
||||
case AccessibilityRole::button: return "android.widget.Button";
|
||||
|
||||
case AccessibilityRole::label:
|
||||
case AccessibilityRole::staticText: return "android.widget.TextView";
|
||||
|
||||
case AccessibilityRole::tooltip:
|
||||
case AccessibilityRole::splashScreen:
|
||||
case AccessibilityRole::dialogWindow: return "android.widget.PopupWindow";
|
||||
|
||||
case AccessibilityRole::column:
|
||||
case AccessibilityRole::row:
|
||||
case AccessibilityRole::cell:
|
||||
case AccessibilityRole::menuItem:
|
||||
case AccessibilityRole::menuBar:
|
||||
case AccessibilityRole::listItem:
|
||||
case AccessibilityRole::treeItem:
|
||||
case AccessibilityRole::window:
|
||||
case AccessibilityRole::tableHeader:
|
||||
case AccessibilityRole::unspecified:
|
||||
case AccessibilityRole::group:
|
||||
case AccessibilityRole::ignored: break;
|
||||
}
|
||||
|
||||
return "android.view.View";
|
||||
}
|
||||
|
||||
static jobject getSourceView (const AccessibilityHandler& handler)
|
||||
{
|
||||
if (auto* peer = handler.getComponent().getPeer())
|
||||
return (jobject) peer->getNativeHandle();
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void sendAccessibilityEventImpl (const AccessibilityHandler& handler, int eventType, int contentChangeTypes);
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityNativeHandle
|
||||
{
|
||||
public:
|
||||
static AccessibilityHandler* getAccessibilityHandlerForVirtualViewId (int virtualViewId)
|
||||
{
|
||||
auto iter = virtualViewIdMap.find (virtualViewId);
|
||||
|
||||
if (iter != virtualViewIdMap.end())
|
||||
return iter->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
explicit AccessibilityNativeHandle (AccessibilityHandler& h)
|
||||
: accessibilityHandler (h),
|
||||
virtualViewId (getVirtualViewIdForHandler (accessibilityHandler))
|
||||
{
|
||||
loadSDKDependentMethods();
|
||||
|
||||
if (virtualViewId != HOST_VIEW_ID)
|
||||
virtualViewIdMap[virtualViewId] = &accessibilityHandler;
|
||||
}
|
||||
|
||||
~AccessibilityNativeHandle()
|
||||
{
|
||||
if (virtualViewId != HOST_VIEW_ID)
|
||||
virtualViewIdMap.erase (virtualViewId);
|
||||
}
|
||||
|
||||
int getVirtualViewId() const noexcept { return virtualViewId; }
|
||||
|
||||
void populateNodeInfo (jobject info)
|
||||
{
|
||||
const ScopedValueSetter<bool> svs (inPopulateNodeInfo, true);
|
||||
|
||||
const auto sourceView = getSourceView (accessibilityHandler);
|
||||
|
||||
if (sourceView == nullptr)
|
||||
return;
|
||||
|
||||
auto* env = getEnv();
|
||||
auto appContext = getAppContext();
|
||||
|
||||
if (appContext.get() == nullptr)
|
||||
return;
|
||||
|
||||
{
|
||||
for (auto* child : accessibilityHandler.getChildren())
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.addChild,
|
||||
sourceView, child->getNativeImplementation()->getVirtualViewId());
|
||||
|
||||
if (auto* parent = accessibilityHandler.getParent())
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setVirtualParent,
|
||||
sourceView, parent->getNativeImplementation()->getVirtualViewId());
|
||||
else
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setParent, sourceView);
|
||||
}
|
||||
|
||||
{
|
||||
const auto scale = Desktop::getInstance().getDisplays().getPrimaryDisplay()->scale;
|
||||
|
||||
const auto screenBounds = accessibilityHandler.getComponent().getScreenBounds() * scale;
|
||||
|
||||
LocalRef<jobject> rect (env->NewObject (AndroidRect, AndroidRect.constructor,
|
||||
screenBounds.getX(), screenBounds.getY(),
|
||||
screenBounds.getRight(), screenBounds.getBottom()));
|
||||
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInScreen, rect.get());
|
||||
|
||||
const auto boundsInParent = accessibilityHandler.getComponent().getBoundsInParent() * scale;
|
||||
|
||||
rect = LocalRef<jobject> (env->NewObject (AndroidRect, AndroidRect.constructor,
|
||||
boundsInParent.getX(), boundsInParent.getY(),
|
||||
boundsInParent.getRight(), boundsInParent.getBottom()));
|
||||
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setBoundsInParent, rect.get());
|
||||
}
|
||||
|
||||
const auto state = accessibilityHandler.getCurrentState();
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setEnabled,
|
||||
! state.isIgnored());
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setVisibleToUser,
|
||||
true);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setPackageName,
|
||||
env->CallObjectMethod (appContext.get(),
|
||||
AndroidContext.getPackageName));
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setSource,
|
||||
sourceView,
|
||||
virtualViewId);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setClassName,
|
||||
javaString (getClassName (accessibilityHandler.getRole())).get());
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setContentDescription,
|
||||
getDescriptionString().get());
|
||||
|
||||
if (state.isFocusable())
|
||||
{
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.setFocusable, true);
|
||||
|
||||
const auto& component = accessibilityHandler.getComponent();
|
||||
|
||||
if (component.getWantsKeyboardFocus())
|
||||
{
|
||||
const auto hasKeyboardFocus = component.hasKeyboardFocus (false);
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setFocused,
|
||||
hasKeyboardFocus);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
hasKeyboardFocus ? ACTION_CLEAR_FOCUS : ACTION_FOCUS);
|
||||
}
|
||||
|
||||
const auto isAccessibleFocused = accessibilityHandler.hasFocus (false);
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setAccessibilityFocused,
|
||||
isAccessibleFocused);
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
isAccessibleFocused ? ACTION_CLEAR_ACCESSIBILITY_FOCUS
|
||||
: ACTION_ACCESSIBILITY_FOCUS);
|
||||
}
|
||||
|
||||
if (state.isCheckable())
|
||||
{
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setCheckable,
|
||||
true);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setChecked,
|
||||
state.isChecked());
|
||||
}
|
||||
|
||||
if (state.isSelectable() || state.isMultiSelectable())
|
||||
{
|
||||
const auto isSelected = state.isSelected();
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setSelected,
|
||||
isSelected);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
isSelected ? ACTION_CLEAR_SELECTION : ACTION_SELECT);
|
||||
}
|
||||
|
||||
if (accessibilityHandler.getActions().contains (AccessibilityActionType::press))
|
||||
{
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setClickable,
|
||||
true);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
ACTION_CLICK);
|
||||
}
|
||||
|
||||
if (accessibilityHandler.getActions().contains (AccessibilityActionType::showMenu)
|
||||
&& state.isExpandable())
|
||||
{
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
state.isExpanded() ? ACTION_COLLAPSE : ACTION_EXPAND);
|
||||
}
|
||||
|
||||
if (auto* textInterface = accessibilityHandler.getTextInterface())
|
||||
{
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setText,
|
||||
javaString (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() })).get());
|
||||
|
||||
const auto isReadOnly = textInterface->isReadOnly();
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setPassword,
|
||||
textInterface->isDisplayingProtectedText());
|
||||
|
||||
if (nodeInfoSetEditable != nullptr)
|
||||
env->CallVoidMethod (info, nodeInfoSetEditable, ! isReadOnly);
|
||||
|
||||
const auto selection = textInterface->getSelection();
|
||||
|
||||
if (nodeInfoSetTextSelection != nullptr && ! selection.isEmpty())
|
||||
env->CallVoidMethod (info,
|
||||
nodeInfoSetTextSelection,
|
||||
selection.getStart(), selection.getEnd());
|
||||
|
||||
if (nodeInfoSetLiveRegion != nullptr && accessibilityHandler.hasFocus (false))
|
||||
env->CallVoidMethod (info,
|
||||
nodeInfoSetLiveRegion,
|
||||
ACCESSIBILITY_LIVE_REGION_POLITE);
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.setMovementGranularities,
|
||||
ALL_GRANULARITIES);
|
||||
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
ACTION_SET_SELECTION);
|
||||
|
||||
if (! isReadOnly)
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo.addAction, ACTION_SET_TEXT);
|
||||
}
|
||||
|
||||
if (auto* valueInterface = accessibilityHandler.getValueInterface())
|
||||
{
|
||||
if (! valueInterface->isReadOnly())
|
||||
{
|
||||
const auto range = valueInterface->getRange();
|
||||
|
||||
if (range.isValid())
|
||||
{
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
ACTION_SCROLL_FORWARD);
|
||||
env->CallVoidMethod (info,
|
||||
AndroidAccessibilityNodeInfo.addAction,
|
||||
ACTION_SCROLL_BACKWARD);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool performAction (int action, jobject arguments)
|
||||
{
|
||||
switch (action)
|
||||
{
|
||||
case ACTION_ACCESSIBILITY_FOCUS:
|
||||
{
|
||||
const WeakReference<Component> safeComponent (&accessibilityHandler.getComponent());
|
||||
|
||||
accessibilityHandler.getActions().invoke (AccessibilityActionType::focus);
|
||||
|
||||
if (safeComponent != nullptr)
|
||||
accessibilityHandler.grabFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
|
||||
{
|
||||
accessibilityHandler.giveAwayFocus();
|
||||
return true;
|
||||
}
|
||||
|
||||
case ACTION_FOCUS:
|
||||
case ACTION_CLEAR_FOCUS:
|
||||
{
|
||||
auto& component = accessibilityHandler.getComponent();
|
||||
|
||||
if (component.getWantsKeyboardFocus())
|
||||
{
|
||||
const auto hasFocus = component.hasKeyboardFocus (false);
|
||||
|
||||
if (hasFocus && action == ACTION_CLEAR_FOCUS)
|
||||
component.giveAwayKeyboardFocus();
|
||||
else if (! hasFocus && action == ACTION_FOCUS)
|
||||
component.grabKeyboardFocus();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_CLICK:
|
||||
{
|
||||
if (accessibilityHandler.getActions().invoke (AccessibilityActionType::press))
|
||||
{
|
||||
sendAccessibilityEventImpl (accessibilityHandler, TYPE_VIEW_CLICKED, 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_SELECT:
|
||||
case ACTION_CLEAR_SELECTION:
|
||||
{
|
||||
const auto state = accessibilityHandler.getCurrentState();
|
||||
|
||||
if (state.isSelectable() || state.isMultiSelectable())
|
||||
{
|
||||
const auto isSelected = state.isSelected();
|
||||
|
||||
if ((isSelected && action == ACTION_CLEAR_SELECTION)
|
||||
|| (! isSelected && action == ACTION_SELECT))
|
||||
{
|
||||
return accessibilityHandler.getActions().invoke (AccessibilityActionType::toggle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_EXPAND:
|
||||
case ACTION_COLLAPSE:
|
||||
{
|
||||
const auto state = accessibilityHandler.getCurrentState();
|
||||
|
||||
if (state.isExpandable())
|
||||
{
|
||||
const auto isExpanded = state.isExpanded();
|
||||
|
||||
if ((isExpanded && action == ACTION_COLLAPSE)
|
||||
|| (! isExpanded && action == ACTION_EXPAND))
|
||||
{
|
||||
return accessibilityHandler.getActions().invoke (AccessibilityActionType::showMenu);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_NEXT_AT_MOVEMENT_GRANULARITY: return moveCursor (arguments, true);
|
||||
case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY: return moveCursor (arguments, false);
|
||||
|
||||
case ACTION_SET_SELECTION:
|
||||
{
|
||||
if (auto* textInterface = accessibilityHandler.getTextInterface())
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
const auto selection = [&]() -> Range<int>
|
||||
{
|
||||
const auto selectionStartKey = javaString ("ACTION_ARGUMENT_SELECTION_START_INT");
|
||||
const auto selectionEndKey = javaString ("ACTION_ARGUMENT_SELECTION_END_INT");
|
||||
|
||||
const auto hasKey = [&env, &arguments] (const auto& key)
|
||||
{
|
||||
return env->CallBooleanMethod (arguments, AndroidBundle.containsKey, key.get());
|
||||
};
|
||||
|
||||
if (hasKey (selectionStartKey) && hasKey (selectionEndKey))
|
||||
{
|
||||
const auto getKey = [&env, &arguments] (const auto& key)
|
||||
{
|
||||
return env->CallIntMethod (arguments, AndroidBundle.getInt, key.get());
|
||||
};
|
||||
|
||||
return { getKey (selectionStartKey), getKey (selectionEndKey) };
|
||||
}
|
||||
|
||||
return {};
|
||||
}();
|
||||
|
||||
textInterface->setSelection (selection);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_SET_TEXT:
|
||||
{
|
||||
if (auto* textInterface = accessibilityHandler.getTextInterface())
|
||||
{
|
||||
if (! textInterface->isReadOnly())
|
||||
{
|
||||
const auto charSequenceKey = javaString ("ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE");
|
||||
|
||||
auto* env = getEnv();
|
||||
|
||||
const auto text = [&]() -> String
|
||||
{
|
||||
if (env->CallBooleanMethod (arguments, AndroidBundle.containsKey, charSequenceKey.get()))
|
||||
{
|
||||
LocalRef<jobject> charSequence (env->CallObjectMethod (arguments,
|
||||
AndroidBundle.getCharSequence,
|
||||
charSequenceKey.get()));
|
||||
LocalRef<jstring> textStringRef ((jstring) env->CallObjectMethod (charSequence,
|
||||
JavaCharSequence.toString));
|
||||
|
||||
return juceString (textStringRef.get());
|
||||
}
|
||||
|
||||
return {};
|
||||
}();
|
||||
|
||||
textInterface->setText (text);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case ACTION_SCROLL_BACKWARD:
|
||||
case ACTION_SCROLL_FORWARD:
|
||||
{
|
||||
if (auto* valueInterface = accessibilityHandler.getValueInterface())
|
||||
{
|
||||
if (! valueInterface->isReadOnly())
|
||||
{
|
||||
const auto range = valueInterface->getRange();
|
||||
|
||||
if (range.isValid())
|
||||
{
|
||||
const auto interval = action == ACTION_SCROLL_BACKWARD ? -range.getInterval()
|
||||
: range.getInterval();
|
||||
valueInterface->setValue (jlimit (range.getMinimumValue(),
|
||||
range.getMaximumValue(),
|
||||
valueInterface->getCurrentValue() + interval));
|
||||
|
||||
// required for Android to announce the new value
|
||||
sendAccessibilityEventImpl (accessibilityHandler, TYPE_VIEW_SELECTED, 0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isInPopulateNodeInfo() const noexcept { return inPopulateNodeInfo; }
|
||||
|
||||
private:
|
||||
static std::unordered_map<int, AccessibilityHandler*> virtualViewIdMap;
|
||||
|
||||
static int getVirtualViewIdForHandler (const AccessibilityHandler& handler)
|
||||
{
|
||||
static int counter = 0;
|
||||
|
||||
if (handler.getComponent().isOnDesktop())
|
||||
return HOST_VIEW_ID;
|
||||
|
||||
return counter++;
|
||||
}
|
||||
|
||||
LocalRef<jstring> getDescriptionString() const
|
||||
{
|
||||
const auto valueString = [this]() -> String
|
||||
{
|
||||
if (auto* textInterface = accessibilityHandler.getTextInterface())
|
||||
return textInterface->getText ({ 0, textInterface->getTotalNumCharacters() });
|
||||
|
||||
if (auto* valueInterface = accessibilityHandler.getValueInterface())
|
||||
return valueInterface->getCurrentValueAsString();
|
||||
|
||||
return {};
|
||||
}();
|
||||
|
||||
StringArray strings (accessibilityHandler.getTitle(),
|
||||
valueString,
|
||||
accessibilityHandler.getDescription(),
|
||||
accessibilityHandler.getHelp());
|
||||
|
||||
strings.removeEmptyStrings();
|
||||
|
||||
return javaString (strings.joinIntoString (","));
|
||||
}
|
||||
|
||||
bool moveCursor (jobject arguments, bool forwards)
|
||||
{
|
||||
if (auto* textInterface = accessibilityHandler.getTextInterface())
|
||||
{
|
||||
const auto granularityKey = javaString ("ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT");
|
||||
const auto extendSelectionKey = javaString ("ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN");
|
||||
|
||||
auto* env = getEnv();
|
||||
|
||||
const auto boundaryType = [&]
|
||||
{
|
||||
const auto granularity = env->CallIntMethod (arguments,
|
||||
AndroidBundle.getInt,
|
||||
granularityKey.get());
|
||||
|
||||
using BoundaryType = AccessibilityTextHelpers::BoundaryType;
|
||||
|
||||
switch (granularity)
|
||||
{
|
||||
case MOVEMENT_GRANULARITY_CHARACTER: return BoundaryType::character;
|
||||
case MOVEMENT_GRANULARITY_WORD: return BoundaryType::word;
|
||||
case MOVEMENT_GRANULARITY_LINE: return BoundaryType::line;
|
||||
case MOVEMENT_GRANULARITY_PARAGRAPH:
|
||||
case MOVEMENT_GRANULARITY_PAGE: return BoundaryType::document;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return BoundaryType::character;
|
||||
}();
|
||||
|
||||
using Direction = AccessibilityTextHelpers::Direction;
|
||||
|
||||
const auto cursorPos = AccessibilityTextHelpers::findTextBoundary (*textInterface,
|
||||
textInterface->getTextInsertionOffset(),
|
||||
boundaryType,
|
||||
forwards ? Direction::forwards
|
||||
: Direction::backwards);
|
||||
|
||||
const auto newSelection = [&]() -> Range<int>
|
||||
{
|
||||
const auto currentSelection = textInterface->getSelection();
|
||||
const auto extendSelection = env->CallBooleanMethod (arguments,
|
||||
AndroidBundle.getBoolean,
|
||||
extendSelectionKey.get());
|
||||
|
||||
if (! extendSelection)
|
||||
return { cursorPos, cursorPos };
|
||||
|
||||
const auto start = currentSelection.getStart();
|
||||
const auto end = currentSelection.getEnd();
|
||||
|
||||
if (forwards)
|
||||
return { start, jmax (start, cursorPos) };
|
||||
|
||||
return { jmin (start, cursorPos), end };
|
||||
}();
|
||||
|
||||
textInterface->setSelection (newSelection);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
AccessibilityHandler& accessibilityHandler;
|
||||
const int virtualViewId;
|
||||
bool inPopulateNodeInfo = false;
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibilityNativeHandle)
|
||||
};
|
||||
|
||||
std::unordered_map<int, AccessibilityHandler*> AccessibilityNativeHandle::virtualViewIdMap;
|
||||
|
||||
class AccessibilityHandler::AccessibilityNativeImpl : public AccessibilityNativeHandle
|
||||
{
|
||||
public:
|
||||
using AccessibilityNativeHandle::AccessibilityNativeHandle;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
AccessibilityNativeHandle* AccessibilityHandler::getNativeImplementation() const
|
||||
{
|
||||
return nativeImpl.get();
|
||||
}
|
||||
|
||||
static bool areAnyAccessibilityClientsActive()
|
||||
{
|
||||
auto* env = getEnv();
|
||||
auto appContext = getAppContext();
|
||||
|
||||
if (appContext.get() != nullptr)
|
||||
{
|
||||
LocalRef<jobject> accessibilityManager (env->CallObjectMethod (appContext.get(), AndroidContext.getSystemService,
|
||||
javaString ("accessibility").get()));
|
||||
|
||||
if (accessibilityManager != nullptr)
|
||||
return env->CallBooleanMethod (accessibilityManager.get(), AndroidAccessibilityManager.isEnabled);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void sendAccessibilityEventImpl (const AccessibilityHandler& handler, int eventType, int contentChangeTypes)
|
||||
{
|
||||
if (! areAnyAccessibilityClientsActive())
|
||||
return;
|
||||
|
||||
if (const auto sourceView = getSourceView (handler))
|
||||
{
|
||||
const auto* nativeImpl = handler.getNativeImplementation();
|
||||
|
||||
if (nativeImpl == nullptr || nativeImpl->isInPopulateNodeInfo())
|
||||
return;
|
||||
|
||||
auto* env = getEnv();
|
||||
auto appContext = getAppContext();
|
||||
|
||||
if (appContext.get() == nullptr)
|
||||
return;
|
||||
|
||||
LocalRef<jobject> event (env->CallStaticObjectMethod (AndroidAccessibilityEvent,
|
||||
AndroidAccessibilityEvent.obtain,
|
||||
eventType));
|
||||
|
||||
env->CallVoidMethod (event,
|
||||
AndroidAccessibilityEvent.setPackageName,
|
||||
env->CallObjectMethod (appContext.get(),
|
||||
AndroidContext.getPackageName));
|
||||
|
||||
env->CallVoidMethod (event,
|
||||
AndroidAccessibilityEvent.setSource,
|
||||
sourceView,
|
||||
nativeImpl->getVirtualViewId());
|
||||
|
||||
if (contentChangeTypes != 0 && accessibilityEventSetContentChangeTypes != nullptr)
|
||||
env->CallVoidMethod (event,
|
||||
accessibilityEventSetContentChangeTypes,
|
||||
contentChangeTypes);
|
||||
|
||||
env->CallBooleanMethod (sourceView,
|
||||
AndroidViewGroup.requestSendAccessibilityEvent,
|
||||
sourceView,
|
||||
event.get());
|
||||
}
|
||||
}
|
||||
|
||||
void notifyAccessibilityEventInternal (const AccessibilityHandler& handler,
|
||||
InternalAccessibilityEvent eventType)
|
||||
{
|
||||
if (eventType == InternalAccessibilityEvent::elementCreated
|
||||
|| eventType == InternalAccessibilityEvent::elementDestroyed
|
||||
|| eventType == InternalAccessibilityEvent::elementMovedOrResized)
|
||||
{
|
||||
if (auto* parent = handler.getParent())
|
||||
sendAccessibilityEventImpl (*parent, TYPE_WINDOW_CONTENT_CHANGED, CONTENT_CHANGE_TYPE_SUBTREE);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto notification = [&handler, eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case InternalAccessibilityEvent::focusChanged:
|
||||
return handler.hasFocus (false) ? TYPE_VIEW_ACCESSIBILITY_FOCUSED
|
||||
: TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED;
|
||||
|
||||
case InternalAccessibilityEvent::elementCreated:
|
||||
case InternalAccessibilityEvent::elementDestroyed:
|
||||
case InternalAccessibilityEvent::elementMovedOrResized:
|
||||
case InternalAccessibilityEvent::windowOpened:
|
||||
case InternalAccessibilityEvent::windowClosed:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}();
|
||||
|
||||
if (notification != 0)
|
||||
sendAccessibilityEventImpl (handler, notification, 0);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
|
||||
{
|
||||
auto notification = [eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case AccessibilityEvent::textSelectionChanged: return TYPE_VIEW_TEXT_SELECTION_CHANGED;
|
||||
case AccessibilityEvent::textChanged: return TYPE_VIEW_TEXT_CHANGED;
|
||||
|
||||
case AccessibilityEvent::titleChanged:
|
||||
case AccessibilityEvent::structureChanged: return TYPE_WINDOW_CONTENT_CHANGED;
|
||||
|
||||
case AccessibilityEvent::rowSelectionChanged:
|
||||
case AccessibilityEvent::valueChanged: break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}();
|
||||
|
||||
if (notification == 0)
|
||||
return;
|
||||
|
||||
const auto contentChangeTypes = [eventType]
|
||||
{
|
||||
if (eventType == AccessibilityEvent::titleChanged) return CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION;
|
||||
if (eventType == AccessibilityEvent::structureChanged) return CONTENT_CHANGE_TYPE_SUBTREE;
|
||||
|
||||
return 0;
|
||||
}();
|
||||
|
||||
sendAccessibilityEventImpl (*this, notification, contentChangeTypes);
|
||||
}
|
||||
|
||||
void AccessibilityHandler::postAnnouncement (const String& announcementString,
|
||||
AnnouncementPriority)
|
||||
{
|
||||
if (! areAnyAccessibilityClientsActive())
|
||||
return;
|
||||
|
||||
const auto rootView = []
|
||||
{
|
||||
LocalRef<jobject> activity (getMainActivity());
|
||||
|
||||
if (activity != nullptr)
|
||||
{
|
||||
auto* env = getEnv();
|
||||
|
||||
LocalRef<jobject> mainWindow (env->CallObjectMethod (activity.get(), AndroidActivity.getWindow));
|
||||
LocalRef<jobject> decorView (env->CallObjectMethod (mainWindow.get(), AndroidWindow.getDecorView));
|
||||
|
||||
return LocalRef<jobject> (env->CallObjectMethod (decorView.get(), AndroidView.getRootView));
|
||||
}
|
||||
|
||||
return LocalRef<jobject>();
|
||||
}();
|
||||
|
||||
if (rootView != nullptr)
|
||||
getEnv()->CallVoidMethod (rootView.get(),
|
||||
AndroidView.announceForAccessibility,
|
||||
javaString (announcementString).get());
|
||||
}
|
||||
|
||||
} // namespace juce
|
570
deps/juce/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm
vendored
Normal file
570
deps/juce/modules/juce_gui_basics/native/accessibility/juce_ios_Accessibility.mm
vendored
Normal file
@ -0,0 +1,570 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
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
|
||||
{
|
||||
|
||||
#if (defined (__IPHONE_11_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_11_0)
|
||||
#define JUCE_IOS_CONTAINER_API_AVAILABLE 1
|
||||
#endif
|
||||
|
||||
constexpr auto juceUIAccessibilityContainerTypeNone =
|
||||
#if JUCE_IOS_CONTAINER_API_AVAILABLE
|
||||
UIAccessibilityContainerTypeNone;
|
||||
#else
|
||||
0;
|
||||
#endif
|
||||
|
||||
constexpr auto juceUIAccessibilityContainerTypeDataTable =
|
||||
#if JUCE_IOS_CONTAINER_API_AVAILABLE
|
||||
UIAccessibilityContainerTypeDataTable;
|
||||
#else
|
||||
1;
|
||||
#endif
|
||||
|
||||
constexpr auto juceUIAccessibilityContainerTypeList =
|
||||
#if JUCE_IOS_CONTAINER_API_AVAILABLE
|
||||
UIAccessibilityContainerTypeList;
|
||||
#else
|
||||
2;
|
||||
#endif
|
||||
|
||||
#define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
|
||||
|
||||
//==============================================================================
|
||||
static NSArray* getContainerAccessibilityElements (AccessibilityHandler& handler)
|
||||
{
|
||||
const auto children = handler.getChildren();
|
||||
|
||||
NSMutableArray* accessibleChildren = [NSMutableArray arrayWithCapacity: (NSUInteger) children.size()];
|
||||
|
||||
[accessibleChildren addObject: (id) handler.getNativeImplementation()];
|
||||
|
||||
for (auto* childHandler : children)
|
||||
{
|
||||
id accessibleElement = [&childHandler]
|
||||
{
|
||||
id native = (id) childHandler->getNativeImplementation();
|
||||
|
||||
if (childHandler->getChildren().size() > 0)
|
||||
return [native accessibilityContainer];
|
||||
|
||||
return native;
|
||||
}();
|
||||
|
||||
if (accessibleElement != nil)
|
||||
[accessibleChildren addObject: accessibleElement];
|
||||
}
|
||||
|
||||
return accessibleChildren;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityHandler::AccessibilityNativeImpl
|
||||
{
|
||||
public:
|
||||
explicit AccessibilityNativeImpl (AccessibilityHandler& handler)
|
||||
: accessibilityElement (AccessibilityElement::create (handler))
|
||||
{
|
||||
}
|
||||
|
||||
UIAccessibilityElement* getAccessibilityElement() const noexcept
|
||||
{
|
||||
return accessibilityElement.get();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class AccessibilityContainer : public ObjCClass<UIAccessibilityElement>
|
||||
{
|
||||
public:
|
||||
AccessibilityContainer()
|
||||
: ObjCClass ("JUCEUIAccessibilityElementContainer_")
|
||||
{
|
||||
addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:");
|
||||
addMethod (@selector (accessibilityFrame), getAccessibilityFrame, @encode (CGRect), "@:");
|
||||
addMethod (@selector (accessibilityElements), getAccessibilityElements, "@@:");
|
||||
addMethod (@selector (accessibilityContainerType), getAccessibilityContainerType, "i@:");
|
||||
|
||||
addIvar<AccessibilityHandler*> ("handler");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
static AccessibilityHandler* getHandler (id self)
|
||||
{
|
||||
return getIvar<AccessibilityHandler*> (self, "handler");
|
||||
}
|
||||
|
||||
static BOOL getIsAccessibilityElement (id, SEL)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
static CGRect getAccessibilityFrame (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return convertToCGRect (handler->getComponent().getScreenBounds());
|
||||
|
||||
return CGRectZero;
|
||||
}
|
||||
|
||||
static NSArray* getAccessibilityElements (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return getContainerAccessibilityElements (*handler);
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityContainerType (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getTableInterface() != nullptr)
|
||||
return juceUIAccessibilityContainerTypeDataTable;
|
||||
|
||||
const auto role = handler->getRole();
|
||||
|
||||
if (role == AccessibilityRole::popupMenu
|
||||
|| role == AccessibilityRole::list
|
||||
|| role == AccessibilityRole::tree)
|
||||
{
|
||||
return juceUIAccessibilityContainerTypeList;
|
||||
}
|
||||
}
|
||||
|
||||
return juceUIAccessibilityContainerTypeNone;
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityElement : public AccessibleObjCClass<UIAccessibilityElement>
|
||||
{
|
||||
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: (id) handler.getComponent().getWindowHandle()]);
|
||||
object_setInstanceVariable (element.get(), "handler", &handler);
|
||||
return element;
|
||||
}
|
||||
|
||||
AccessibilityElement (Type elementType)
|
||||
{
|
||||
addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:");
|
||||
addMethod (@selector (accessibilityContainer), getAccessibilityContainer, "@@:");
|
||||
addMethod (@selector (accessibilityFrame), getAccessibilityFrame, @encode (CGRect), "@:");
|
||||
addMethod (@selector (accessibilityTraits), getAccessibilityTraits, "i@:");
|
||||
addMethod (@selector (accessibilityLabel), getAccessibilityTitle, "@@:");
|
||||
addMethod (@selector (accessibilityHint), getAccessibilityHelp, "@@:");
|
||||
addMethod (@selector (accessibilityValue), getAccessibilityValue, "@@:");
|
||||
addMethod (@selector (setAccessibilityValue:), setAccessibilityValue, "v@:@");
|
||||
|
||||
addMethod (@selector (accessibilityElementDidBecomeFocused), onFocusGain, "v@:");
|
||||
addMethod (@selector (accessibilityElementDidLoseFocus), onFocusLoss, "v@:");
|
||||
addMethod (@selector (accessibilityElementIsFocused), isFocused, "c@:");
|
||||
addMethod (@selector (accessibilityViewIsModal), getIsAccessibilityModal, "c@:");
|
||||
|
||||
addMethod (@selector (accessibilityActivate), accessibilityPerformActivate, "c@:");
|
||||
addMethod (@selector (accessibilityIncrement), accessibilityPerformIncrement, "c@:");
|
||||
addMethod (@selector (accessibilityDecrement), accessibilityPerformDecrement, "c@:");
|
||||
addMethod (@selector (accessibilityPerformEscape), accessibilityEscape, "c@:");
|
||||
|
||||
addMethod (@selector (accessibilityDataTableCellElementForRow:column:), getAccessibilityDataTableCellElementForRowColumn, "@@:ii");
|
||||
addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount, "i@:");
|
||||
addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount, "i@:");
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTable));
|
||||
|
||||
addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange, @encode (NSRange), "@:");
|
||||
addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange, @encode (NSRange), "@:");
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTableCell));
|
||||
|
||||
if (elementType == Type::textElement)
|
||||
{
|
||||
addMethod (@selector (accessibilityLineNumberForPoint:), getAccessibilityLineNumberForPoint, "i@:", @encode (CGPoint));
|
||||
addMethod (@selector (accessibilityContentForLineNumber:), getAccessibilityContentForLineNumber, "@@:i");
|
||||
addMethod (@selector (accessibilityFrameForLineNumber:), getAccessibilityFrameForLineNumber, @encode (CGRect), "@:i");
|
||||
addMethod (@selector (accessibilityPageContent), getAccessibilityPageContent, "@@:");
|
||||
|
||||
addProtocol (@protocol (UIAccessibilityReadingContent));
|
||||
}
|
||||
|
||||
addIvar<UIAccessibilityElement*> ("container");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
static UIAccessibilityElement* getContainer (id self)
|
||||
{
|
||||
return getIvar<UIAccessibilityElement*> (self, "container");
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static id getAccessibilityContainer (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getComponent().isOnDesktop())
|
||||
return (id) handler->getComponent().getWindowHandle();
|
||||
|
||||
if (handler->getChildren().size() > 0)
|
||||
{
|
||||
if (UIAccessibilityElement* container = getContainer (self))
|
||||
return container;
|
||||
|
||||
static AccessibilityContainer cls;
|
||||
|
||||
id windowHandle = (id) handler->getComponent().getWindowHandle();
|
||||
UIAccessibilityElement* container = [cls.createInstance() initWithAccessibilityContainer: windowHandle];
|
||||
|
||||
[container retain];
|
||||
|
||||
object_setInstanceVariable (container, "handler", handler);
|
||||
object_setInstanceVariable (self, "container", container);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
if (auto* parent = handler->getParent())
|
||||
return [(id) parent->getNativeImplementation() accessibilityContainer];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static CGRect getAccessibilityFrame (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return convertToCGRect (handler->getComponent().getScreenBounds());
|
||||
|
||||
return CGRectZero;
|
||||
}
|
||||
|
||||
static UIAccessibilityTraits getAccessibilityTraits (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::editableText: return UIAccessibilityTraitKeyboardKey;
|
||||
case AccessibilityRole::ignored: return UIAccessibilityTraitNotEnabled;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
static NSString* getAccessibilityValue (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getCurrentState().isCheckable())
|
||||
return handler->getCurrentState().isChecked() ? @"1" : @"0";
|
||||
|
||||
return (NSString*) getAccessibilityValueFromInterfaces (*handler);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static void onFocusGain (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
const WeakReference<Component> safeComponent (&handler->getComponent());
|
||||
|
||||
performActionIfSupported (self, AccessibilityActionType::focus);
|
||||
|
||||
if (safeComponent != nullptr)
|
||||
handler->grabFocus();
|
||||
}
|
||||
}
|
||||
|
||||
static void onFocusLoss (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
handler->giveAwayFocus();
|
||||
}
|
||||
|
||||
static BOOL isFocused (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->hasFocus (false);
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static BOOL accessibilityPerformActivate (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
// occasionaly 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 (! [(id) handler->getNativeImplementation() isEqual: focusedElement])
|
||||
return [focusedElement accessibilityActivate];
|
||||
|
||||
if (handler->hasFocus (false))
|
||||
return accessibilityPerformPress (self, {});
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static BOOL accessibilityEscape (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self)) {
|
||||
// HACK - look for parent that is a CalloutBox and dismiss it
|
||||
if (CallOutBox* const cb = handler->getComponent().findParentComponentOfClass<CallOutBox>()) {
|
||||
cb->dismiss();
|
||||
return YES;
|
||||
}
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
|
||||
static id getAccessibilityDataTableCellElementForRowColumn (id self, SEL, NSUInteger row, NSUInteger column)
|
||||
{
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
if (auto* cellHandler = tableInterface->getCellHandler ((int) row, (int) column))
|
||||
return (id) cellHandler->getNativeImplementation();
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityLineNumberForPoint (id self, SEL, CGPoint point)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* textInterface = handler->getTextInterface())
|
||||
{
|
||||
auto pointInt = roundToIntPoint (point);
|
||||
|
||||
if (handler->getComponent().getScreenBounds().contains (pointInt))
|
||||
{
|
||||
auto textBounds = textInterface->getTextBounds ({ 0, textInterface->getTotalNumCharacters() });
|
||||
|
||||
for (int i = 0; i < textBounds.getNumRectangles(); ++i)
|
||||
if (textBounds.getRectangle (i).contains (pointInt))
|
||||
return (NSInteger) i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NSNotFound;
|
||||
}
|
||||
|
||||
static NSString* getAccessibilityContentForLineNumber (id self, SEL, NSInteger lineNumber)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
{
|
||||
auto lines = StringArray::fromLines (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() }));
|
||||
|
||||
if ((int) lineNumber < lines.size())
|
||||
return juceStringToNS (lines[(int) lineNumber]);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static CGRect getAccessibilityFrameForLineNumber (id self, SEL, NSInteger lineNumber)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
{
|
||||
auto textBounds = textInterface->getTextBounds ({ 0, textInterface->getTotalNumCharacters() });
|
||||
|
||||
if (lineNumber < textBounds.getNumRectangles())
|
||||
return convertToCGRect (textBounds.getRectangle ((int) lineNumber));
|
||||
}
|
||||
|
||||
return CGRectZero;
|
||||
}
|
||||
|
||||
static NSString* getAccessibilityPageContent (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return juceStringToNS (textInterface->getText ({ 0, textInterface->getTotalNumCharacters() }));
|
||||
|
||||
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 notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
|
||||
{
|
||||
auto notification = [eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case InternalAccessibilityEvent::elementCreated:
|
||||
case InternalAccessibilityEvent::elementDestroyed:
|
||||
case InternalAccessibilityEvent::elementMovedOrResized:
|
||||
case InternalAccessibilityEvent::focusChanged: return UIAccessibilityLayoutChangedNotification;
|
||||
|
||||
case InternalAccessibilityEvent::windowOpened:
|
||||
case InternalAccessibilityEvent::windowClosed: return UIAccessibilityScreenChangedNotification;
|
||||
}
|
||||
|
||||
return UIAccessibilityNotifications{};
|
||||
}();
|
||||
|
||||
if (notification != UIAccessibilityNotifications{})
|
||||
{
|
||||
const bool moveToHandler = (eventType == InternalAccessibilityEvent::focusChanged && handler.hasFocus (false));
|
||||
|
||||
sendAccessibilityEvent (notification,
|
||||
moveToHandler ? (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, (id) getNativeImplementation());
|
||||
}
|
||||
|
||||
void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority)
|
||||
{
|
||||
sendAccessibilityEvent (UIAccessibilityAnnouncementNotification, juceStringToNS (announcementString));
|
||||
}
|
||||
|
||||
} // namespace juce
|
963
deps/juce/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm
vendored
Normal file
963
deps/juce/modules/juce_gui_basics/native/accessibility/juce_mac_Accessibility.mm
vendored
Normal file
@ -0,0 +1,963 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
static void juceFreeAccessibilityPlatformSpecificData (NSAccessibilityElement<NSAccessibility>*) {}
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#if (! defined MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13
|
||||
using NSAccessibilityRole = NSString*;
|
||||
using NSAccessibilityNotificationName = NSString*;
|
||||
#endif
|
||||
|
||||
#define JUCE_NATIVE_ACCESSIBILITY_INCLUDED 1
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wunguarded-availability", "-Wunguarded-availability-new")
|
||||
|
||||
//==============================================================================
|
||||
class AccessibilityHandler::AccessibilityNativeImpl
|
||||
{
|
||||
public:
|
||||
explicit AccessibilityNativeImpl (AccessibilityHandler& handler)
|
||||
: accessibilityElement (AccessibilityElement::create (handler))
|
||||
{}
|
||||
|
||||
NSAccessibilityElement<NSAccessibility>* getAccessibilityElement() const noexcept
|
||||
{
|
||||
return accessibilityElement.get();
|
||||
}
|
||||
|
||||
private:
|
||||
//==============================================================================
|
||||
class AccessibilityElement : 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), getAccessibilityNotifiesWhenDestroyed, "c@:");
|
||||
addMethod (@selector (isAccessibilityElement), getIsAccessibilityElement, "c@:");
|
||||
addMethod (@selector (isAccessibilityEnabled), getIsAccessibilityEnabled, "c@:");
|
||||
addMethod (@selector (accessibilityWindow), getAccessibilityWindow, "@@:");
|
||||
addMethod (@selector (accessibilityTopLevelUIElement), getAccessibilityWindow, "@@:");
|
||||
addMethod (@selector (accessibilityFocusedUIElement), getAccessibilityFocusedUIElement, "@@:");
|
||||
addMethod (@selector (accessibilityHitTest:), accessibilityHitTest, "@@:", @encode (NSPoint));
|
||||
addMethod (@selector (accessibilityParent), getAccessibilityParent, "@@:");
|
||||
addMethod (@selector (accessibilityChildren), getAccessibilityChildren, "@@:");
|
||||
addMethod (@selector (isAccessibilityFocused), getIsAccessibilityFocused, "c@:");
|
||||
addMethod (@selector (setAccessibilityFocused:), setAccessibilityFocused, "v@:c");
|
||||
addMethod (@selector (isAccessibilityModal), getIsAccessibilityModal, "c@:");
|
||||
addMethod (@selector (accessibilityFrame), getAccessibilityFrame, @encode (NSRect), "@:");
|
||||
addMethod (@selector (accessibilityRole), getAccessibilityRole, "@@:");
|
||||
addMethod (@selector (accessibilitySubrole), getAccessibilitySubrole, "@@:");
|
||||
addMethod (@selector (accessibilityTitle), getAccessibilityTitle, "@@:");
|
||||
addMethod (@selector (accessibilityLabel), getAccessibilityLabel, "@@:");
|
||||
addMethod (@selector (accessibilityHelp), getAccessibilityHelp, "@@:");
|
||||
addMethod (@selector (accessibilityValue), getAccessibilityValue, "@@:");
|
||||
addMethod (@selector (setAccessibilityValue:), setAccessibilityValue, "v@:@");
|
||||
addMethod (@selector (accessibilitySelectedChildren), getAccessibilitySelectedChildren, "@@:");
|
||||
addMethod (@selector (setAccessibilitySelectedChildren:), setAccessibilitySelectedChildren, "v@:@");
|
||||
addMethod (@selector (accessibilityOrientation), getAccessibilityOrientation, "i@:@");
|
||||
|
||||
addMethod (@selector (accessibilityInsertionPointLineNumber), getAccessibilityInsertionPointLineNumber, "i@:");
|
||||
addMethod (@selector (accessibilityVisibleCharacterRange), getAccessibilityVisibleCharacterRange, @encode (NSRange), "@:");
|
||||
addMethod (@selector (accessibilityNumberOfCharacters), getAccessibilityNumberOfCharacters, "i@:");
|
||||
addMethod (@selector (accessibilitySelectedText), getAccessibilitySelectedText, "@@:");
|
||||
addMethod (@selector (accessibilitySelectedTextRange), getAccessibilitySelectedTextRange, @encode (NSRange), "@:");
|
||||
addMethod (@selector (accessibilityAttributedStringForRange:), getAccessibilityAttributedStringForRange, "@@:", @encode (NSRange));
|
||||
addMethod (@selector (accessibilityRangeForLine:), getAccessibilityRangeForLine, @encode (NSRange), "@:i");
|
||||
addMethod (@selector (accessibilityStringForRange:), getAccessibilityStringForRange, "@@:", @encode (NSRange));
|
||||
addMethod (@selector (accessibilityRangeForPosition:), getAccessibilityRangeForPosition, @encode (NSRange), "@:", @encode (NSPoint));
|
||||
addMethod (@selector (accessibilityRangeForIndex:), getAccessibilityRangeForIndex, @encode (NSRange), "@:i");
|
||||
addMethod (@selector (accessibilityFrameForRange:), getAccessibilityFrameForRange, @encode (NSRect), "@:", @encode (NSRange));
|
||||
addMethod (@selector (accessibilityLineForIndex:), getAccessibilityLineForIndex, "i@:i");
|
||||
addMethod (@selector (setAccessibilitySelectedTextRange:), setAccessibilitySelectedTextRange, "v@:", @encode (NSRange));
|
||||
|
||||
addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount, "i@:");
|
||||
addMethod (@selector (accessibilityRows), getAccessibilityRows, "@@:");
|
||||
addMethod (@selector (accessibilitySelectedRows), getAccessibilitySelectedRows, "@@:");
|
||||
addMethod (@selector (setAccessibilitySelectedRows:), setAccessibilitySelectedRows, "v@:@");
|
||||
addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount, "i@:");
|
||||
addMethod (@selector (accessibilityColumns), getAccessibilityColumns, "@@:");
|
||||
addMethod (@selector (accessibilitySelectedColumns), getAccessibilitySelectedColumns, "@@:");
|
||||
addMethod (@selector (setAccessibilitySelectedColumns:), setAccessibilitySelectedColumns, "v@:@");
|
||||
|
||||
addMethod (@selector (accessibilityRowIndexRange), getAccessibilityRowIndexRange, @encode (NSRange), "@:");
|
||||
addMethod (@selector (accessibilityColumnIndexRange), getAccessibilityColumnIndexRange, @encode (NSRange), "@:");
|
||||
addMethod (@selector (accessibilityIndex), getAccessibilityIndex, "i@:");
|
||||
addMethod (@selector (accessibilityDisclosureLevel), getAccessibilityDisclosureLevel, "i@:");
|
||||
addMethod (@selector (isAccessibilityExpanded), getIsAccessibilityExpanded, "c@:");
|
||||
|
||||
addMethod (@selector (accessibilityPerformIncrement), accessibilityPerformIncrement, "c@:");
|
||||
addMethod (@selector (accessibilityPerformDecrement), accessibilityPerformDecrement, "c@:");
|
||||
addMethod (@selector (accessibilityPerformDelete), accessibilityPerformDelete, "c@:");
|
||||
addMethod (@selector (accessibilityPerformPress), accessibilityPerformPress, "c@:");
|
||||
addMethod (@selector (accessibilityPerformShowMenu), accessibilityPerformShowMenu, "c@:");
|
||||
addMethod (@selector (accessibilityPerformRaise), accessibilityPerformRaise, "c@:");
|
||||
|
||||
addMethod (@selector (isAccessibilitySelectorAllowed:), getIsAccessibilitySelectorAllowed, "c@:@");
|
||||
|
||||
#if defined (MAC_OS_X_VERSION_10_13) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_13
|
||||
addMethod (@selector (accessibilityChildrenInNavigationOrder), getAccessibilityChildren, "@@:");
|
||||
#endif
|
||||
|
||||
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 setSelectedChildren (NSArray* children, NSArray* selected)
|
||||
{
|
||||
for (id child in children)
|
||||
{
|
||||
if (auto* handler = getHandler (child))
|
||||
{
|
||||
const auto currentState = handler->getCurrentState();
|
||||
const BOOL isSelected = [selected containsObject: child];
|
||||
|
||||
if (isSelectable (currentState))
|
||||
{
|
||||
if (currentState.isSelected() != isSelected)
|
||||
handler->getActions().invoke (AccessibilityActionType::toggle);
|
||||
}
|
||||
else if (currentState.isFocusable())
|
||||
{
|
||||
[child setAccessibilityFocused: isSelected];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static BOOL getAccessibilityNotifiesWhenDestroyed (id, SEL) { return YES; }
|
||||
|
||||
static BOOL getIsAccessibilityEnabled (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->getComponent().isEnabled();
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
static id getAccessibilityWindow (id self, SEL)
|
||||
{
|
||||
return [[self accessibilityParent] accessibilityWindow];
|
||||
}
|
||||
|
||||
static id getAccessibilityFocusedUIElement (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* modal = Component::getCurrentlyModalComponent())
|
||||
{
|
||||
const auto& component = handler->getComponent();
|
||||
|
||||
if (! component.isParentOf (modal)
|
||||
&& component.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (auto* modalHandler = modal->getAccessibilityHandler())
|
||||
{
|
||||
if (auto* focusChild = modalHandler->getChildFocus())
|
||||
return (id) focusChild->getNativeImplementation();
|
||||
|
||||
return (id) modalHandler->getNativeImplementation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* focusChild = handler->getChildFocus())
|
||||
return (id) focusChild->getNativeImplementation();
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static id accessibilityHitTest (id self, SEL, NSPoint point)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* child = handler->getChildAt (roundToIntPoint (flippedScreenPoint (point))))
|
||||
return (id) child->getNativeImplementation();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static id getAccessibilityParent (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* parentHandler = handler->getParent())
|
||||
return NSAccessibilityUnignoredAncestor ((id) parentHandler->getNativeImplementation());
|
||||
|
||||
return NSAccessibilityUnignoredAncestor ((id) handler->getComponent().getWindowHandle());
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
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: (id) childHandler->getNativeImplementation()];
|
||||
|
||||
return accessibleChildren;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static id getAccessibilityValue (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getCurrentState().isCheckable())
|
||||
return handler->getCurrentState().isChecked() ? @(1) : @(0);
|
||||
|
||||
return getAccessibilityValueFromInterfaces (*handler);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSArray* getAccessibilitySelectedChildren (id self, SEL)
|
||||
{
|
||||
return getSelectedChildren ([self accessibilityChildren]);
|
||||
}
|
||||
|
||||
static void setAccessibilitySelectedChildren (id self, SEL, NSArray* selected)
|
||||
{
|
||||
setSelectedChildren ([self accessibilityChildren], selected);
|
||||
}
|
||||
|
||||
static NSAccessibilityOrientation getAccessibilityOrientation (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->getComponent().getBounds().toFloat().getAspectRatio() > 1.0f
|
||||
? NSAccessibilityOrientationHorizontal
|
||||
: NSAccessibilityOrientationVertical;
|
||||
|
||||
return NSAccessibilityOrientationUnknown;
|
||||
}
|
||||
|
||||
static BOOL getIsAccessibilityFocused (id self, SEL)
|
||||
{
|
||||
return [[self accessibilityWindow] accessibilityFocusedUIElement] == self;
|
||||
}
|
||||
|
||||
static void 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static NSRect getAccessibilityFrame (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return flippedScreenRect (makeNSRect (handler->getComponent().getScreenBounds()));
|
||||
|
||||
return NSZeroRect;
|
||||
}
|
||||
|
||||
static NSAccessibilityRole getAccessibilityRole (id self, SEL)
|
||||
{
|
||||
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 NSAccessibilityListRole;
|
||||
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;
|
||||
}
|
||||
|
||||
static NSAccessibilityRole getAccessibilitySubrole (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
if (textInterface->isDisplayingProtectedText())
|
||||
return NSAccessibilitySecureTextFieldSubrole;
|
||||
|
||||
const auto role = handler->getRole();
|
||||
|
||||
if (role == AccessibilityRole::window) return NSAccessibilityStandardWindowSubrole;
|
||||
if (role == AccessibilityRole::dialogWindow) return NSAccessibilityDialogSubrole;
|
||||
if (role == AccessibilityRole::tooltip
|
||||
|| role == AccessibilityRole::splashScreen) return NSAccessibilityFloatingWindowSubrole;
|
||||
if (role == AccessibilityRole::toggleButton) return NSAccessibilityToggleSubrole;
|
||||
if (role == AccessibilityRole::treeItem
|
||||
|| role == AccessibilityRole::listItem) return NSAccessibilityOutlineRowSubrole;
|
||||
if (role == AccessibilityRole::row && getCellInterface (self) != nullptr) return NSAccessibilityTableRowSubrole;
|
||||
|
||||
const auto& component = handler->getComponent();
|
||||
|
||||
if (auto* documentWindow = component.findParentComponentOfClass<DocumentWindow>())
|
||||
{
|
||||
if (role == AccessibilityRole::button)
|
||||
{
|
||||
if (&component == documentWindow->getCloseButton()) return NSAccessibilityCloseButtonSubrole;
|
||||
if (&component == documentWindow->getMinimiseButton()) return NSAccessibilityMinimizeButtonSubrole;
|
||||
if (&component == documentWindow->getMaximiseButton()) return NSAccessibilityFullScreenButtonSubrole;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NSAccessibilityUnknownRole;
|
||||
}
|
||||
|
||||
static NSString* getAccessibilityLabel (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return juceStringToNS (handler->getDescription());
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static NSInteger getAccessibilityInsertionPointLineNumber (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return [self accessibilityLineForIndex: textInterface->getTextInsertionOffset()];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityVisibleCharacterRange (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return juceRangeToNS ({ 0, textInterface->getTotalNumCharacters() });
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityNumberOfCharacters (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return textInterface->getTotalNumCharacters();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NSString* getAccessibilitySelectedText (id self, SEL)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return juceStringToNS (textInterface->getText (textInterface->getSelection()));
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSRange getAccessibilitySelectedTextRange (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);
|
||||
}
|
||||
|
||||
static NSAttributedString* getAccessibilityAttributedStringForRange (id self, SEL, NSRange range)
|
||||
{
|
||||
NSString* string = [self accessibilityStringForRange: range];
|
||||
|
||||
if (string != nil)
|
||||
return [[[NSAttributedString alloc] initWithString: string] autorelease];
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityRangeForLine (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);
|
||||
}
|
||||
|
||||
static NSString* getAccessibilityStringForRange (id self, SEL, NSRange range)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return juceStringToNS (textInterface->getText (nsRangeToJuce (range)));
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityRangeForPosition (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);
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityRangeForIndex (id self, SEL, NSInteger index)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
if (isPositiveAndBelow (index, textInterface->getTotalNumCharacters()))
|
||||
return NSMakeRange ((NSUInteger) index, 1);
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
}
|
||||
|
||||
static NSRect getAccessibilityFrameForRange (id self, SEL, NSRange range)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
return flippedScreenRect (makeNSRect (textInterface->getTextBounds (nsRangeToJuce (range)).getBounds()));
|
||||
|
||||
return NSZeroRect;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityLineForIndex (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;
|
||||
}
|
||||
|
||||
static void setAccessibilitySelectedTextRange (id self, SEL, NSRange selectedRange)
|
||||
{
|
||||
if (auto* textInterface = getTextInterface (self))
|
||||
textInterface->setSelection (nsRangeToJuce (selectedRange));
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static NSArray* getAccessibilityRows (id self, SEL)
|
||||
{
|
||||
NSMutableArray* rows = [[NSMutableArray new] autorelease];
|
||||
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
{
|
||||
for (int row = 0; row < tableInterface->getNumRows(); ++row)
|
||||
{
|
||||
if (auto* handler = tableInterface->getCellHandler (row, 0))
|
||||
{
|
||||
[rows addObject: (id) handler->getNativeImplementation()];
|
||||
}
|
||||
else
|
||||
{
|
||||
[rows addObject: [NSAccessibilityElement accessibilityElementWithRole: NSAccessibilityRowRole
|
||||
frame: NSZeroRect
|
||||
label: @"Offscreen Row"
|
||||
parent: self]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
static NSArray* getAccessibilitySelectedRows (id self, SEL)
|
||||
{
|
||||
return getSelectedChildren ([self accessibilityRows]);
|
||||
}
|
||||
|
||||
static void setAccessibilitySelectedRows (id self, SEL, NSArray* selected)
|
||||
{
|
||||
setSelectedChildren ([self accessibilityRows], selected);
|
||||
}
|
||||
|
||||
static NSArray* getAccessibilityColumns (id self, SEL)
|
||||
{
|
||||
NSMutableArray* columns = [[NSMutableArray new] autorelease];
|
||||
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
{
|
||||
for (int column = 0; column < tableInterface->getNumColumns(); ++column)
|
||||
{
|
||||
if (auto* handler = tableInterface->getCellHandler (0, column))
|
||||
{
|
||||
[columns addObject: (id) handler->getNativeImplementation()];
|
||||
}
|
||||
else
|
||||
{
|
||||
[columns addObject: [NSAccessibilityElement accessibilityElementWithRole: NSAccessibilityColumnRole
|
||||
frame: NSZeroRect
|
||||
label: @"Offscreen Column"
|
||||
parent: self]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return columns;
|
||||
}
|
||||
|
||||
static NSArray* getAccessibilitySelectedColumns (id self, SEL)
|
||||
{
|
||||
return getSelectedChildren ([self accessibilityColumns]);
|
||||
}
|
||||
|
||||
static void setAccessibilitySelectedColumns (id self, SEL, NSArray* selected)
|
||||
{
|
||||
setSelectedChildren ([self accessibilityColumns], selected);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static NSInteger getAccessibilityIndex (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* cellInterface = handler->getCellInterface())
|
||||
{
|
||||
NSAccessibilityRole role = [self accessibilityRole];
|
||||
|
||||
if ([role isEqual: NSAccessibilityRowRole])
|
||||
return cellInterface->getRowIndex();
|
||||
|
||||
if ([role isEqual: NSAccessibilityColumnRole])
|
||||
return cellInterface->getColumnIndex();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityDisclosureLevel (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
if (auto* cellInterface = handler->getCellInterface())
|
||||
return cellInterface->getDisclosureLevel();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL getIsAccessibilityExpanded (id self, SEL)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
return handler->getCurrentState().isExpanded();
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static BOOL accessibilityPerformShowMenu (id self, SEL) { return performActionIfSupported (self, AccessibilityActionType::showMenu); }
|
||||
static BOOL accessibilityPerformRaise (id self, SEL) { [self setAccessibilityFocused: YES]; return YES; }
|
||||
|
||||
static BOOL 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;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
static BOOL getIsAccessibilitySelectorAllowed (id self, SEL, SEL selector)
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
const auto role = 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 (accessibilityColumns),
|
||||
@selector (accessibilitySelectedColumns) })
|
||||
{
|
||||
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->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 role == AccessibilityRole::popupMenu;
|
||||
|
||||
if (selector == @selector (accessibilityOrientation))
|
||||
return role == AccessibilityRole::scrollBar;
|
||||
|
||||
if (selector == @selector (isAccessibilityExpanded))
|
||||
return currentState.isExpandable();
|
||||
|
||||
return sendSuperclassMessage<BOOL> (self, @selector (isAccessibilitySelectorAllowed:), selector);
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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()
|
||||
{
|
||||
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 (id accessibilityElement = (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 notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
|
||||
{
|
||||
auto notification = [eventType]
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case InternalAccessibilityEvent::elementCreated: return NSAccessibilityCreatedNotification;
|
||||
case InternalAccessibilityEvent::elementDestroyed: return NSAccessibilityUIElementDestroyedNotification;
|
||||
case InternalAccessibilityEvent::elementMovedOrResized: return layoutChangedNotification();
|
||||
case InternalAccessibilityEvent::focusChanged: return NSAccessibilityFocusedUIElementChangedNotification;
|
||||
case InternalAccessibilityEvent::windowOpened: return NSAccessibilityWindowCreatedNotification;
|
||||
case InternalAccessibilityEvent::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.10, *))
|
||||
{
|
||||
auto nsPriority = [priority]
|
||||
{
|
||||
switch (priority)
|
||||
{
|
||||
case AnnouncementPriority::low: return NSAccessibilityPriorityLow;
|
||||
case AnnouncementPriority::medium: return NSAccessibilityPriorityMedium;
|
||||
case AnnouncementPriority::high: return NSAccessibilityPriorityHigh;
|
||||
}
|
||||
|
||||
jassertfalse;
|
||||
return NSAccessibilityPriorityLow;
|
||||
}();
|
||||
|
||||
sendAccessibilityEvent ((id) [NSApp mainWindow],
|
||||
NSAccessibilityAnnouncementRequestedNotification,
|
||||
@{ NSAccessibilityAnnouncementKey: juceStringToNS (announcementString),
|
||||
NSAccessibilityPriorityKey: @(nsPriority) });
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
249
deps/juce/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm
vendored
Normal file
249
deps/juce/modules/juce_gui_basics/native/accessibility/juce_mac_AccessibilitySharedCode.mm
vendored
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
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() : ObjCClass<Base> ("JUCEAccessibilityElement_")
|
||||
{
|
||||
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)
|
||||
{
|
||||
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 = getAccessibleApplicationOrPluginName();
|
||||
|
||||
NSString* nsString = juceStringToNS (title);
|
||||
|
||||
#if ! JUCE_IOS
|
||||
if (nsString != nil && [[self accessibilityValue] isEqual: nsString])
|
||||
return @"";
|
||||
#endif
|
||||
|
||||
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* tableInterface = getTableInterface (self))
|
||||
return tableInterface->getNumRows();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityColumnCount (id self, SEL)
|
||||
{
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
return tableInterface->getNumColumns();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityRowIndexRange (id self, SEL)
|
||||
{
|
||||
if (auto* cellInterface = getCellInterface (self))
|
||||
return NSMakeRange ((NSUInteger) cellInterface->getRowIndex(),
|
||||
(NSUInteger) cellInterface->getRowSpan());
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityColumnIndexRange (id self, SEL)
|
||||
{
|
||||
if (auto* cellInterface = getCellInterface (self))
|
||||
return NSMakeRange ((NSUInteger) cellInterface->getColumnIndex(),
|
||||
(NSUInteger) cellInterface->getColumnSpan());
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibleObjCClass)
|
||||
};
|
||||
|
||||
}
|
301
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp
vendored
Normal file
301
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_Accessibility.cpp
vendored
Normal file
@ -0,0 +1,301 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#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()
|
||||
{
|
||||
accessibilityElement->invalidateElement();
|
||||
--providerCount;
|
||||
|
||||
if (auto* uiaWrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
{
|
||||
ComSmartPtr<IRawElementProviderSimple> provider;
|
||||
accessibilityElement->QueryInterface (IID_PPV_ARGS (provider.resetAndGetPointerAddress()));
|
||||
|
||||
uiaWrapper->disconnectProvider (provider);
|
||||
|
||||
if (providerCount == 0)
|
||||
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 notifyAccessibilityEventInternal (const AccessibilityHandler& handler, InternalAccessibilityEvent eventType)
|
||||
{
|
||||
if (eventType == InternalAccessibilityEvent::elementCreated
|
||||
|| eventType == InternalAccessibilityEvent::elementDestroyed)
|
||||
{
|
||||
if (auto* parent = handler.getParent())
|
||||
sendAccessibilityAutomationEvent (*parent, UIA_LayoutInvalidatedEventId);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
auto event = [eventType]() -> EVENTID
|
||||
{
|
||||
switch (eventType)
|
||||
{
|
||||
case InternalAccessibilityEvent::focusChanged: return UIA_AutomationFocusChangedEventId;
|
||||
case InternalAccessibilityEvent::windowOpened: return UIA_Window_WindowOpenedEventId;
|
||||
case InternalAccessibilityEvent::windowClosed: return UIA_Window_WindowClosedEventId;
|
||||
case InternalAccessibilityEvent::elementCreated:
|
||||
case InternalAccessibilityEvent::elementDestroyed:
|
||||
case InternalAccessibilityEvent::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);
|
||||
}
|
||||
|
||||
auto event = [eventType]() -> EVENTID
|
||||
{
|
||||
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 : public DeletedAtShutdown
|
||||
{
|
||||
SpVoiceWrapper()
|
||||
{
|
||||
auto hr = voice.CoCreateInstance (CLSID_SpVoice);
|
||||
|
||||
jassertquiet (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
|
||||
{
|
||||
long getUiaRootObjectId()
|
||||
{
|
||||
return static_cast<long> (UiaRootObjectId);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
553
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp
vendored
Normal file
553
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.cpp
vendored
Normal file
@ -0,0 +1,553 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
int AccessibilityNativeHandle::idCounter = 0;
|
||||
|
||||
//==============================================================================
|
||||
static String getAutomationId (const AccessibilityHandler& handler)
|
||||
{
|
||||
auto result = handler.getTitle();
|
||||
auto* parentComponent = handler.getComponent().getParentComponent();
|
||||
|
||||
while (parentComponent != nullptr)
|
||||
{
|
||||
if (auto* parentHandler = parentComponent->getAccessibilityHandler())
|
||||
{
|
||||
auto parentTitle = parentHandler->getTitle();
|
||||
result << "." << (parentTitle.isNotEmpty() ? parentTitle : "<empty>");
|
||||
}
|
||||
|
||||
parentComponent = parentComponent->getParentComponent();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static auto roleToControlTypeId (AccessibilityRole roleType)
|
||||
{
|
||||
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_HeaderItemControlTypeId;
|
||||
|
||||
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 (IRawElementProviderFragmentRoot) && ! isFragmentRoot()))
|
||||
return E_NOINTERFACE;
|
||||
|
||||
return ComBaseClassHelper::QueryInterface (refId, result);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::get_HostRawElementProvider (IRawElementProviderSimple** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (isFragmentRoot())
|
||||
if (auto* wrapper = WindowsUIAWrapper::getInstanceWithoutCreating())
|
||||
return wrapper->hostProviderFromHwnd ((HWND) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::get_ProviderOptions (ProviderOptions* options)
|
||||
{
|
||||
if (options == nullptr)
|
||||
return E_INVALIDARG;
|
||||
|
||||
*options = ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUnknown** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = [&]() -> IUnknown*
|
||||
{
|
||||
const auto role = accessibilityHandler.getRole();
|
||||
const auto fragmentRoot = isFragmentRoot();
|
||||
|
||||
switch (pId)
|
||||
{
|
||||
case UIA_WindowPatternId:
|
||||
{
|
||||
if (fragmentRoot)
|
||||
return new UIAWindowProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_TransformPatternId:
|
||||
{
|
||||
if (fragmentRoot)
|
||||
return new UIATransformProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_TextPatternId:
|
||||
case UIA_TextPattern2Id:
|
||||
{
|
||||
if (accessibilityHandler.getTextInterface() != nullptr)
|
||||
return new UIATextProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_ValuePatternId:
|
||||
{
|
||||
if (accessibilityHandler.getValueInterface() != nullptr)
|
||||
return new UIAValueProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_RangeValuePatternId:
|
||||
{
|
||||
if (accessibilityHandler.getValueInterface() != nullptr
|
||||
&& accessibilityHandler.getValueInterface()->getRange().isValid())
|
||||
{
|
||||
return new UIARangeValueProvider (this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_TogglePatternId:
|
||||
{
|
||||
if (accessibilityHandler.getActions().contains (AccessibilityActionType::toggle)
|
||||
&& accessibilityHandler.getCurrentState().isCheckable())
|
||||
{
|
||||
return new UIAToggleProvider (this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_SelectionPatternId:
|
||||
{
|
||||
if (role == AccessibilityRole::list
|
||||
|| role == AccessibilityRole::popupMenu
|
||||
|| role == AccessibilityRole::tree)
|
||||
{
|
||||
return new UIASelectionProvider (this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_SelectionItemPatternId:
|
||||
{
|
||||
auto state = accessibilityHandler.getCurrentState();
|
||||
|
||||
if (state.isSelectable() || state.isMultiSelectable()
|
||||
|| role == AccessibilityRole::radioButton)
|
||||
{
|
||||
return new UIASelectionItemProvider (this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_GridPatternId:
|
||||
{
|
||||
if (accessibilityHandler.getTableInterface() != nullptr)
|
||||
return new UIAGridProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_GridItemPatternId:
|
||||
{
|
||||
if (accessibilityHandler.getCellInterface() != nullptr)
|
||||
return new UIAGridItemProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_InvokePatternId:
|
||||
{
|
||||
if (accessibilityHandler.getActions().contains (AccessibilityActionType::press))
|
||||
return new UIAInvokeProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case UIA_ExpandCollapsePatternId:
|
||||
{
|
||||
if (accessibilityHandler.getActions().contains (AccessibilityActionType::showMenu)
|
||||
&& accessibilityHandler.getCurrentState().isExpandable())
|
||||
return new UIAExpandCollapseProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetPropertyValue (PROPERTYID propertyId, VARIANT* pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
VariantHelpers::clear (pRetVal);
|
||||
|
||||
const auto fragmentRoot = isFragmentRoot();
|
||||
|
||||
const auto role = accessibilityHandler.getRole();
|
||||
const auto state = accessibilityHandler.getCurrentState();
|
||||
|
||||
switch (propertyId)
|
||||
{
|
||||
case UIA_AutomationIdPropertyId:
|
||||
VariantHelpers::setString (getAutomationId (accessibilityHandler), pRetVal);
|
||||
break;
|
||||
case UIA_ControlTypePropertyId:
|
||||
VariantHelpers::setInt (roleToControlTypeId (role), pRetVal);
|
||||
break;
|
||||
case UIA_FrameworkIdPropertyId:
|
||||
VariantHelpers::setString ("JUCE", pRetVal);
|
||||
break;
|
||||
case UIA_FullDescriptionPropertyId:
|
||||
VariantHelpers::setString (accessibilityHandler.getDescription(), pRetVal);
|
||||
break;
|
||||
case UIA_HelpTextPropertyId:
|
||||
VariantHelpers::setString (accessibilityHandler.getHelp(), pRetVal);
|
||||
break;
|
||||
case UIA_IsContentElementPropertyId:
|
||||
VariantHelpers::setBool (! accessibilityHandler.isIgnored() && accessibilityHandler.isVisibleWithinParent(),
|
||||
pRetVal);
|
||||
break;
|
||||
case UIA_IsControlElementPropertyId:
|
||||
VariantHelpers::setBool (true, pRetVal);
|
||||
break;
|
||||
case UIA_IsDialogPropertyId:
|
||||
VariantHelpers::setBool (role == AccessibilityRole::dialogWindow, pRetVal);
|
||||
break;
|
||||
case UIA_IsEnabledPropertyId:
|
||||
VariantHelpers::setBool (accessibilityHandler.getComponent().isEnabled(), pRetVal);
|
||||
break;
|
||||
case UIA_IsKeyboardFocusablePropertyId:
|
||||
VariantHelpers::setBool (state.isFocusable(), pRetVal);
|
||||
break;
|
||||
case UIA_HasKeyboardFocusPropertyId:
|
||||
VariantHelpers::setBool (accessibilityHandler.hasFocus (true), pRetVal);
|
||||
break;
|
||||
case UIA_IsOffscreenPropertyId:
|
||||
VariantHelpers::setBool (! accessibilityHandler.isVisibleWithinParent(), pRetVal);
|
||||
break;
|
||||
case UIA_IsPasswordPropertyId:
|
||||
if (auto* textInterface = accessibilityHandler.getTextInterface())
|
||||
VariantHelpers::setBool (textInterface->isDisplayingProtectedText(), pRetVal);
|
||||
|
||||
break;
|
||||
case UIA_IsPeripheralPropertyId:
|
||||
VariantHelpers::setBool (role == AccessibilityRole::tooltip
|
||||
|| role == AccessibilityRole::popupMenu
|
||||
|| role == AccessibilityRole::splashScreen,
|
||||
pRetVal);
|
||||
break;
|
||||
case UIA_NamePropertyId:
|
||||
VariantHelpers::setString (getElementName(), pRetVal);
|
||||
break;
|
||||
case UIA_ProcessIdPropertyId:
|
||||
VariantHelpers::setInt ((int) GetCurrentProcessId(), pRetVal);
|
||||
break;
|
||||
case UIA_NativeWindowHandlePropertyId:
|
||||
if (fragmentRoot)
|
||||
VariantHelpers::setInt ((int) (pointer_sized_int) accessibilityHandler.getComponent().getWindowHandle(), pRetVal);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::Navigate (NavigateDirection direction, IRawElementProviderFragment** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto* handler = [&]() -> AccessibilityHandler*
|
||||
{
|
||||
if (direction == NavigateDirection_Parent)
|
||||
return accessibilityHandler.getParent();
|
||||
|
||||
if (direction == NavigateDirection_FirstChild
|
||||
|| direction == NavigateDirection_LastChild)
|
||||
{
|
||||
auto children = accessibilityHandler.getChildren();
|
||||
|
||||
return children.empty() ? nullptr
|
||||
: (direction == NavigateDirection_FirstChild ? children.front()
|
||||
: children.back());
|
||||
}
|
||||
|
||||
if (direction == NavigateDirection_NextSibling
|
||||
|| direction == NavigateDirection_PreviousSibling)
|
||||
{
|
||||
if (auto* parent = accessibilityHandler.getParent())
|
||||
{
|
||||
const auto siblings = parent->getChildren();
|
||||
const auto iter = std::find (siblings.cbegin(), siblings.cend(), &accessibilityHandler);
|
||||
|
||||
if (iter == siblings.end())
|
||||
return nullptr;
|
||||
|
||||
if (direction == NavigateDirection_NextSibling && iter != std::prev (siblings.cend()))
|
||||
return *std::next (iter);
|
||||
|
||||
if (direction == NavigateDirection_PreviousSibling && iter != siblings.cbegin())
|
||||
return *std::prev (iter);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (handler != nullptr)
|
||||
if (auto* provider = handler->getNativeImplementation())
|
||||
if (provider->isElementValid())
|
||||
provider->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetRuntimeId (SAFEARRAY** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
if (! isFragmentRoot())
|
||||
{
|
||||
*pRetVal = SafeArrayCreateVector (VT_I4, 0, 2);
|
||||
|
||||
if (*pRetVal == nullptr)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
for (LONG i = 0; i < 2; ++i)
|
||||
{
|
||||
auto hr = SafeArrayPutElement (*pRetVal, &i, &rtid[(size_t) i]);
|
||||
|
||||
if (FAILED (hr))
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::get_BoundingRectangle (UiaRect* pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto bounds = Desktop::getInstance().getDisplays()
|
||||
.logicalToPhysical (accessibilityHandler.getComponent().getScreenBounds());
|
||||
|
||||
pRetVal->left = bounds.getX();
|
||||
pRetVal->top = bounds.getY();
|
||||
pRetVal->width = bounds.getWidth();
|
||||
pRetVal->height = bounds.getHeight();
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetEmbeddedFragmentRoots (SAFEARRAY** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, []
|
||||
{
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::SetFocus()
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const WeakReference<Component> safeComponent (&accessibilityHandler.getComponent());
|
||||
|
||||
accessibilityHandler.getActions().invoke (AccessibilityActionType::focus);
|
||||
|
||||
if (safeComponent != nullptr)
|
||||
accessibilityHandler.grabFocus();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::get_FragmentRoot (IRawElementProviderFragmentRoot** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
auto* handler = [&]() -> AccessibilityHandler*
|
||||
{
|
||||
if (isFragmentRoot())
|
||||
return &accessibilityHandler;
|
||||
|
||||
if (auto* peer = accessibilityHandler.getComponent().getPeer())
|
||||
return peer->getComponent().getAccessibilityHandler();
|
||||
|
||||
return nullptr;
|
||||
}();
|
||||
|
||||
if (handler != nullptr)
|
||||
{
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::ElementProviderFromPoint (double x, double y, IRawElementProviderFragment** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto* handler = [&]
|
||||
{
|
||||
auto logicalScreenPoint = Desktop::getInstance().getDisplays()
|
||||
.physicalToLogical (Point<int> (roundToInt (x),
|
||||
roundToInt (y)));
|
||||
|
||||
if (auto* child = accessibilityHandler.getChildAt (logicalScreenPoint))
|
||||
return child;
|
||||
|
||||
return &accessibilityHandler;
|
||||
}();
|
||||
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT AccessibilityNativeHandle::GetFocus (IRawElementProviderFragment** pRetVal)
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
const auto getFocusHandler = [this]() -> AccessibilityHandler*
|
||||
{
|
||||
if (auto* modal = Component::getCurrentlyModalComponent())
|
||||
{
|
||||
const auto& component = accessibilityHandler.getComponent();
|
||||
|
||||
if (! component.isParentOf (modal)
|
||||
&& component.isCurrentlyBlockedByAnotherModalComponent())
|
||||
{
|
||||
if (auto* modalHandler = modal->getAccessibilityHandler())
|
||||
{
|
||||
if (auto* focusChild = modalHandler->getChildFocus())
|
||||
return focusChild;
|
||||
|
||||
return modalHandler;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (auto* focusChild = accessibilityHandler.getChildFocus())
|
||||
return focusChild;
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
|
||||
if (auto* focusHandler = getFocusHandler())
|
||||
focusHandler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
String AccessibilityNativeHandle::getElementName() const
|
||||
{
|
||||
if (accessibilityHandler.getRole() == AccessibilityRole::tooltip)
|
||||
return accessibilityHandler.getDescription();
|
||||
|
||||
auto name = accessibilityHandler.getTitle();
|
||||
|
||||
if (name.isEmpty() && isFragmentRoot())
|
||||
return getAccessibleApplicationOrPluginName();
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
} // namespace juce
|
80
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h
vendored
Normal file
80
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_AccessibilityElement.h
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
#define UIA_FullDescriptionPropertyId 30159
|
||||
#define UIA_IsDialogPropertyId 30174
|
||||
|
||||
class AccessibilityNativeHandle : public ComBaseClassHelper<IRawElementProviderSimple,
|
||||
IRawElementProviderFragment,
|
||||
IRawElementProviderFragmentRoot>
|
||||
{
|
||||
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 (NavigateDirection direction, IRawElementProviderFragment** pRetVal) override;
|
||||
JUCE_COMRESULT GetRuntimeId (SAFEARRAY** pRetVal) override;
|
||||
JUCE_COMRESULT get_BoundingRectangle (UiaRect* pRetVal) override;
|
||||
JUCE_COMRESULT GetEmbeddedFragmentRoots (SAFEARRAY** pRetVal) override;
|
||||
JUCE_COMRESULT SetFocus() override;
|
||||
JUCE_COMRESULT get_FragmentRoot (IRawElementProviderFragmentRoot** pRetVal) override;
|
||||
|
||||
JUCE_COMRESULT ElementProviderFromPoint (double x, double y, IRawElementProviderFragment** pRetVal) override;
|
||||
JUCE_COMRESULT GetFocus (IRawElementProviderFragment** 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)
|
||||
};
|
||||
|
||||
}
|
86
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAExpandCollapseProvider.h
vendored
Normal file
86
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAExpandCollapseProvider.h
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAExpandCollapseProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IExpandCollapseProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIAExpandCollapseProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT Expand() override
|
||||
{
|
||||
return invokeShowMenu();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Collapse() override
|
||||
{
|
||||
return invokeShowMenu();
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ExpandCollapseState (ExpandCollapseState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = getHandler().getCurrentState().isExpanded()
|
||||
? ExpandCollapseState_Expanded
|
||||
: 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))
|
||||
{
|
||||
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
|
105
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridItemProvider.h
vendored
Normal file
105
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridItemProvider.h
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAGridItemProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IGridItemProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIAGridItemProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT get_Row (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getRowIndex();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_Column (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getColumnIndex();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_RowSpan (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getRowSpan();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ColumnSpan (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getColumnSpan();
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ContainingGrid (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (auto* handler = cellInterface.getTableHandler())
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withCellInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* cellInterface = getHandler().getCellInterface())
|
||||
{
|
||||
callback (*cellInterface);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAGridItemProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
94
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridProvider.h
vendored
Normal file
94
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAGridProvider.h
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAGridProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IGridProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIAGridProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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* handler = tableInterface.getCellHandler (row, column))
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withTableInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* tableInterface = getHandler().getTableInterface())
|
||||
return callback (*tableInterface);
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAGridProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
105
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAHelpers.h
vendored
Normal file
105
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAHelpers.h
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
namespace VariantHelpers
|
||||
{
|
||||
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 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
|
62
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAInvokeProvider.h
vendored
Normal file
62
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAInvokeProvider.h
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAInvokeProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IInvokeProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIAInvokeProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT Invoke() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (handler.getActions().invoke (AccessibilityActionType::press))
|
||||
{
|
||||
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
|
58
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAProviderBase.h
vendored
Normal file
58
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAProviderBase.h
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
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
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAProviders.h
vendored
Normal file
43
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAProviders.h
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
void sendAccessibilityAutomationEvent (const AccessibilityHandler&, EVENTID);
|
||||
void sendAccessibilityPropertyChangedEvent (const AccessibilityHandler&, PROPERTYID, VARIANT);
|
||||
}
|
||||
|
||||
#include "juce_win32_UIAProviderBase.h"
|
||||
#include "juce_win32_UIAExpandCollapseProvider.h"
|
||||
#include "juce_win32_UIAGridItemProvider.h"
|
||||
#include "juce_win32_UIAGridProvider.h"
|
||||
#include "juce_win32_UIAInvokeProvider.h"
|
||||
#include "juce_win32_UIARangeValueProvider.h"
|
||||
#include "juce_win32_UIASelectionProvider.h"
|
||||
#include "juce_win32_UIATextProvider.h"
|
||||
#include "juce_win32_UIAToggleProvider.h"
|
||||
#include "juce_win32_UIATransformProvider.h"
|
||||
#include "juce_win32_UIAValueProvider.h"
|
||||
#include "juce_win32_UIAWindowProvider.h"
|
140
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIARangeValueProvider.h
vendored
Normal file
140
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIARangeValueProvider.h
vendored
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIARangeValueProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IRangeValueProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIARangeValueProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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
|
256
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIASelectionProvider.h
vendored
Normal file
256
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIASelectionProvider.h
vendored
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class UIASelectionItemProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ISelectionItemProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIASelectionItemProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle),
|
||||
isRadioButton (getHandler().getRole() == AccessibilityRole::radioButton)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT AddToSelection() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (isRadioButton)
|
||||
{
|
||||
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<ISelectionProvider2>
|
||||
{
|
||||
public:
|
||||
explicit UIASelectionProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT QueryInterface (REFIID iid, void** result) override
|
||||
{
|
||||
if (iid == _uuidof (IUnknown) || iid == _uuidof (ISelectionProvider))
|
||||
return castToType<ISelectionProvider> (result);
|
||||
|
||||
if (iid == _uuidof (ISelectionProvider2))
|
||||
return castToType<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
|
592
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIATextProvider.h
vendored
Normal file
592
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIATextProvider.h
vendored
Normal file
@ -0,0 +1,592 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIATextProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ITextProvider2>
|
||||
{
|
||||
public:
|
||||
explicit UIATextProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT QueryInterface (REFIID iid, void** result) override
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (iid == _uuidof (IUnknown) || iid == _uuidof (ITextProvider))
|
||||
return castToType<ITextProvider> (result);
|
||||
|
||||
if (iid == _uuidof (ITextProvider2))
|
||||
return castToType<ITextProvider2> (result);
|
||||
|
||||
*result = nullptr;
|
||||
return E_NOINTERFACE;
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
|
||||
//=============================================================================
|
||||
JUCE_COMRESULT get_DocumentRange (ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
*pRetVal = new UIATextRangeProvider (*this, { 0, textInterface.getTotalNumCharacters() });
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_SupportedTextSelection (SupportedTextSelection* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = 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*, ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, []
|
||||
{
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT RangeFromPoint (UiaPoint point, 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, 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*, 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<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 (ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = new UIATextRangeProvider (*owner, selectionRange);
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT Compare (ITextRangeProvider* range, BOOL* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = (selectionRange == static_cast<UIATextRangeProvider*> (range)->getSelectionRange());
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT CompareEndpoints (TextPatternRangeEndpoint endpoint,
|
||||
ITextRangeProvider* targetRange,
|
||||
TextPatternRangeEndpoint targetEndpoint,
|
||||
int* pRetVal) override
|
||||
{
|
||||
if (targetRange == nullptr)
|
||||
return E_INVALIDARG;
|
||||
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
auto offset = (endpoint == TextPatternRangeEndpoint_Start ? selectionRange.getStart()
|
||||
: selectionRange.getEnd());
|
||||
|
||||
auto otherRange = static_cast<UIATextRangeProvider*> (targetRange)->getSelectionRange();
|
||||
auto otherOffset = (targetEndpoint == TextPatternRangeEndpoint_Start ? otherRange.getStart()
|
||||
: otherRange.getEnd());
|
||||
|
||||
*pRetVal = offset - otherOffset;
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT ExpandToEnclosingUnit (TextUnit unit) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* textInterface = owner->getHandler().getTextInterface())
|
||||
{
|
||||
const auto boundaryType = getBoundaryType (unit);
|
||||
|
||||
const auto start = AccessibilityTextHelpers::findTextBoundary (*textInterface,
|
||||
selectionRange.getStart(),
|
||||
boundaryType,
|
||||
AccessibilityTextHelpers::Direction::backwards);
|
||||
|
||||
const auto end = AccessibilityTextHelpers::findTextBoundary (*textInterface,
|
||||
start,
|
||||
boundaryType,
|
||||
AccessibilityTextHelpers::Direction::forwards);
|
||||
|
||||
selectionRange = Range<int> (start, end);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT FindAttribute (TEXTATTRIBUTEID, VARIANT, BOOL, ITextRangeProvider** pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, []
|
||||
{
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT FindText (BSTR text, BOOL backward, BOOL ignoreCase,
|
||||
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);
|
||||
|
||||
switch (attributeId)
|
||||
{
|
||||
case UIA_IsReadOnlyAttributeId:
|
||||
{
|
||||
VariantHelpers::setBool (textInterface.isReadOnly(), pRetVal);
|
||||
break;
|
||||
}
|
||||
|
||||
case UIA_CaretPositionAttributeId:
|
||||
{
|
||||
auto cursorPos = textInterface.getTextInsertionOffset();
|
||||
|
||||
auto caretPos = [&]
|
||||
{
|
||||
if (cursorPos == 0)
|
||||
return CaretPosition_BeginningOfLine;
|
||||
|
||||
if (cursorPos == textInterface.getTotalNumCharacters())
|
||||
return CaretPosition_EndOfLine;
|
||||
|
||||
return 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 (TextUnit unit, int count, int* pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface&)
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
MoveEndpointByUnit (TextPatternRangeEndpoint_End, unit, count, pRetVal);
|
||||
MoveEndpointByUnit (TextPatternRangeEndpoint_Start, unit, count, pRetVal);
|
||||
}
|
||||
else if (count < 0)
|
||||
{
|
||||
MoveEndpointByUnit (TextPatternRangeEndpoint_Start, unit, count, pRetVal);
|
||||
MoveEndpointByUnit (TextPatternRangeEndpoint_End, unit, count, pRetVal);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT MoveEndpointByRange (TextPatternRangeEndpoint endpoint,
|
||||
ITextRangeProvider* targetRange,
|
||||
TextPatternRangeEndpoint targetEndpoint) override
|
||||
{
|
||||
if (targetRange == nullptr)
|
||||
return E_INVALIDARG;
|
||||
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* textInterface = owner->getHandler().getTextInterface())
|
||||
{
|
||||
auto otherRange = static_cast<UIATextRangeProvider*> (targetRange)->getSelectionRange();
|
||||
auto targetPoint = (targetEndpoint == TextPatternRangeEndpoint_Start ? otherRange.getStart()
|
||||
: otherRange.getEnd());
|
||||
|
||||
setEndpointChecked (endpoint, targetPoint);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT MoveEndpointByUnit (TextPatternRangeEndpoint endpoint,
|
||||
TextUnit unit,
|
||||
int count,
|
||||
int* pRetVal) override
|
||||
{
|
||||
return owner->withTextInterface (pRetVal, [&] (const AccessibilityTextInterface& textInterface)
|
||||
{
|
||||
if (count == 0 || textInterface.getTotalNumCharacters() == 0)
|
||||
return S_OK;
|
||||
|
||||
auto endpointToMove = (endpoint == TextPatternRangeEndpoint_Start ? selectionRange.getStart()
|
||||
: selectionRange.getEnd());
|
||||
|
||||
const auto direction = (count > 0 ? AccessibilityTextHelpers::Direction::forwards
|
||||
: AccessibilityTextHelpers::Direction::backwards);
|
||||
|
||||
const auto boundaryType = getBoundaryType (unit);
|
||||
|
||||
// handle case where endpoint is on a boundary
|
||||
if (AccessibilityTextHelpers::findTextBoundary (textInterface, endpointToMove, boundaryType, direction) == endpointToMove)
|
||||
endpointToMove += (direction == AccessibilityTextHelpers::Direction::forwards ? 1 : -1);
|
||||
|
||||
int numMoved;
|
||||
for (numMoved = 0; numMoved < std::abs (count); ++numMoved)
|
||||
{
|
||||
auto nextEndpoint = AccessibilityTextHelpers::findTextBoundary (textInterface,
|
||||
endpointToMove,
|
||||
boundaryType,
|
||||
direction);
|
||||
|
||||
if (nextEndpoint == endpointToMove)
|
||||
break;
|
||||
|
||||
endpointToMove = nextEndpoint;
|
||||
}
|
||||
|
||||
*pRetVal = numMoved;
|
||||
setEndpointChecked (endpoint, endpointToMove);
|
||||
|
||||
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 (TextUnit unit)
|
||||
{
|
||||
switch (unit)
|
||||
{
|
||||
case TextUnit_Character:
|
||||
return AccessibilityTextHelpers::BoundaryType::character;
|
||||
|
||||
case TextUnit_Format:
|
||||
case TextUnit_Word:
|
||||
return AccessibilityTextHelpers::BoundaryType::word;
|
||||
|
||||
case TextUnit_Line:
|
||||
return AccessibilityTextHelpers::BoundaryType::line;
|
||||
|
||||
case TextUnit_Paragraph:
|
||||
case TextUnit_Page:
|
||||
case TextUnit_Document:
|
||||
return AccessibilityTextHelpers::BoundaryType::document;
|
||||
};
|
||||
|
||||
jassertfalse;
|
||||
return AccessibilityTextHelpers::BoundaryType::character;
|
||||
}
|
||||
|
||||
void setEndpointChecked (TextPatternRangeEndpoint endpoint, int newEndpoint)
|
||||
{
|
||||
if (endpoint == 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
|
80
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAToggleProvider.h
vendored
Normal file
80
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAToggleProvider.h
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAToggleProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IToggleProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIAToggleProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT Toggle() override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
const auto& handler = getHandler();
|
||||
|
||||
if (handler.getActions().invoke (AccessibilityActionType::toggle))
|
||||
{
|
||||
VARIANT newValue;
|
||||
VariantHelpers::setInt (getCurrentToggleState(), &newValue);
|
||||
|
||||
sendAccessibilityPropertyChangedEvent (handler, UIA_ToggleToggleStatePropertyId, newValue);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ToggleState (ToggleState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]
|
||||
{
|
||||
*pRetVal = getCurrentToggleState();
|
||||
return S_OK;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
ToggleState getCurrentToggleState() const
|
||||
{
|
||||
return getHandler().getCurrentState().isChecked() ? ToggleState_On
|
||||
: ToggleState_Off;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAToggleProvider)
|
||||
};
|
||||
|
||||
} // namespace juce
|
125
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIATransformProvider.h
vendored
Normal file
125
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIATransformProvider.h
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIATransformProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ITransformProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIATransformProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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
|
86
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAValueProvider.h
vendored
Normal file
86
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAValueProvider.h
vendored
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAValueProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IValueProvider>
|
||||
{
|
||||
public:
|
||||
UIAValueProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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
|
197
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAWindowProvider.h
vendored
Normal file
197
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_UIAWindowProvider.h
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
//==============================================================================
|
||||
class UIAWindowProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<IWindowProvider>
|
||||
{
|
||||
public:
|
||||
explicit UIAWindowProvider (AccessibilityNativeHandle* nativeHandle)
|
||||
: UIAProviderBase (nativeHandle)
|
||||
{
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_COMRESULT SetVisualState (WindowVisualState state) override
|
||||
{
|
||||
if (! isElementValid())
|
||||
return (HRESULT) UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case WindowVisualState_Maximized:
|
||||
peer->setFullScreen (true);
|
||||
break;
|
||||
|
||||
case WindowVisualState_Minimized:
|
||||
peer->setMinimised (true);
|
||||
break;
|
||||
|
||||
case 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 (WindowVisualState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
if (peer->isFullScreen())
|
||||
*pRetVal = WindowVisualState_Maximized;
|
||||
else if (peer->isMinimised())
|
||||
*pRetVal = WindowVisualState_Minimized;
|
||||
else
|
||||
*pRetVal = WindowVisualState_Normal;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_WindowInteractionState (WindowInteractionState* pRetVal) override
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* peer = getPeer())
|
||||
{
|
||||
*pRetVal = peer->getComponent().isCurrentlyBlockedByAnotherModalComponent()
|
||||
? WindowInteractionState::WindowInteractionState_BlockedByModalWindow
|
||||
: 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
|
158
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h
vendored
Normal file
158
deps/juce/modules/juce_gui_basics/native/accessibility/juce_win32_WindowsUIAWrapper.h
vendored
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library.
|
||||
Copyright (c) 2020 - Raw Material Software Limited
|
||||
|
||||
JUCE is an open source library subject to commercial or open-source
|
||||
licensing.
|
||||
|
||||
By using JUCE, you agree to the terms of both the JUCE 6 End-User License
|
||||
Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
|
||||
|
||||
End User License Agreement: www.juce.com/juce-6-licence
|
||||
Privacy Policy: www.juce.com/juce-privacy-policy
|
||||
|
||||
Or: You may also use this code under the terms of the GPL v3 (see
|
||||
www.gnu.org/licenses).
|
||||
|
||||
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
|
||||
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
|
||||
DISCLAIMED.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
namespace juce
|
||||
{
|
||||
|
||||
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, StringRef funcName)
|
||||
{
|
||||
return (FuncType) GetProcAddress (module, funcName);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
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