mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-02-03 03:30:06 +00:00
Accessibility: Improve table navigation, row/column index/header reporting
This commit is contained in:
parent
dd92f66387
commit
921d86e586
16 changed files with 775 additions and 204 deletions
|
|
@ -39,18 +39,6 @@ public:
|
|||
/** Destructor. */
|
||||
virtual ~AccessibilityCellInterface() = default;
|
||||
|
||||
/** Returns the column index of the cell in the table. */
|
||||
virtual int getColumnIndex() const = 0;
|
||||
|
||||
/** Returns the number of columns occupied by the cell in the table. */
|
||||
virtual int getColumnSpan() const = 0;
|
||||
|
||||
/** Returns the row index of the cell in the table. */
|
||||
virtual int getRowIndex() const = 0;
|
||||
|
||||
/** Returns the number of rows occupied by the cell in the table. */
|
||||
virtual int getRowSpan() const = 0;
|
||||
|
||||
/** Returns the indentation level for the cell. */
|
||||
virtual int getDisclosureLevel() const = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,33 @@ public:
|
|||
as there are columns in the table.
|
||||
*/
|
||||
virtual const AccessibilityHandler* getHeaderHandler() const = 0;
|
||||
|
||||
struct Span { int begin, num; };
|
||||
|
||||
/** Given the handler of one of the cells in the table, returns the rows covered
|
||||
by that cell, or null if the cell does not exist in the table.
|
||||
|
||||
This function replaces the getRowIndex and getRowSpan
|
||||
functions from AccessibilityCellInterface. Most of the time, it's easier for the
|
||||
table itself to keep track of cell locations, than to delegate to the individual
|
||||
cells.
|
||||
*/
|
||||
virtual Optional<Span> getRowSpan (const AccessibilityHandler&) const = 0;
|
||||
|
||||
/** Given the handler of one of the cells in the table, returns the columns covered
|
||||
by that cell, or null if the cell does not exist in the table.
|
||||
|
||||
This function replaces the getColumnIndex and getColumnSpan
|
||||
functions from AccessibilityCellInterface. Most of the time, it's easier for the
|
||||
table itself to keep track of cell locations, than to delegate to the individual
|
||||
cells.
|
||||
*/
|
||||
virtual Optional<Span> getColumnSpan (const AccessibilityHandler&) const = 0;
|
||||
|
||||
/** Attempts to scroll the table (if necessary) so that the cell with the given handler
|
||||
is visible.
|
||||
*/
|
||||
virtual void showCell (const AccessibilityHandler&) const = 0;
|
||||
};
|
||||
|
||||
} // namespace juce
|
||||
|
|
|
|||
|
|
@ -127,6 +127,18 @@ namespace juce
|
|||
ScaledImage image;
|
||||
Point<int> hotspot;
|
||||
};
|
||||
|
||||
template <typename MemberFn>
|
||||
static const AccessibilityHandler* getEnclosingHandlerWithInterface (const AccessibilityHandler* handler, MemberFn fn)
|
||||
{
|
||||
if (handler == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if ((handler->*fn)() != nullptr)
|
||||
return handler;
|
||||
|
||||
return getEnclosingHandlerWithInterface (handler->getParent(), fn);
|
||||
}
|
||||
} // namespace juce
|
||||
|
||||
#include "mouse/juce_PointerState.h"
|
||||
|
|
|
|||
|
|
@ -56,6 +56,25 @@ namespace juce
|
|||
DECLARE_JNI_CLASS (AndroidAccessibilityNodeInfo, "android/view/accessibility/AccessibilityNodeInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
METHOD (setCollectionInfo, "setCollectionInfo", "(Landroid/view/accessibility/AccessibilityNodeInfo$CollectionInfo;)V") \
|
||||
METHOD (setCollectionItemInfo, "setCollectionItemInfo", "(Landroid/view/accessibility/AccessibilityNodeInfo$CollectionItemInfo;)V")
|
||||
|
||||
DECLARE_JNI_CLASS (AndroidAccessibilityNodeInfo19, "android/view/accessibility/AccessibilityNodeInfo")
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (obtain, "obtain", "(IIZ)Landroid/view/accessibility/AccessibilityNodeInfo$CollectionInfo;")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidAccessibilityNodeInfoCollectionInfo, "android/view/accessibility/AccessibilityNodeInfo$CollectionInfo", 19)
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD, CALLBACK) \
|
||||
STATICMETHOD (obtain, "obtain", "(IIIIZ)Landroid/view/accessibility/AccessibilityNodeInfo$CollectionItemInfo;")
|
||||
|
||||
DECLARE_JNI_CLASS_WITH_MIN_SDK (AndroidAccessibilityNodeInfoCollectionItemInfo, "android/view/accessibility/AccessibilityNodeInfo$CollectionItemInfo", 19)
|
||||
#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") \
|
||||
|
|
@ -124,6 +143,18 @@ static jmethodID nodeInfoSetTextSelection = nullptr;
|
|||
static jmethodID nodeInfoSetLiveRegion = nullptr;
|
||||
static jmethodID accessibilityEventSetContentChangeTypes = nullptr;
|
||||
|
||||
template <typename MemberFn>
|
||||
static AccessibilityHandler* getEnclosingHandlerWithInterface (AccessibilityHandler* handler, MemberFn fn)
|
||||
{
|
||||
if (handler == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if ((handler->*fn)() != nullptr)
|
||||
return handler;
|
||||
|
||||
return getEnclosingHandlerWithInterface (handler->getParent(), fn);
|
||||
}
|
||||
|
||||
static void loadSDKDependentMethods()
|
||||
{
|
||||
static bool hasChecked = false;
|
||||
|
|
@ -160,8 +191,6 @@ static constexpr auto getClassName (AccessibilityRole role)
|
|||
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:
|
||||
|
|
@ -177,6 +206,11 @@ static constexpr auto getClassName (AccessibilityRole role)
|
|||
case AccessibilityRole::splashScreen:
|
||||
case AccessibilityRole::dialogWindow: return "android.widget.PopupWindow";
|
||||
|
||||
// If we don't supply a custom class type, then TalkBack will use the node's CollectionInfo
|
||||
// to make a sensible decision about how to describe the container
|
||||
case AccessibilityRole::list:
|
||||
case AccessibilityRole::table:
|
||||
|
||||
case AccessibilityRole::column:
|
||||
case AccessibilityRole::row:
|
||||
case AccessibilityRole::cell:
|
||||
|
|
@ -437,6 +471,63 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getAndroidSDKVersion() >= 19)
|
||||
{
|
||||
if (auto* tableInterface = accessibilityHandler.getTableInterface())
|
||||
{
|
||||
const auto rows = tableInterface->getNumRows();
|
||||
const auto columns = tableInterface->getNumColumns();
|
||||
const LocalRef<jobject> collectionInfo { env->CallStaticObjectMethod (AndroidAccessibilityNodeInfoCollectionInfo,
|
||||
AndroidAccessibilityNodeInfoCollectionInfo.obtain,
|
||||
(jint) rows,
|
||||
(jint) columns,
|
||||
(jboolean) false) };
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo19.setCollectionInfo, collectionInfo.get());
|
||||
}
|
||||
|
||||
if (auto* enclosingTableHandler = getEnclosingHandlerWithInterface (&accessibilityHandler, &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
auto* interface = enclosingTableHandler->getTableInterface();
|
||||
jassert (interface != nullptr);
|
||||
const auto rowSpan = interface->getRowSpan (accessibilityHandler);
|
||||
const auto columnSpan = interface->getColumnSpan (accessibilityHandler);
|
||||
|
||||
enum class IsHeader { no, yes };
|
||||
|
||||
const auto addCellInfo = [env, &info] (AccessibilityTableInterface::Span rows, AccessibilityTableInterface::Span columns, IsHeader header)
|
||||
{
|
||||
const LocalRef<jobject> collectionItemInfo { env->CallStaticObjectMethod (AndroidAccessibilityNodeInfoCollectionItemInfo,
|
||||
AndroidAccessibilityNodeInfoCollectionItemInfo.obtain,
|
||||
(jint) rows.begin,
|
||||
(jint) rows.num,
|
||||
(jint) columns.begin,
|
||||
(jint) columns.num,
|
||||
(jboolean) (header == IsHeader::yes)) };
|
||||
env->CallVoidMethod (info, AndroidAccessibilityNodeInfo19.setCollectionItemInfo, collectionItemInfo.get());
|
||||
};
|
||||
|
||||
if (rowSpan.hasValue() && columnSpan.hasValue())
|
||||
{
|
||||
addCellInfo (*rowSpan, *columnSpan, IsHeader::no);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (auto* tableHeader = interface->getHeaderHandler())
|
||||
{
|
||||
if (accessibilityHandler.getParent() == tableHeader)
|
||||
{
|
||||
const auto children = tableHeader->getChildren();
|
||||
const auto column = std::distance (children.cbegin(), std::find (children.cbegin(), children.cend(), &accessibilityHandler));
|
||||
|
||||
// Talkback will only treat a row as a column header if its row index is zero
|
||||
// https://github.com/google/talkback/blob/acd0bc7631a3dfbcf183789c7557596a45319e1f/utils/src/main/java/CollectionState.java#L853
|
||||
addCellInfo ({ 0, 1 }, { (int) column, 1 }, IsHeader::yes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool performAction (int action, jobject arguments)
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ static NSArray* getContainerAccessibilityElements (AccessibilityHandler& handler
|
|||
{
|
||||
id accessibleElement = [&childHandler]
|
||||
{
|
||||
id native = (id) childHandler->getNativeImplementation();
|
||||
id native = static_cast<id> (childHandler->getNativeImplementation());
|
||||
|
||||
if (! childHandler->getChildren().empty())
|
||||
return [native accessibilityContainer];
|
||||
|
|
@ -66,7 +66,7 @@ static NSArray* getContainerAccessibilityElements (AccessibilityHandler& handler
|
|||
[accessibleChildren addObject: accessibleElement];
|
||||
}
|
||||
|
||||
[accessibleChildren addObject: (id) handler.getNativeImplementation()];
|
||||
[accessibleChildren addObject: static_cast<id> (handler.getNativeImplementation())];
|
||||
|
||||
return accessibleChildren;
|
||||
}
|
||||
|
|
@ -87,11 +87,11 @@ public:
|
|||
|
||||
private:
|
||||
//==============================================================================
|
||||
class AccessibilityContainer : public ObjCClass<NSObject>
|
||||
class AccessibilityContainer : public AccessibleObjCClass<NSObject>
|
||||
{
|
||||
public:
|
||||
AccessibilityContainer()
|
||||
: ObjCClass ("JUCEUIAccessibilityElementContainer_")
|
||||
: AccessibleObjCClass ("JUCEUIAccessibilityContainer_")
|
||||
{
|
||||
addMethod (@selector (isAccessibilityElement), [] (id, SEL) { return false; });
|
||||
|
||||
|
|
@ -114,6 +114,43 @@ private:
|
|||
#if JUCE_IOS_CONTAINER_API_AVAILABLE
|
||||
if (@available (iOS 11.0, *))
|
||||
{
|
||||
addMethod (@selector (accessibilityDataTableCellElementForRow:column:), [] (id self, SEL, NSUInteger row, NSUInteger column) -> id
|
||||
{
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
if (auto* cellHandler = tableInterface->getCellHandler ((int) row, (int) column))
|
||||
if (auto* parent = getAccessibleParent (cellHandler))
|
||||
return static_cast<id> (parent->getNativeImplementation());
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount);
|
||||
addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount);
|
||||
|
||||
addMethod (@selector (accessibilityHeaderElementsForColumn:), [] (id self, SEL, NSUInteger column) -> NSArray*
|
||||
{
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
if (auto* header = tableInterface->getHeaderHandler())
|
||||
{
|
||||
if (isPositiveAndBelow (column, header->getChildren().size()))
|
||||
{
|
||||
auto* result = [NSMutableArray new];
|
||||
[result addObject: static_cast<id> (header->getChildren()[(size_t) column]->getNativeImplementation())];
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTable));
|
||||
|
||||
addMethod (@selector (accessibilityContainerType), [] (id self, SEL) -> NSInteger
|
||||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
|
|
@ -147,12 +184,21 @@ private:
|
|||
}
|
||||
#endif
|
||||
|
||||
addIvar<AccessibilityHandler*> ("handler");
|
||||
|
||||
registerClass();
|
||||
}
|
||||
|
||||
private:
|
||||
static const AccessibilityHandler* getAccessibleParent (const AccessibilityHandler* h)
|
||||
{
|
||||
if (h == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if ([static_cast<id> (h->getNativeImplementation()) isAccessibilityElement])
|
||||
return h;
|
||||
|
||||
return getAccessibleParent (h->getParent());
|
||||
}
|
||||
|
||||
static AccessibilityHandler* getHandler (id self)
|
||||
{
|
||||
return getIvar<AccessibilityHandler*> (self, "handler");
|
||||
|
|
@ -172,7 +218,7 @@ private:
|
|||
|
||||
id instance = (hasEditableText (handler) ? textCls : cls).createInstance();
|
||||
|
||||
Holder element ([instance initWithAccessibilityContainer: (id) handler.getComponent().getWindowHandle()]);
|
||||
Holder element ([instance initWithAccessibilityContainer: static_cast<id> (handler.getComponent().getWindowHandle())]);
|
||||
object_setInstanceVariable (element.get(), "handler", &handler);
|
||||
return element;
|
||||
}
|
||||
|
|
@ -183,15 +229,33 @@ private:
|
|||
{
|
||||
auto* handler = getHandler (self);
|
||||
|
||||
if (handler == nullptr)
|
||||
return false;
|
||||
const auto hasAccessiblePropertiesOrIsTableCell = [] (auto& handlerRef)
|
||||
{
|
||||
const auto isTableCell = [&]
|
||||
{
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (&handlerRef, &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
return tableInterface->getRowSpan (handlerRef).hasValue()
|
||||
&& tableInterface->getColumnSpan (handlerRef).hasValue();
|
||||
}
|
||||
}
|
||||
|
||||
return ! handler->isIgnored()
|
||||
&& handler->getRole() != AccessibilityRole::window
|
||||
&& (handler->getTitle().isNotEmpty()
|
||||
|| handler->getDescription().isNotEmpty()
|
||||
|| handler->getHelp().isNotEmpty()
|
||||
|| handler->getValueInterface() != nullptr);
|
||||
return false;
|
||||
};
|
||||
|
||||
return handlerRef.getTitle().isNotEmpty()
|
||||
|| handlerRef.getHelp().isNotEmpty()
|
||||
|| handlerRef.getTextInterface() != nullptr
|
||||
|| handlerRef.getValueInterface() != nullptr
|
||||
|| isTableCell();
|
||||
};
|
||||
|
||||
return handler != nullptr
|
||||
&& ! handler->isIgnored()
|
||||
&& handler->getRole() != AccessibilityRole::window
|
||||
&& hasAccessiblePropertiesOrIsTableCell (*handler);
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityContainer), [] (id self, SEL) -> id
|
||||
|
|
@ -199,7 +263,7 @@ private:
|
|||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (handler->getComponent().isOnDesktop())
|
||||
return (id) handler->getComponent().getWindowHandle();
|
||||
return static_cast<id> (handler->getComponent().getWindowHandle());
|
||||
|
||||
if (! handler->getChildren().empty())
|
||||
{
|
||||
|
|
@ -217,7 +281,7 @@ private:
|
|||
}
|
||||
|
||||
if (auto* parent = handler->getParent())
|
||||
return [(id) parent->getNativeImplementation() accessibilityContainer];
|
||||
return [static_cast<id> (parent->getNativeImplementation()) accessibilityContainer];
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
|
@ -252,9 +316,9 @@ private:
|
|||
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::editableText:
|
||||
case AccessibilityRole::slider:
|
||||
case AccessibilityRole::menuItem:
|
||||
case AccessibilityRole::menuBar:
|
||||
|
|
@ -348,7 +412,7 @@ private:
|
|||
// which element it thinks has focus and forward the event on to that element if it differs
|
||||
id focusedElement = UIAccessibilityFocusedElement (UIAccessibilityNotificationVoiceOverIdentifier);
|
||||
|
||||
if (focusedElement != nullptr && ! [(id) handler->getNativeImplementation() isEqual: focusedElement])
|
||||
if (focusedElement != nullptr && ! [static_cast<id> (handler->getNativeImplementation()) isEqual: focusedElement])
|
||||
return [focusedElement accessibilityActivate];
|
||||
|
||||
if (handler->hasFocus (false))
|
||||
|
|
@ -381,28 +445,6 @@ private:
|
|||
return NO;
|
||||
});
|
||||
|
||||
#if JUCE_IOS_CONTAINER_API_AVAILABLE
|
||||
if (@available (iOS 11.0, *))
|
||||
{
|
||||
addMethod (@selector (accessibilityDataTableCellElementForRow:column:), [] (id self, SEL, NSUInteger row, NSUInteger column) -> id
|
||||
{
|
||||
if (auto* tableInterface = getEnclosingInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* cellHandler = tableInterface->getCellHandler ((int) row, (int) column))
|
||||
return (id) cellHandler->getNativeImplementation();
|
||||
|
||||
return nil;
|
||||
});
|
||||
|
||||
addMethod (@selector (accessibilityRowCount), getAccessibilityRowCount);
|
||||
addMethod (@selector (accessibilityColumnCount), getAccessibilityColumnCount);
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTable));
|
||||
|
||||
addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange);
|
||||
addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange);
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTableCell));
|
||||
}
|
||||
#endif
|
||||
|
||||
if (elementType == Type::textElement)
|
||||
{
|
||||
addMethod (@selector (accessibilityLineNumberForPoint:), [] (id self, SEL, CGPoint point)
|
||||
|
|
@ -464,6 +506,15 @@ private:
|
|||
addProtocol (@protocol (UIAccessibilityReadingContent));
|
||||
}
|
||||
|
||||
#if JUCE_IOS_CONTAINER_API_AVAILABLE
|
||||
if (@available (iOS 11.0, *))
|
||||
{
|
||||
addMethod (@selector (accessibilityRowRange), getAccessibilityRowIndexRange);
|
||||
addMethod (@selector (accessibilityColumnRange), getAccessibilityColumnIndexRange);
|
||||
addProtocol (@protocol (UIAccessibilityContainerDataTableCell));
|
||||
}
|
||||
#endif
|
||||
|
||||
addIvar<UIAccessibilityElement*> ("container");
|
||||
|
||||
registerClass();
|
||||
|
|
@ -534,9 +585,8 @@ void notifyAccessibilityEventInternal (const AccessibilityHandler& handler, Inte
|
|||
const bool moveToHandler = (eventType == InternalAccessibilityEvent::focusChanged && handler.hasFocus (false));
|
||||
|
||||
sendAccessibilityEvent (notification,
|
||||
moveToHandler ? (id) handler.getNativeImplementation() : nil);
|
||||
moveToHandler ? static_cast<id> (handler.getNativeImplementation()) : nil);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventType) const
|
||||
|
|
@ -558,7 +608,7 @@ void AccessibilityHandler::notifyAccessibilityEvent (AccessibilityEvent eventTyp
|
|||
}();
|
||||
|
||||
if (notification != UIAccessibilityNotifications{})
|
||||
sendAccessibilityEvent (notification, (id) getNativeImplementation());
|
||||
sendAccessibilityEvent (notification, static_cast<id> (getNativeImplementation()));
|
||||
}
|
||||
|
||||
void AccessibilityHandler::postAnnouncement (const String& announcementString, AnnouncementPriority)
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ private:
|
|||
case AccessibilityRole::editableText: return NSAccessibilityTextAreaRole;
|
||||
case AccessibilityRole::menuItem: return NSAccessibilityMenuItemRole;
|
||||
case AccessibilityRole::menuBar: return NSAccessibilityMenuRole;
|
||||
case AccessibilityRole::table: return NSAccessibilityListRole;
|
||||
case AccessibilityRole::table: return NSAccessibilityOutlineRole;
|
||||
case AccessibilityRole::column: return NSAccessibilityColumnRole;
|
||||
case AccessibilityRole::row: return NSAccessibilityRowRole;
|
||||
case AccessibilityRole::cell: return NSAccessibilityCellRole;
|
||||
|
|
@ -504,15 +504,20 @@ private:
|
|||
{
|
||||
if (auto* handler = getHandler (self))
|
||||
{
|
||||
if (auto* cellInterface = handler->getCellInterface())
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (handler, &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
NSAccessibilityRole handlerRole = [self accessibilityRole];
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
NSAccessibilityRole handlerRole = [self accessibilityRole];
|
||||
|
||||
if ([handlerRole isEqual: NSAccessibilityRowRole])
|
||||
return cellInterface->getRowIndex();
|
||||
if ([handlerRole isEqual: NSAccessibilityRowRole])
|
||||
if (const auto span = tableInterface->getRowSpan (*handler))
|
||||
return span->begin;
|
||||
|
||||
if ([handlerRole isEqual: NSAccessibilityColumnRole])
|
||||
return cellInterface->getColumnIndex();
|
||||
if ([handlerRole isEqual: NSAccessibilityColumnRole])
|
||||
if (const auto span = tableInterface->getColumnSpan (*handler))
|
||||
return span->begin;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,18 +73,6 @@ protected:
|
|||
static AccessibilityTableInterface* getTableInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getTableInterface); }
|
||||
static AccessibilityCellInterface* getCellInterface (id self) noexcept { return getInterface (self, &AccessibilityHandler::getCellInterface); }
|
||||
|
||||
template <typename MemberFn>
|
||||
static auto getEnclosingInterface (AccessibilityHandler* handler, MemberFn fn) noexcept -> decltype ((std::declval<AccessibilityHandler>().*fn)())
|
||||
{
|
||||
if (handler == nullptr)
|
||||
return nullptr;
|
||||
|
||||
if (auto* interface = (handler->*fn)())
|
||||
return interface;
|
||||
|
||||
return getEnclosingInterface (handler->getParent(), fn);
|
||||
}
|
||||
|
||||
static bool hasEditableText (AccessibilityHandler& handler) noexcept
|
||||
{
|
||||
return handler.getRole() == AccessibilityRole::editableText
|
||||
|
|
@ -199,10 +187,8 @@ protected:
|
|||
|
||||
NSString* nsString = juceStringToNS (title);
|
||||
|
||||
#if ! JUCE_IOS
|
||||
if (nsString != nil && [[self accessibilityValue] isEqual: nsString])
|
||||
return @"";
|
||||
#endif
|
||||
|
||||
return nsString;
|
||||
}
|
||||
|
|
@ -228,36 +214,58 @@ protected:
|
|||
|
||||
static NSInteger getAccessibilityRowCount (id self, SEL)
|
||||
{
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
return tableInterface->getNumRows();
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
return tableInterface->getNumRows();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static NSInteger getAccessibilityColumnCount (id self, SEL)
|
||||
{
|
||||
if (auto* tableInterface = getTableInterface (self))
|
||||
return tableInterface->getNumColumns();
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
return tableInterface->getNumColumns();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename Getter>
|
||||
static NSRange getCellDimensions (id self, Getter getter)
|
||||
{
|
||||
const auto notFound = NSMakeRange (NSNotFound, 0);
|
||||
|
||||
auto* handler = getHandler (self);
|
||||
|
||||
if (handler == nullptr)
|
||||
return notFound;
|
||||
|
||||
auto* tableHandler = getEnclosingHandlerWithInterface (getHandler (self), &AccessibilityHandler::getTableInterface);
|
||||
|
||||
if (tableHandler == nullptr)
|
||||
return notFound;
|
||||
|
||||
auto* tableInterface = tableHandler->getTableInterface();
|
||||
|
||||
if (tableInterface == nullptr)
|
||||
return notFound;
|
||||
|
||||
const auto result = (tableInterface->*getter) (*handler);
|
||||
|
||||
if (! result.hasValue())
|
||||
return notFound;
|
||||
|
||||
return NSMakeRange ((NSUInteger) result->begin, (NSUInteger) result->num);
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityRowIndexRange (id self, SEL)
|
||||
{
|
||||
if (auto* cellInterface = getCellInterface (self))
|
||||
return NSMakeRange ((NSUInteger) cellInterface->getRowIndex(),
|
||||
(NSUInteger) cellInterface->getRowSpan());
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
return getCellDimensions (self, &AccessibilityTableInterface::getRowSpan);
|
||||
}
|
||||
|
||||
static NSRange getAccessibilityColumnIndexRange (id self, SEL)
|
||||
{
|
||||
if (auto* cellInterface = getCellInterface (self))
|
||||
return NSMakeRange ((NSUInteger) cellInterface->getColumnIndex(),
|
||||
(NSUInteger) cellInterface->getColumnSpan());
|
||||
|
||||
return NSMakeRange (0, 0);
|
||||
return getCellDimensions (self, &AccessibilityTableInterface::getColumnSpan);
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (AccessibleObjCClass)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,50 @@ JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
|||
|
||||
int AccessibilityNativeHandle::idCounter = 0;
|
||||
|
||||
//==============================================================================
|
||||
class UIAScrollProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IScrollProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
JUCE_COMCALL Scroll (ComTypes::ScrollAmount, ComTypes::ScrollAmount) override { return E_FAIL; }
|
||||
JUCE_COMCALL SetScrollPercent (double, double) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_HorizontalScrollPercent (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_VerticalScrollPercent (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_HorizontalViewSize (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_VerticalViewSize (double*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_HorizontallyScrollable (BOOL*) override { return E_FAIL; }
|
||||
JUCE_COMCALL get_VerticallyScrollable (BOOL*) override { return E_FAIL; }
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAScrollProvider)
|
||||
};
|
||||
|
||||
class UIAScrollItemProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IScrollItemProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
||||
JUCE_COMCALL ScrollIntoView() override
|
||||
{
|
||||
if (auto* handler = getEnclosingHandlerWithInterface (&getHandler(), &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = handler->getTableInterface())
|
||||
{
|
||||
tableInterface->showCell (getHandler());
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
private:
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAScrollItemProvider)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
static String getAutomationId (const AccessibilityHandler& handler)
|
||||
{
|
||||
|
|
@ -63,7 +107,7 @@ static auto roleToControlTypeId (AccessibilityRole roleType)
|
|||
case AccessibilityRole::staticText: return ComTypes::UIA_TextControlTypeId;
|
||||
|
||||
case AccessibilityRole::column:
|
||||
case AccessibilityRole::row: return ComTypes::UIA_HeaderItemControlTypeId;
|
||||
case AccessibilityRole::row: return ComTypes::UIA_ListItemControlTypeId;
|
||||
|
||||
case AccessibilityRole::button: return ComTypes::UIA_ButtonControlTypeId;
|
||||
case AccessibilityRole::toggleButton: return ComTypes::UIA_CheckBoxControlTypeId;
|
||||
|
|
@ -146,6 +190,22 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUn
|
|||
const auto role = accessibilityHandler.getRole();
|
||||
const auto fragmentRoot = isFragmentRoot();
|
||||
|
||||
const auto isListOrTableCell = [] (auto& handler)
|
||||
{
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (&handler, &AccessibilityHandler::getTableInterface))
|
||||
{
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
{
|
||||
const auto row = tableInterface->getRowSpan (handler);
|
||||
const auto column = tableInterface->getColumnSpan (handler);
|
||||
|
||||
return row.hasValue() && column.hasValue();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
switch (pId)
|
||||
{
|
||||
case ComTypes::UIA_WindowPatternId:
|
||||
|
|
@ -213,25 +273,27 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUn
|
|||
{
|
||||
auto state = accessibilityHandler.getCurrentState();
|
||||
|
||||
if (state.isSelectable() || state.isMultiSelectable()
|
||||
|| role == AccessibilityRole::radioButton)
|
||||
if (state.isSelectable() || state.isMultiSelectable() || role == AccessibilityRole::radioButton)
|
||||
{
|
||||
return new UIASelectionItemProvider (this);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ComTypes::UIA_TablePatternId:
|
||||
case ComTypes::UIA_GridPatternId:
|
||||
{
|
||||
if (accessibilityHandler.getTableInterface() != nullptr)
|
||||
return new UIAGridProvider (this);
|
||||
if (accessibilityHandler.getTableInterface() != nullptr
|
||||
&& (pId == ComTypes::UIA_GridPatternId || accessibilityHandler.getRole() == AccessibilityRole::table))
|
||||
return static_cast<ComTypes::IGridProvider*> (new UIAGridProvider (this));
|
||||
|
||||
break;
|
||||
}
|
||||
case ComTypes::UIA_TableItemPatternId:
|
||||
case ComTypes::UIA_GridItemPatternId:
|
||||
{
|
||||
if (accessibilityHandler.getCellInterface() != nullptr)
|
||||
return new UIAGridItemProvider (this);
|
||||
if (isListOrTableCell (accessibilityHandler))
|
||||
return static_cast<ComTypes::IGridItemProvider*> (new UIAGridItemProvider (this));
|
||||
|
||||
break;
|
||||
}
|
||||
|
|
@ -250,6 +312,20 @@ JUCE_COMRESULT AccessibilityNativeHandle::GetPatternProvider (PATTERNID pId, IUn
|
|||
|
||||
break;
|
||||
}
|
||||
case ComTypes::UIA_ScrollPatternId:
|
||||
{
|
||||
if (accessibilityHandler.getTableInterface() != nullptr)
|
||||
return new UIAScrollProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
case ComTypes::UIA_ScrollItemPatternId:
|
||||
{
|
||||
if (isListOrTableCell (accessibilityHandler))
|
||||
return new UIAScrollItemProvider (this);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
|
|
|||
|
|
@ -120,18 +120,38 @@ enum WindowInteractionState
|
|||
WindowInteractionState_NotResponding = 4
|
||||
};
|
||||
|
||||
enum RowOrColumnMajor
|
||||
{
|
||||
RowOrColumnMajor_RowMajor = 0,
|
||||
RowOrColumnMajor_ColumnMajor = 1,
|
||||
RowOrColumnMajor_Indeterminate = 2
|
||||
};
|
||||
|
||||
enum ScrollAmount
|
||||
{
|
||||
ScrollAmount_LargeDecrement = 0,
|
||||
ScrollAmount_SmallDecrement = 1,
|
||||
ScrollAmount_NoAmount = 2,
|
||||
ScrollAmount_LargeIncrement = 3,
|
||||
ScrollAmount_SmallIncrement = 4
|
||||
};
|
||||
|
||||
const long UIA_InvokePatternId = 10000;
|
||||
const long UIA_SelectionPatternId = 10001;
|
||||
const long UIA_ValuePatternId = 10002;
|
||||
const long UIA_RangeValuePatternId = 10003;
|
||||
const long UIA_ScrollPatternId = 10004;
|
||||
const long UIA_ExpandCollapsePatternId = 10005;
|
||||
const long UIA_GridPatternId = 10006;
|
||||
const long UIA_GridItemPatternId = 10007;
|
||||
const long UIA_WindowPatternId = 10009;
|
||||
const long UIA_SelectionItemPatternId = 10010;
|
||||
const long UIA_TablePatternId = 10012;
|
||||
const long UIA_TableItemPatternId = 10013;
|
||||
const long UIA_TextPatternId = 10014;
|
||||
const long UIA_TogglePatternId = 10015;
|
||||
const long UIA_TransformPatternId = 10016;
|
||||
const long UIA_ScrollItemPatternId = 10017;
|
||||
const long UIA_TextPattern2Id = 10024;
|
||||
const long UIA_StructureChangedEventId = 20002;
|
||||
const long UIA_MenuOpenedEventId = 20003;
|
||||
|
|
@ -220,6 +240,21 @@ public:
|
|||
JUCE_COMCALL get_ColumnCount (__RPC__out int* pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITableItemProvider, "b9734fa6-771f-4d78-9c90-2517999349cd") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetRowHeaderItems (SAFEARRAY** pRetVal) = 0;
|
||||
JUCE_COMCALL GetColumnHeaderItems (SAFEARRAY** pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (ITableProvider, "9c860395-97b3-490a-b52a-858cc22af166") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL GetRowHeaders (SAFEARRAY** pRetVal) = 0;
|
||||
JUCE_COMCALL GetColumnHeaders (SAFEARRAY** pRetVal) = 0;
|
||||
JUCE_COMCALL get_RowOrColumnMajor (RowOrColumnMajor* pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IInvokeProvider, "54fcb24b-e18e-47a2-b4d3-eccbe77599a2") : public IUnknown
|
||||
{
|
||||
public:
|
||||
|
|
@ -345,6 +380,25 @@ public:
|
|||
JUCE_COMCALL get_IsTopmost (__RPC__out BOOL * pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IScrollProvider, "b38b8077-1fc3-42a5-8cae-d40c2215055a") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL Scroll (ScrollAmount horizontalAmount, ScrollAmount verticalAmount) = 0;
|
||||
JUCE_COMCALL SetScrollPercent (double horizontalPercent,double verticalPercent) = 0;
|
||||
JUCE_COMCALL get_HorizontalScrollPercent (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_VerticalScrollPercent (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_HorizontalViewSize (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_VerticalViewSize (double* pRetVal) = 0;
|
||||
JUCE_COMCALL get_HorizontallyScrollable (BOOL* pRetVal) = 0;
|
||||
JUCE_COMCALL get_VerticallyScrollable (BOOL* pRetVal) = 0;
|
||||
};
|
||||
|
||||
JUCE_COMCLASS (IScrollItemProvider, "2360c714-4bf1-4b26-ba65-9b21316127eb") : public IUnknown
|
||||
{
|
||||
public:
|
||||
JUCE_COMCALL ScrollIntoView() = 0;
|
||||
};
|
||||
|
||||
constexpr CLSID CLSID_SpVoice { 0x96749377, 0x3391, 0x11D2, { 0x9E, 0xE3, 0x00, 0xC0, 0x4F, 0x79, 0x73, 0x96 } };
|
||||
|
||||
} // namespace ComTypes
|
||||
|
|
@ -368,4 +422,8 @@ __CRT_UUID_DECL (juce::ComTypes::IToggleProvider, 0x56d00bd0, 0x
|
|||
__CRT_UUID_DECL (juce::ComTypes::ITransformProvider, 0x6829ddc4, 0x4f91, 0x4ffa, 0xb8, 0x6f, 0xbd, 0x3e, 0x29, 0x87, 0xcb, 0x4c)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IValueProvider, 0xc7935180, 0x6fb3, 0x4201, 0xb1, 0x74, 0x7d, 0xf7, 0x3a, 0xdb, 0xf6, 0x4a)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IWindowProvider, 0x987df77b, 0xdb06, 0x4d77, 0x8f, 0x8a, 0x86, 0xa9, 0xc3, 0xbb, 0x90, 0xb9)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITableItemProvider, 0xb9734fa6, 0x771f, 0x4d78, 0x9c, 0x90, 0x25, 0x17, 0x99, 0x93, 0x49, 0xcd)
|
||||
__CRT_UUID_DECL (juce::ComTypes::ITableProvider, 0x9c860395, 0x97b3, 0x490a, 0xb5, 0x2a, 0x85, 0x8c, 0xc2, 0x2a, 0xf1, 0x66)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IScrollProvider, 0xb38b8077, 0x1fc3, 0x42a5, 0x8c, 0xae, 0xd4, 0x0c, 0x22, 0x15, 0x05, 0x5a)
|
||||
__CRT_UUID_DECL (juce::ComTypes::IScrollItemProvider, 0x2360c714, 0x4bf1, 0x4b26, 0xba, 0x65, 0x9b, 0x21, 0x31, 0x61, 0x27, 0xeb)
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace juce
|
|||
|
||||
//==============================================================================
|
||||
class UIAGridItemProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IGridItemProvider>
|
||||
public ComBaseClassHelper<ComTypes::IGridItemProvider, ComTypes::ITableItemProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
|
@ -36,65 +36,116 @@ public:
|
|||
//==============================================================================
|
||||
JUCE_COMRESULT get_Row (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getRowIndex();
|
||||
});
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getRowSpan,
|
||||
&AccessibilityTableInterface::Span::begin);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_Column (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getColumnIndex();
|
||||
});
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getColumnSpan,
|
||||
&AccessibilityTableInterface::Span::begin);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_RowSpan (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getRowSpan();
|
||||
});
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getRowSpan,
|
||||
&AccessibilityTableInterface::Span::num);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ColumnSpan (int* pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
{
|
||||
*pRetVal = cellInterface.getColumnSpan();
|
||||
});
|
||||
return withTableSpan (pRetVal,
|
||||
&AccessibilityTableInterface::getColumnSpan,
|
||||
&AccessibilityTableInterface::Span::num);
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_ContainingGrid (IRawElementProviderSimple** pRetVal) override
|
||||
{
|
||||
return withCellInterface (pRetVal, [&] (const AccessibilityCellInterface& cellInterface)
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityHandler& tableHandler)
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (auto* handler = cellInterface.getTableHandler())
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
|
||||
tableHandler.getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetRowHeaderItems (SAFEARRAY**) override
|
||||
{
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetColumnHeaderItems (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityHandler& tableHandler)
|
||||
{
|
||||
if (auto* tableInterface = tableHandler.getTableInterface())
|
||||
{
|
||||
if (const auto column = tableInterface->getColumnSpan (getHandler()))
|
||||
{
|
||||
if (auto* header = tableInterface->getHeaderHandler())
|
||||
{
|
||||
const auto children = header->getChildren();
|
||||
|
||||
if (isPositiveAndBelow (column->begin, children.size()))
|
||||
{
|
||||
IRawElementProviderSimple* provider = nullptr;
|
||||
|
||||
if (auto* child = children[(size_t) column->begin])
|
||||
{
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
if (child->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (&provider)) == S_OK && provider != nullptr)
|
||||
{
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, 1);
|
||||
LONG index = 0;
|
||||
const auto hr = SafeArrayPutElement (*pRetVal, &index, provider);
|
||||
|
||||
return ! FAILED (hr);
|
||||
}
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
private:
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withCellInterface (Value* pRetVal, Callback&& callback) const
|
||||
JUCE_COMRESULT withTableInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* cellInterface = getHandler().getCellInterface())
|
||||
{
|
||||
callback (*cellInterface);
|
||||
return S_OK;
|
||||
}
|
||||
if (auto* handler = getEnclosingHandlerWithInterface (&getHandler(), &AccessibilityHandler::getTableInterface))
|
||||
if (handler->getTableInterface() != nullptr && callback (*handler))
|
||||
return S_OK;
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT withTableSpan (int* pRetVal,
|
||||
Optional<AccessibilityTableInterface::Span> (AccessibilityTableInterface::* getSpan) (const AccessibilityHandler&) const,
|
||||
int AccessibilityTableInterface::Span::* spanMember) const
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityHandler& handler)
|
||||
{
|
||||
if (const auto span = ((handler.getTableInterface())->*getSpan) (getHandler()))
|
||||
{
|
||||
*pRetVal = (*span).*spanMember;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (UIAGridItemProvider)
|
||||
};
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ namespace juce
|
|||
|
||||
//==============================================================================
|
||||
class UIAGridProvider : public UIAProviderBase,
|
||||
public ComBaseClassHelper<ComTypes::IGridProvider>
|
||||
public ComBaseClassHelper<ComTypes::IGridProvider, ComTypes::ITableProvider>
|
||||
{
|
||||
public:
|
||||
using UIAProviderBase::UIAProviderBase;
|
||||
|
|
@ -44,12 +44,21 @@ public:
|
|||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
|
||||
if (auto* handler = tableInterface.getCellHandler (row, column))
|
||||
handler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
if (auto* cellHandler = tableInterface.getCellHandler (row, column))
|
||||
{
|
||||
cellHandler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (auto* rowHandler = tableInterface.getRowHandler (row))
|
||||
{
|
||||
rowHandler->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (pRetVal));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
return S_OK;
|
||||
return E_FAIL;
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -71,14 +80,65 @@ public:
|
|||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetRowHeaders (SAFEARRAY**) override
|
||||
{
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
}
|
||||
|
||||
JUCE_COMRESULT GetColumnHeaders (SAFEARRAY** pRetVal) override
|
||||
{
|
||||
return withTableInterface (pRetVal, [&] (const AccessibilityTableInterface& tableInterface)
|
||||
{
|
||||
if (auto* header = tableInterface.getHeaderHandler())
|
||||
{
|
||||
const auto children = header->getChildren();
|
||||
|
||||
*pRetVal = SafeArrayCreateVector (VT_UNKNOWN, 0, (ULONG) children.size());
|
||||
|
||||
LONG index = 0;
|
||||
|
||||
for (const auto& child : children)
|
||||
{
|
||||
IRawElementProviderSimple* provider = nullptr;
|
||||
|
||||
JUCE_BEGIN_IGNORE_WARNINGS_GCC_LIKE ("-Wlanguage-extension-token")
|
||||
if (child != nullptr)
|
||||
child->getNativeImplementation()->QueryInterface (IID_PPV_ARGS (&provider));
|
||||
JUCE_END_IGNORE_WARNINGS_GCC_LIKE
|
||||
|
||||
if (provider == nullptr)
|
||||
return E_FAIL;
|
||||
|
||||
const auto hr = SafeArrayPutElement (*pRetVal, &index, provider);
|
||||
|
||||
if (FAILED (hr))
|
||||
return E_FAIL;
|
||||
|
||||
++index;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
}
|
||||
|
||||
JUCE_COMRESULT get_RowOrColumnMajor (ComTypes::RowOrColumnMajor* pRetVal) override
|
||||
{
|
||||
*pRetVal = ComTypes::RowOrColumnMajor_RowMajor;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename Value, typename Callback>
|
||||
JUCE_COMRESULT withTableInterface (Value* pRetVal, Callback&& callback) const
|
||||
{
|
||||
return withCheckedComArgs (pRetVal, *this, [&]() -> HRESULT
|
||||
{
|
||||
if (auto* tableInterface = getHandler().getTableInterface())
|
||||
return callback (*tableInterface);
|
||||
if (auto* tableHandler = getEnclosingHandlerWithInterface (&getHandler(), &AccessibilityHandler::getTableInterface))
|
||||
if (auto* tableInterface = tableHandler->getTableInterface())
|
||||
return callback (*tableInterface);
|
||||
|
||||
return (HRESULT) UIA_E_NOTSUPPORTED;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -241,16 +241,6 @@ public:
|
|||
public:
|
||||
explicit RowCellInterface (RowAccessibilityHandler& h) : handler (h) {}
|
||||
|
||||
int getColumnIndex() const override { return 0; }
|
||||
int getColumnSpan() const override { return 1; }
|
||||
|
||||
int getRowIndex() const override
|
||||
{
|
||||
return handler.rowComponent.row;
|
||||
}
|
||||
|
||||
int getRowSpan() const override { return 1; }
|
||||
|
||||
int getDisclosureLevel() const override { return 0; }
|
||||
|
||||
const AccessibilityHandler* getTableHandler() const override
|
||||
|
|
@ -289,7 +279,15 @@ public:
|
|||
{
|
||||
setWantsKeyboardFocus (false);
|
||||
|
||||
auto content = std::make_unique<Component>();
|
||||
struct IgnoredComponent : Component
|
||||
{
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return createIgnoredAccessibilityHandler (*this);
|
||||
}
|
||||
};
|
||||
|
||||
auto content = std::make_unique<IgnoredComponent>();
|
||||
content->setWantsKeyboardFocus (false);
|
||||
|
||||
setViewedComponent (content.release());
|
||||
|
|
@ -301,20 +299,23 @@ public:
|
|||
{
|
||||
const auto startIndex = getIndexOfFirstVisibleRow();
|
||||
|
||||
return (startIndex <= row && row < startIndex + rows.size())
|
||||
? rows[row % jmax (1, rows.size())] : nullptr;
|
||||
return (startIndex <= row && row < startIndex + (int) rows.size())
|
||||
? rows[(size_t) (row % jmax (1, (int) rows.size()))].get()
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
int getRowNumberOfComponent (Component* const rowComponent) const noexcept
|
||||
int getRowNumberOfComponent (const Component* const rowComponent) const noexcept
|
||||
{
|
||||
const int index = getViewedComponent()->getIndexOfChildComponent (rowComponent);
|
||||
const int num = rows.size();
|
||||
const auto iter = std::find_if (rows.begin(), rows.end(), [=] (auto& ptr) { return ptr.get() == rowComponent; });
|
||||
|
||||
for (int i = num; --i >= 0;)
|
||||
if (((firstIndex + i) % jmax (1, num)) == index)
|
||||
return firstIndex + i;
|
||||
if (iter == rows.end())
|
||||
return -1;
|
||||
|
||||
return -1;
|
||||
const auto index = (int) std::distance (rows.begin(), iter);
|
||||
const auto mod = jmax (1, (int) rows.size());
|
||||
const auto startIndex = getIndexOfFirstVisibleRow();
|
||||
|
||||
return index + mod * ((startIndex / mod) + (index < (startIndex % mod) ? 1 : 0));
|
||||
}
|
||||
|
||||
void visibleAreaChanged (const Rectangle<int>&) override
|
||||
|
|
@ -357,13 +358,13 @@ public:
|
|||
auto y = getViewPositionY();
|
||||
auto w = content.getWidth();
|
||||
|
||||
const int numNeeded = 4 + getMaximumVisibleHeight() / rowH;
|
||||
rows.removeRange (numNeeded, rows.size());
|
||||
const auto numNeeded = (size_t) (4 + getMaximumVisibleHeight() / rowH);
|
||||
rows.resize (jmin (numNeeded, rows.size()));
|
||||
|
||||
while (numNeeded > rows.size())
|
||||
{
|
||||
auto* newRow = rows.add (new RowComponent (owner));
|
||||
content.addAndMakeVisible (newRow);
|
||||
rows.emplace_back (new RowComponent (owner));
|
||||
content.addAndMakeVisible (*rows.back());
|
||||
}
|
||||
|
||||
firstIndex = y / rowH;
|
||||
|
|
@ -371,7 +372,7 @@ public:
|
|||
lastWholeIndex = (y + getMaximumVisibleHeight() - 1) / rowH;
|
||||
|
||||
const auto startIndex = getIndexOfFirstVisibleRow();
|
||||
const auto lastIndex = startIndex + rows.size();
|
||||
const auto lastIndex = startIndex + (int) rows.size();
|
||||
|
||||
for (auto row = startIndex; row < lastIndex; ++row)
|
||||
{
|
||||
|
|
@ -477,7 +478,7 @@ private:
|
|||
}
|
||||
|
||||
ListBox& owner;
|
||||
OwnedArray<RowComponent> rows;
|
||||
std::vector<std::unique_ptr<RowComponent>> rows;
|
||||
int firstIndex = 0, firstWholeIndex = 0, lastWholeIndex = 0;
|
||||
bool hasUpdated = false;
|
||||
|
||||
|
|
@ -839,7 +840,7 @@ Component* ListBox::getComponentForRowNumber (const int row) const noexcept
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
int ListBox::getRowNumberOfComponent (Component* const rowComponent) const noexcept
|
||||
int ListBox::getRowNumberOfComponent (const Component* const rowComponent) const noexcept
|
||||
{
|
||||
return viewport->getRowNumberOfComponent (rowComponent);
|
||||
}
|
||||
|
|
@ -1174,6 +1175,25 @@ std::unique_ptr<AccessibilityHandler> ListBox::createAccessibilityHandler()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<Span> getRowSpan (const AccessibilityHandler& handler) const override
|
||||
{
|
||||
const auto rowNumber = listBox.getRowNumberOfComponent (&handler.getComponent());
|
||||
|
||||
return rowNumber != -1 ? makeOptional (Span { rowNumber, 1 })
|
||||
: nullopt;
|
||||
}
|
||||
|
||||
Optional<Span> getColumnSpan (const AccessibilityHandler&) const override
|
||||
{
|
||||
return Span { 0, 1 };
|
||||
}
|
||||
|
||||
void showCell (const AccessibilityHandler& h) const override
|
||||
{
|
||||
if (const auto row = getRowSpan (h))
|
||||
listBox.scrollToEnsureRowIsOnscreen (row->begin);
|
||||
}
|
||||
|
||||
private:
|
||||
ListBox& listBox;
|
||||
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ public:
|
|||
/** Returns the row number that the given component represents.
|
||||
If the component isn't one of the list's rows, this will return -1.
|
||||
*/
|
||||
int getRowNumberOfComponent (Component* rowComponent) const noexcept;
|
||||
int getRowNumberOfComponent (const Component* rowComponent) const noexcept;
|
||||
|
||||
/** Returns the width of a row (which may be less than the width of this component
|
||||
if there's a scrollbar).
|
||||
|
|
|
|||
|
|
@ -46,8 +46,8 @@ public:
|
|||
tableModel->paintRowBackground (g, row, getWidth(), getHeight(), isSelected);
|
||||
|
||||
auto& headerComp = owner.getHeader();
|
||||
auto numColumns = headerComp.getNumColumns (true);
|
||||
auto clipBounds = g.getClipBounds();
|
||||
const auto numColumns = jmin ((int) columnComponents.size(), headerComp.getNumColumns (true));
|
||||
const auto clipBounds = g.getClipBounds();
|
||||
|
||||
for (int i = 0; i < numColumns; ++i)
|
||||
{
|
||||
|
|
@ -89,8 +89,14 @@ public:
|
|||
|
||||
if (tableModel != nullptr && row < owner.getNumRows())
|
||||
{
|
||||
const ComponentDeleter deleter { columnForComponent };
|
||||
const auto numColumns = owner.getHeader().getNumColumns (true);
|
||||
columnComponents.resize ((size_t) numColumns);
|
||||
|
||||
while (numColumns < (int) columnComponents.size())
|
||||
columnComponents.pop_back();
|
||||
|
||||
while ((int) columnComponents.size() < numColumns)
|
||||
columnComponents.emplace_back (nullptr, deleter);
|
||||
|
||||
for (int i = 0; i < numColumns; ++i)
|
||||
{
|
||||
|
|
@ -98,11 +104,17 @@ public:
|
|||
auto originalComp = std::move (columnComponents[(size_t) i]);
|
||||
auto oldCustomComp = originalComp != nullptr && ! originalComp->getProperties().contains (tableAccessiblePlaceholderProperty)
|
||||
? std::move (originalComp)
|
||||
: nullptr;
|
||||
: std::unique_ptr<Component, ComponentDeleter> { nullptr, deleter };
|
||||
auto compToRefresh = oldCustomComp != nullptr && columnId == static_cast<int> (oldCustomComp->getProperties()[tableColumnProperty])
|
||||
? std::move (oldCustomComp)
|
||||
: nullptr;
|
||||
auto newCustomComp = rawToUniquePtr (tableModel->refreshComponentForCell (row, columnId, isSelected, compToRefresh.release()));
|
||||
: std::unique_ptr<Component, ComponentDeleter> { nullptr, deleter };
|
||||
|
||||
columnForComponent.erase (compToRefresh.get());
|
||||
std::unique_ptr<Component, ComponentDeleter> newCustomComp { tableModel->refreshComponentForCell (row,
|
||||
columnId,
|
||||
isSelected,
|
||||
compToRefresh.release()),
|
||||
deleter };
|
||||
|
||||
auto columnComp = [&]
|
||||
{
|
||||
|
|
@ -115,12 +127,14 @@ public:
|
|||
return std::move (originalComp);
|
||||
|
||||
// Create a new placeholder component to use
|
||||
auto comp = std::make_unique<Component>();
|
||||
std::unique_ptr<Component, ComponentDeleter> comp { new Component, deleter };
|
||||
comp->setInterceptsMouseClicks (false, false);
|
||||
comp->getProperties().set (tableAccessiblePlaceholderProperty, true);
|
||||
return comp;
|
||||
}();
|
||||
|
||||
columnForComponent.emplace (columnComp.get(), i);
|
||||
|
||||
// In order for navigation to work correctly on macOS, the number of child
|
||||
// accessibility elements on each row must match the number of header accessibility
|
||||
// elements.
|
||||
|
|
@ -250,6 +264,12 @@ public:
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
int getColumnNumberOfComponent (const Component* comp) const
|
||||
{
|
||||
const auto iter = columnForComponent.find (comp);
|
||||
return iter != columnForComponent.cend() ? iter->second : -1;
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> createAccessibilityHandler() override
|
||||
{
|
||||
return std::make_unique<RowAccessibilityHandler> (*this);
|
||||
|
|
@ -297,6 +317,7 @@ public:
|
|||
return state;
|
||||
}
|
||||
|
||||
private:
|
||||
class RowComponentCellInterface : public AccessibilityCellInterface
|
||||
{
|
||||
public:
|
||||
|
|
@ -305,12 +326,6 @@ public:
|
|||
{
|
||||
}
|
||||
|
||||
int getColumnIndex() const override { return 0; }
|
||||
int getColumnSpan() const override { return 1; }
|
||||
|
||||
int getRowIndex() const override { return owner.rowComponent.row; }
|
||||
int getRowSpan() const override { return 1; }
|
||||
|
||||
int getDisclosureLevel() const override { return 0; }
|
||||
|
||||
const AccessibilityHandler* getTableHandler() const override { return owner.rowComponent.owner.getAccessibilityHandler(); }
|
||||
|
|
@ -324,8 +339,27 @@ public:
|
|||
};
|
||||
|
||||
//==============================================================================
|
||||
class ComponentDeleter
|
||||
{
|
||||
public:
|
||||
explicit ComponentDeleter (std::map<const Component*, int>& locations)
|
||||
: columnForComponent (&locations) {}
|
||||
|
||||
void operator() (Component* comp) const
|
||||
{
|
||||
columnForComponent->erase (comp);
|
||||
|
||||
if (comp != nullptr)
|
||||
delete comp;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<const Component*, int>* columnForComponent;
|
||||
};
|
||||
|
||||
TableListBox& owner;
|
||||
std::vector<std::unique_ptr<Component>> columnComponents;
|
||||
std::map<const Component*, int> columnForComponent;
|
||||
std::vector<std::unique_ptr<Component, ComponentDeleter>> columnComponents;
|
||||
int row = -1;
|
||||
bool isSelected = false, isDragging = false, selectRowOnMouseUp = false;
|
||||
|
||||
|
|
@ -571,6 +605,22 @@ void TableListBox::updateColumnComponents() const
|
|||
rowComp->resized();
|
||||
}
|
||||
|
||||
template <typename FindIndex>
|
||||
Optional<AccessibilityTableInterface::Span> findRecursively (const AccessibilityHandler& handler,
|
||||
Component* outermost,
|
||||
FindIndex&& findIndexOfComponent)
|
||||
{
|
||||
for (auto* comp = &handler.getComponent(); comp != outermost; comp = comp->getParentComponent())
|
||||
{
|
||||
const auto result = findIndexOfComponent (comp);
|
||||
|
||||
if (result != -1)
|
||||
return AccessibilityTableInterface::Span { result, 1 };
|
||||
}
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<AccessibilityHandler> TableListBox::createAccessibilityHandler()
|
||||
{
|
||||
class TableInterface : public AccessibilityTableInterface
|
||||
|
|
@ -591,7 +641,7 @@ std::unique_ptr<AccessibilityHandler> TableListBox::createAccessibilityHandler()
|
|||
|
||||
int getNumColumns() const override
|
||||
{
|
||||
return tableListBox.getHeader().getNumColumns (false);
|
||||
return tableListBox.getHeader().getNumColumns (true);
|
||||
}
|
||||
|
||||
const AccessibilityHandler* getRowHandler (int row) const override
|
||||
|
|
@ -606,7 +656,7 @@ std::unique_ptr<AccessibilityHandler> TableListBox::createAccessibilityHandler()
|
|||
const AccessibilityHandler* getCellHandler (int row, int column) const override
|
||||
{
|
||||
if (isPositiveAndBelow (row, getNumRows()) && isPositiveAndBelow (column, getNumColumns()))
|
||||
if (auto* cellComponent = tableListBox.getCellComponent (tableListBox.getHeader().getColumnIdOfIndex (column, false), row))
|
||||
if (auto* cellComponent = tableListBox.getCellComponent (tableListBox.getHeader().getColumnIdOfIndex (column, true), row))
|
||||
return cellComponent->getAccessibilityHandler();
|
||||
|
||||
return nullptr;
|
||||
|
|
@ -620,6 +670,35 @@ std::unique_ptr<AccessibilityHandler> TableListBox::createAccessibilityHandler()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<Span> getRowSpan (const AccessibilityHandler& handler) const override
|
||||
{
|
||||
if (tableListBox.isParentOf (&handler.getComponent()))
|
||||
return findRecursively (handler, &tableListBox, [&] (auto* c) { return tableListBox.getRowNumberOfComponent (c); });
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
Optional<Span> getColumnSpan (const AccessibilityHandler& handler) const override
|
||||
{
|
||||
if (const auto rowSpan = getRowSpan (handler))
|
||||
if (auto* rowComponent = dynamic_cast<RowComp*> (tableListBox.getComponentForRowNumber (rowSpan->begin)))
|
||||
return findRecursively (handler, &tableListBox, [&] (auto* c) { return rowComponent->getColumnNumberOfComponent (c); });
|
||||
|
||||
return nullopt;
|
||||
}
|
||||
|
||||
void showCell (const AccessibilityHandler& handler) const override
|
||||
{
|
||||
const auto row = getRowSpan (handler);
|
||||
const auto col = getColumnSpan (handler);
|
||||
|
||||
if (row.hasValue() && col.hasValue())
|
||||
{
|
||||
tableListBox.scrollToEnsureRowIsOnscreen (row->begin);
|
||||
tableListBox.scrollToEnsureColumnIsOnscreen (col->begin);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
TableListBox& tableListBox;
|
||||
|
||||
|
|
@ -627,7 +706,7 @@ std::unique_ptr<AccessibilityHandler> TableListBox::createAccessibilityHandler()
|
|||
};
|
||||
|
||||
return std::make_unique<AccessibilityHandler> (*this,
|
||||
AccessibilityRole::list,
|
||||
AccessibilityRole::table,
|
||||
AccessibilityActions{},
|
||||
AccessibilityHandler::Interfaces { std::make_unique<TableInterface> (*this) });
|
||||
}
|
||||
|
|
|
|||
|
|
@ -143,19 +143,6 @@ private:
|
|||
public:
|
||||
explicit ItemCellInterface (ItemComponent& c) : itemComponent (c) {}
|
||||
|
||||
int getColumnIndex() const override { return 0; }
|
||||
int getColumnSpan() const override { return 1; }
|
||||
|
||||
int getRowIndex() const override
|
||||
{
|
||||
return itemComponent.getRepresentedItem().getRowNumberInTree();
|
||||
}
|
||||
|
||||
int getRowSpan() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
int getDisclosureLevel() const override
|
||||
{
|
||||
return getItemDepth (&itemComponent.getRepresentedItem());
|
||||
|
|
@ -312,7 +299,7 @@ public:
|
|||
ItemComponent* getItemComponentAt (Point<int> p)
|
||||
{
|
||||
auto iter = std::find_if (itemComponents.cbegin(), itemComponents.cend(),
|
||||
[p] (const std::unique_ptr<ItemComponent>& c)
|
||||
[p] (const auto& c)
|
||||
{
|
||||
return c->getBounds().contains (p);
|
||||
});
|
||||
|
|
@ -326,7 +313,7 @@ public:
|
|||
ItemComponent* getComponentForItem (const TreeViewItem* item) const
|
||||
{
|
||||
const auto iter = std::find_if (itemComponents.begin(), itemComponents.end(),
|
||||
[item] (const std::unique_ptr<ItemComponent>& c)
|
||||
[item] (const auto& c)
|
||||
{
|
||||
return &c->getRepresentedItem() == item;
|
||||
});
|
||||
|
|
@ -340,7 +327,7 @@ public:
|
|||
void itemBeingDeleted (const TreeViewItem* item)
|
||||
{
|
||||
const auto iter = std::find_if (itemComponents.begin(), itemComponents.end(),
|
||||
[item] (const std::unique_ptr<ItemComponent>& c)
|
||||
[item] (const auto& c)
|
||||
{
|
||||
return &c->getRepresentedItem() == item;
|
||||
});
|
||||
|
|
@ -357,6 +344,12 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
const TreeViewItem* getItemForItemComponent (const Component* comp) const
|
||||
{
|
||||
const auto iter = itemForItemComponent.find (comp);
|
||||
return iter != itemForItemComponent.cend() ? iter->second : nullptr;
|
||||
}
|
||||
|
||||
void updateComponents()
|
||||
{
|
||||
std::set<ItemComponent*> componentsToKeep;
|
||||
|
|
@ -369,7 +362,8 @@ public:
|
|||
}
|
||||
else
|
||||
{
|
||||
auto newComp = std::make_unique<ItemComponent> (*treeItem);
|
||||
std::unique_ptr<ItemComponent, Deleter> newComp { new ItemComponent (*treeItem), Deleter { itemForItemComponent } };
|
||||
itemForItemComponent.emplace (newComp.get(), treeItem);
|
||||
|
||||
addAndMakeVisible (*newComp);
|
||||
newComp->addMouseListener (this, treeItem->customComponentUsesTreeViewMouseHandler());
|
||||
|
|
@ -430,7 +424,7 @@ private:
|
|||
updateItemUnderMouse (e);
|
||||
|
||||
isDragging = false;
|
||||
scopedScrollDisabler = nullptr;
|
||||
scopedScrollDisabler = nullopt;
|
||||
needSelectionOnMouseUp = false;
|
||||
|
||||
if (! isEnabled())
|
||||
|
|
@ -523,7 +517,7 @@ private:
|
|||
auto imageOffset = pos.getPosition() - e.getPosition();
|
||||
dragContainer->startDragging (dragDescription, &owner, { dragImage, additionalScale }, true, &imageOffset, &e.source);
|
||||
|
||||
scopedScrollDisabler = std::make_unique<ScopedDisableViewportScroll> (*itemComponent);
|
||||
scopedScrollDisabler.emplace (*itemComponent);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -688,12 +682,32 @@ private:
|
|||
return visibleItems;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
class Deleter
|
||||
{
|
||||
public:
|
||||
explicit Deleter (std::map<const Component*, const TreeViewItem*>& map)
|
||||
: itemForItemComponent (&map) {}
|
||||
|
||||
void operator() (ItemComponent* ptr) const
|
||||
{
|
||||
itemForItemComponent->erase (ptr);
|
||||
|
||||
if (ptr != nullptr)
|
||||
delete ptr;
|
||||
}
|
||||
|
||||
private:
|
||||
std::map<const Component*, const TreeViewItem*>* itemForItemComponent = nullptr;
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
TreeView& owner;
|
||||
|
||||
std::vector<std::unique_ptr<ItemComponent>> itemComponents;
|
||||
std::map<const Component*, const TreeViewItem*> itemForItemComponent;
|
||||
std::vector<std::unique_ptr<ItemComponent, Deleter>> itemComponents;
|
||||
ItemComponent* itemUnderMouse = nullptr;
|
||||
std::unique_ptr<ScopedDisableViewportScroll> scopedScrollDisabler;
|
||||
Optional<ScopedDisableViewportScroll> scopedScrollDisabler;
|
||||
bool isDragging = false, needSelectionOnMouseUp = false;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ContentComponent)
|
||||
|
|
@ -1081,7 +1095,7 @@ void TreeView::moveSelectedRow (int delta)
|
|||
}
|
||||
}
|
||||
|
||||
void TreeView::scrollToKeepItemVisible (TreeViewItem* item)
|
||||
void TreeView::scrollToKeepItemVisible (const TreeViewItem* item)
|
||||
{
|
||||
if (item != nullptr && item->ownerView == this)
|
||||
{
|
||||
|
|
@ -1494,7 +1508,39 @@ std::unique_ptr<AccessibilityHandler> TreeView::createAccessibilityHandler()
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
Optional<Span> getRowSpan (const AccessibilityHandler& handler) const override
|
||||
{
|
||||
auto* item = getItemForHandler (handler);
|
||||
|
||||
if (item == nullptr)
|
||||
return nullopt;
|
||||
|
||||
const auto rowNumber = item->getRowNumberInTree();
|
||||
|
||||
return rowNumber != -1 ? makeOptional (Span { rowNumber, 1 })
|
||||
: nullopt;
|
||||
}
|
||||
|
||||
Optional<Span> getColumnSpan (const AccessibilityHandler&) const override
|
||||
{
|
||||
return Span { 0, 1 };
|
||||
}
|
||||
|
||||
void showCell (const AccessibilityHandler& cellHandler) const override
|
||||
{
|
||||
treeView.scrollToKeepItemVisible (getItemForHandler (cellHandler));
|
||||
}
|
||||
|
||||
private:
|
||||
const TreeViewItem* getItemForHandler (const AccessibilityHandler& handler) const
|
||||
{
|
||||
for (auto* comp = &handler.getComponent(); comp != &treeView; comp = comp->getParentComponent())
|
||||
if (auto* result = treeView.viewport->getContentComp()->getItemForItemComponent (comp))
|
||||
return result;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TreeView& treeView;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (TableInterface)
|
||||
|
|
@ -1849,7 +1895,7 @@ void TreeViewItem::updatePositions (int newY)
|
|||
}
|
||||
}
|
||||
|
||||
TreeViewItem* TreeViewItem::getDeepestOpenParentItem() noexcept
|
||||
const TreeViewItem* TreeViewItem::getDeepestOpenParentItem() const noexcept
|
||||
{
|
||||
auto* result = this;
|
||||
auto* item = this;
|
||||
|
|
|
|||
|
|
@ -613,7 +613,7 @@ private:
|
|||
int getIndentX() const noexcept;
|
||||
void setOwnerView (TreeView*) noexcept;
|
||||
TreeViewItem* getTopLevelItem() noexcept;
|
||||
TreeViewItem* getDeepestOpenParentItem() noexcept;
|
||||
const TreeViewItem* getDeepestOpenParentItem() const noexcept;
|
||||
int getNumRows() const noexcept;
|
||||
TreeViewItem* getItemOnRow (int) noexcept;
|
||||
void deselectAllRecursively (TreeViewItem*);
|
||||
|
|
@ -798,7 +798,7 @@ public:
|
|||
TreeViewItem* getItemAt (int yPosition) const noexcept;
|
||||
|
||||
/** Tries to scroll the tree so that this item is on-screen somewhere. */
|
||||
void scrollToKeepItemVisible (TreeViewItem* item);
|
||||
void scrollToKeepItemVisible (const TreeViewItem* item);
|
||||
|
||||
/** Returns the TreeView's Viewport object. */
|
||||
Viewport* getViewport() const noexcept;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue