/* * SPDX-FileCopyrightText: 2012 Amandeep Singh * SPDX-FileCopyrightText: 2024 Felix Ernst * * SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef KITEMLISTVIEWACCESSIBLE_H #define KITEMLISTVIEWACCESSIBLE_H #include "dolphin_export.h" #include #include #include #include class KItemListView; class KItemListContainer; class KItemListContainerAccessible; class KItemListSelectionManager; /** * The main class for making the main view accessible. * * Such a class is necessary because the KItemListView is a mostly custom entity. This class provides a lot of the functionality to make it possible to * interact with the view using accessibility tools. It implements various interfaces mostly to generally allow working with the view as a whole. However, * actually interacting with singular items within the view is implemented in KItemListDelegateAccessible. * * @note For documentation of most of the methods within this class, check out the documentation of the methods which are being overridden here. */ class DOLPHIN_EXPORT KItemListViewAccessible : public QAccessibleObject, public QAccessibleSelectionInterface, public QAccessibleTableInterface, public QAccessibleActionInterface { public: explicit KItemListViewAccessible(KItemListView *view, KItemListContainerAccessible *parent); ~KItemListViewAccessible() override; // QAccessibleObject void *interface_cast(QAccessible::InterfaceType type) override; QAccessible::Role role() const override; QAccessible::State state() const override; QString text(QAccessible::Text t) const override; QRect rect() const override; QAccessibleInterface *child(int index) const override; int childCount() const override; int indexOfChild(const QAccessibleInterface *) const override; QAccessibleInterface *childAt(int x, int y) const override; QAccessibleInterface *parent() const override; // Table interface QAccessibleInterface *cellAt(int row, int column) const override; QAccessibleInterface *caption() const override; QAccessibleInterface *summary() const override; QString columnDescription(int column) const override; QString rowDescription(int row) const override; int columnCount() const override; int rowCount() const override; // Selection int selectedCellCount() const override; int selectedColumnCount() const override; int selectedRowCount() const override; QList selectedCells() const override; QList selectedColumns() const override; QList selectedRows() const override; bool isColumnSelected(int column) const override; bool isRowSelected(int row) const override; bool selectRow(int row) override; bool selectColumn(int column) override; bool unselectRow(int row) override; bool unselectColumn(int column) override; void modelChange(QAccessibleTableModelChangeEvent *) override; // Selection interface /** Clear selection */ bool clear() override; bool isSelected(QAccessibleInterface *childItem) const override; bool select(QAccessibleInterface *childItem) override; bool selectAll() override; QAccessibleInterface *selectedItem(int selectionIndex) const override; int selectedItemCount() const override; QList selectedItems() const override; bool unselect(QAccessibleInterface *childItem) override; // Action interface QStringList actionNames() const override; void doAction(const QString &actionName) override; QStringList keyBindingsForAction(const QString &actionName) const override; // Custom non-interface methods KItemListView *view() const; /** * Called by KItemListContainer when it passes on focus to the view. Accessible focus is then meant to go towards this accessible interface and a detailed * announcement of the current view state (current item and overall location state) should be triggered. */ void setAccessibleFocusAndAnnounceAll(); /** * Called multiple times while a new location is loading. A timer is restarted, and if this method has not been called for a split second, the newly loaded * location is finally announced. * Either the @p placeholderMessage is announced when there are no items in the view (yet), or the current item is announced together with the view state. * * @param placeholderMessage The message that should be announced when no items are visible (yet). This message is mostly identical to * DolphinView::m_placeholderLabel in both content and purpose. @see DolphinView::updatePlaceHolderLabel(). * * If there are items in the view and the placeholderMessage is therefore not visible, the current item and location is announced instead. */ void announceNewlyLoadedLocation(const QString &placeholderMessage); /** * Starts a timer that will trigger an announcement of the current item. The timer makes sure that quick changes to the current item will only lead to a * singular announcement. This way when a new folder is loaded we only trigger a single announcement even if the items quickly change. * * When m_shouldAnnounceLocation is true, the current location will be announced following the announcement of the current item. * * If the current item is invalid, only the current location is announced, which has the responsibility of then telling why there is no valid item in the * view. */ void announceCurrentItem(); /** * Toggling selection mode completely changes how the application controls, so it is absolutely necessary to communicate this change to users as soon as it * happens. */ void announceSelectionModeEnabled(bool enabled); private: /** * @returns a KItemListDelegateAccessible representing the file or folder at the @index. Returns nullptr for invalid indices. * If a KItemListDelegateAccessible for an index does not yet exist, it will be created. * Index is 0-based. */ inline QAccessibleInterface *accessibleDelegate(int index) const; KItemListSelectionManager *selectionManager() const; private Q_SLOTS: /** * Is run in response to announceCurrentItem(). If the current item exists, it is announced. Otherwise the view is announced. * Also announces some general information about the current location if it has changed recently. */ void slotAnnounceCurrentItemTimerTimeout(); private: /** @see setPlaceholderMessage(). */ QString m_placeholderMessage; bool m_selectionMode = false; /** * Is started by announceCurrentItem(). * If we announce the current item as soon as it changes, we would announce multiple items while loading a folder. * This timer makes sure we only announce the singular currently focused item when things have settled down. */ QTimer *m_announceCurrentItemTimer; /** * If we want announceCurrentItem() to always announce the current item, we must be aware if this is equal to the previous current item, because * - if the accessibility focus moves to a new item, it is automatically announced, but * - if the focus is still on the item at the same index, the focus does not technically move to a new item even if the file at that index changed, so we * need to instead send change events for the accessible name and accessible description. */ int m_lastAnnouncedIndex = -1; /** * Is set to true in response to announceDescriptionChange(). When true, the next time slotAnnounceCurrentItemTimerTimeout() is called the description is * also announced. Then this bool is set to false. */ bool m_shouldAnnounceLocation = true; class AccessibleIdWrapper { public: AccessibleIdWrapper(); bool isValid; QAccessible::Id id; }; /** * A list that maps the indices of the children of this KItemListViewAccessible to the accessible ids of the matching KItemListDelegateAccessible objects. * For example: m_accessibleDelegates.at(2) would be the AccessibleIdWrapper with an id which can be used to retrieve the QAccessibleObject that represents * the third file in this view. */ mutable QVector m_accessibleDelegates; KItemListContainerAccessible *m_parent; }; #endif