]> cloud.milkyroute.net Git - dolphin.git/commitdiff
Have special keyboard controls in selection mode
authorFelix Ernst <felixernst@kde.org>
Sun, 29 Dec 2024 11:27:18 +0000 (11:27 +0000)
committerFelix Ernst <felixernst@kde.org>
Sun, 29 Dec 2024 11:27:18 +0000 (11:27 +0000)
Prior to this commit keyboard controls and behaviour of Dolphin's main
view were identical no matter if selection mode was enabled or not.
While selection mode makes it impossible to accidentally clear the
selection by singular mouse clicks, any press of an arrow key on the
keyboard would still clear the full selection which goes against
selection mode's objective.

Furthermore, keyboard-only users had no reason to ever enable selection
mode because it made no difference to them.

This commit changes this by offering a changed control scheme for key
presses while in selection mode. Arrow key presses without modifier now
only move focus between items but do no longer clear or change the
selection. Similarly, Page Up/Down, Home, and End key presses only move
keyboard focus. Enter, Return, and Space key presses now only toggle
the selection for the current item.

The above controls are however mostly unchanged when combining them
with Modifier keys like Shift or Control.

The type-ahead feature is also changed in selection mode to only move
keyboard focus without changing the selection.

This way keyboard users are less likely to clear their selection by
mistake. Regression tests are added for these selection mode controls.

The code changes to change this keyboard behaviour are quite minimal.
Most of the added code is for making selection mode accessible. That's
because we need to make sure the changed control scheme is properly
announced and communicated or a blind user will be left utterly
confused why the normal keyboard controls "stopped working".

Enabling or disabling selection mode is announced to accessibility
software. Furthermore whenever focus goes to the main view, the
selection mode state is also mentioned when active.

BUG: 458091

src/kitemviews/accessibility/kitemlistviewaccessible.cpp
src/kitemviews/accessibility/kitemlistviewaccessible.h
src/kitemviews/kitemlistcontroller.cpp
src/selectionmode/topbar.cpp
src/selectionmode/topbar.h
src/tests/kitemlistcontrollertest.cpp
src/views/dolphinview.cpp

index f8c14bf4a7ce5e372d9dc44d7284b8670466fd22..f58527be67267078b6c680d03d1360b2c1b553ef 100644 (file)
@@ -280,8 +280,18 @@ QString KItemListViewAccessible::text(QAccessible::Text t) const
             if (numberOfSelectedItems < 1 || (numberOfSelectedItems == 1 && isSelected(currentItem))) {
                 // We do not announce the number of selected items if the only selected item is the current item
                 // because the selection state of the current item is already announced elsewhere.
+                if (m_selectionMode) {
+                    return i18nc("@info accessibility, 1 is path", "in a grid layout in selection mode in location %1", modelRootUrl.toDisplayString());
+                }
                 return i18nc("@info accessibility, 1 is path", "in a grid layout in location %1", modelRootUrl.toDisplayString());
             }
+            if (m_selectionMode) {
+                return i18ncp("@info accessibility, 2 is path",
+                              "%1 selected item in a grid layout in selection mode in location %2",
+                              "%1 selected items in a grid layout in selection mode in location %2",
+                              numberOfSelectedItems,
+                              modelRootUrl.toDisplayString());
+            }
             return i18ncp("@info accessibility, 2 is path",
                           "%1 selected item in a grid layout in location %2",
                           "%1 selected items in a grid layout in location %2",
@@ -293,8 +303,18 @@ QString KItemListViewAccessible::text(QAccessible::Text t) const
     if (numberOfSelectedItems < 1 || (numberOfSelectedItems == 1 && isSelected(currentItem))) {
         // We do not announce the number of selected items if the only selected item is the current item
         // because the selection state of the current item is already announced elsewhere.
+        if (m_selectionMode) {
+            return i18nc("@info accessibility, 1 is path", "in selection mode in location %1", modelRootUrl.toDisplayString());
+        }
         return i18nc("@info accessibility, 1 is path", "in location %1", modelRootUrl.toDisplayString());
     }
+    if (m_selectionMode) {
+        return i18ncp("@info accessibility, 2 is path",
+                      "%1 selected item in selection mode in location %2",
+                      "%1 selected items in selection mode in location %2",
+                      numberOfSelectedItems,
+                      modelRootUrl.toDisplayString());
+    }
     return i18ncp("@info accessibility, 2 is path",
                   "%1 selected item in location %2",
                   "%1 selected items in location %2",
@@ -490,3 +510,14 @@ void KItemListViewAccessible::slotAnnounceCurrentItemTimerTimeout()
         QAccessible::updateAccessibility(&announceAccessibleDescriptionEvent);
     }
 }
+
+void KItemListViewAccessible::announceSelectionModeEnabled(const bool enabled)
+{
+    m_selectionMode = enabled;
+#if QT_VERSION >= QT_VERSION_CHECK(6, 8, 0) // QAccessibleAnnouncementEvent is only available since 6.8
+    QAccessibleAnnouncementEvent announceChangedControlsEvent(view(),
+                                                              enabled ? i18nc("accessibility announcement", "Selection mode enabled")
+                                                                      : i18nc("accessibility announcement", "Selection mode disabled"));
+    QAccessible::updateAccessibility(&announceChangedControlsEvent);
+#endif
+}
index db28324356c25732d5c843b781b405420e818cb6..64b5a2442a1957189e5ddb748183ad5d6be3b17b 100644 (file)
@@ -124,6 +124,12 @@ public:
      */
     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.
@@ -144,6 +150,7 @@ private Q_SLOTS:
 private:
     /** @see setPlaceholderMessage(). */
     QString m_placeholderMessage;
+    bool m_selectionMode = false;
 
     /**
      * Is started by announceCurrentItem().
index 821e1b75fea1ff100bd96d22a81c58092a44d29e..2ae4a1f25291c7e68b8f1e259d9bec5b7ead53f1 100644 (file)
@@ -270,7 +270,9 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
     }
 
     const bool controlPressed = event->modifiers() & Qt::ControlModifier;
-    const bool shiftOrControlPressed = shiftPressed || controlPressed;
+    if (m_selectionMode && !controlPressed && !shiftPressed && (key == Qt::Key_Enter || key == Qt::Key_Return)) {
+        key = Qt::Key_Space; // In selection mode one moves around with arrow keys and toggles selection with Enter.
+    }
     const bool navigationPressed = key == Qt::Key_Home || key == Qt::Key_End || key == Qt::Key_PageUp || key == Qt::Key_PageDown || key == Qt::Key_Up
         || key == Qt::Key_Down || key == Qt::Key_Left || key == Qt::Key_Right;
 
@@ -467,7 +469,7 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
 
     case Qt::Key_Space:
         if (m_selectionBehavior == MultiSelection) {
-            if (controlPressed) {
+            if (controlPressed || m_selectionMode) {
                 // Toggle the selection state of the current item.
                 m_selectionManager->endAnchoredSelection();
                 m_selectionManager->setSelected(index, 1, KItemListSelectionManager::Toggle);
@@ -503,13 +505,13 @@ bool KItemListController::keyPressEvent(QKeyEvent *event)
             break;
 
         case MultiSelection:
-            if (controlPressed) {
+            if (controlPressed || (m_selectionMode && !shiftPressed)) {
                 m_selectionManager->endAnchoredSelection();
             }
 
             m_selectionManager->setCurrentItem(index);
 
-            if (!shiftOrControlPressed) {
+            if (!shiftPressed && !controlPressed && !m_selectionMode) {
                 m_selectionManager->clearSelection();
                 m_selectionManager->setSelected(index, 1);
             }
@@ -540,10 +542,16 @@ void KItemListController::slotChangeCurrentItem(const QString &text, bool search
         index = m_model->indexForKeyboardSearch(text, 0);
     }
     if (index >= 0) {
+        if (m_selectionMode) {
+            m_selectionManager->endAnchoredSelection();
+        }
+
         m_selectionManager->setCurrentItem(index);
 
         if (m_selectionBehavior != NoSelection) {
-            m_selectionManager->replaceSelection(index);
+            if (!m_selectionMode) { // Don't clear the selection in selection mode.
+                m_selectionManager->replaceSelection(index);
+            }
             m_selectionManager->beginAnchoredSelection(index);
         }
 
index 5d77a4c008e12261d50bc067c35eaa6e0a492f11..7ef507910587c62b59b55bcdacc0b58399f6d88f 100644 (file)
@@ -10,8 +10,8 @@
 #include "backgroundcolorhelper.h"
 
 #include <KColorScheme>
+#include <KContextualHelpButton>
 #include <KLocalizedString>
-#include <KToolTipHelper>
 
 #include <QHBoxLayout>
 #include <QLabel>
@@ -23,14 +23,6 @@ using namespace SelectionMode;
 TopBar::TopBar(QWidget *parent)
     : AnimatedHeightWidget{parent}
 {
-    setToolTip(KToolTipHelper::whatsThisHintOnly());
-    setWhatsThis(xi18nc("@info:whatsthis",
-                        "<title>Selection Mode</title><para>Select files or folders to manage or manipulate them."
-                        "<list><item>Press on a file or folder to select it.</item><item>Press on an already selected file or folder to deselect it.</item>"
-                        "<item>Pressing an empty area does <emphasis>not</emphasis> clear the selection.</item>"
-                        "<item>Selection rectangles (created by dragging from an empty area) invert the selection status of items within.</item></list></para>"
-                        "<para>The available action buttons at the bottom change depending on the current selection.</para>"));
-
     QWidget *contentsContainer = prepareContentsContainer();
 
     BackgroundColorHelper::instance()->controlBackgroundColor(this);
@@ -39,8 +31,24 @@ TopBar::TopBar(QWidget *parent)
     m_shortLabelString = i18nc("@info label above the view explaining the state", "Selection Mode");
     m_label = new QLabel(contentsContainer);
     m_label->setMinimumWidth(0);
+    m_label->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard | Qt::LinksAccessibleByKeyboard); // for keyboard accessibility
     BackgroundColorHelper::instance()->controlBackgroundColor(m_label);
 
+    m_contextualHelpButton = new KContextualHelpButton{
+        xi18nc("@info",
+               "<title>Selection Mode</title><para>Select files or folders to manage or manipulate them."
+               "<list><item>Press on a file or folder to select it.</item><item>Press on an already selected file or folder to deselect it.</item>"
+               "<item>Pressing an empty area does <emphasis>not</emphasis> clear the selection.</item>"
+               "<item>Selection rectangles (created by dragging from an empty area) invert the selection status of items within.</item>"
+               "<item>Moving with <shortcut>arrow keys</shortcut> does <emphasis>not</emphasis> change the selection.</item>"
+               "<item>Pressing <shortcut>%1</shortcut>, <shortcut>%2</shortcut>, or <shortcut>%3</shortcut> toggles the selection.</item></list></para>"
+               "<para>The available action buttons at the bottom change depending on the current selection.</para>",
+               QKeySequence{Qt::Key_Enter}.toString(QKeySequence::NativeText),
+               QKeySequence{Qt::Key_Return}.toString(QKeySequence::NativeText),
+               QKeySequence{Qt::CTRL | Qt::Key_Space}.toString(QKeySequence::NativeText)),
+        nullptr,
+        contentsContainer};
+
     m_closeButton = new QPushButton(QIcon::fromTheme(QStringLiteral("window-close-symbolic")), "", contentsContainer);
     m_closeButton->setText(i18nc("@action:button", "Exit Selection Mode"));
     m_closeButton->setFlat(true);
@@ -54,6 +62,7 @@ TopBar::TopBar(QWidget *parent)
 
     layout->addStretch();
     layout->addWidget(m_label);
+    layout->addWidget(m_contextualHelpButton);
     layout->addStretch();
     layout->addWidget(m_closeButton);
 }
@@ -67,8 +76,8 @@ void TopBar::resizeEvent(QResizeEvent *resizeEvent)
 void TopBar::updateLabelString()
 {
     QFontMetrics fontMetrics = m_label->fontMetrics();
-    if (fontMetrics.horizontalAdvance(m_fullLabelString) + m_closeButton->sizeHint().width() + style()->pixelMetric(QStyle::PM_LayoutLeftMargin) * 2
-            + style()->pixelMetric(QStyle::PM_LayoutRightMargin) * 2
+    if (fontMetrics.horizontalAdvance(m_fullLabelString) + m_contextualHelpButton->sizeHint().width() + m_closeButton->sizeHint().width()
+            + style()->pixelMetric(QStyle::PM_LayoutLeftMargin) * 2 + style()->pixelMetric(QStyle::PM_LayoutRightMargin) * 2
         < width()) {
         m_label->setText(m_fullLabelString);
     } else {
index 1f9cfdb18ae59d6ac20d9f395fd9e55c4f024f5c..760c3da632e2af7097c3f4f6ccc58dc8f3fd1f6e 100644 (file)
@@ -10,6 +10,7 @@
 
 #include "animatedheightwidget.h"
 
+class KContextualHelpButton;
 class QLabel;
 class QPushButton;
 class QResizeEvent;
@@ -46,6 +47,7 @@ private:
 
 private:
     QLabel *m_label;
+    KContextualHelpButton *m_contextualHelpButton;
     QPushButton *m_closeButton;
 
     /** @see updateLabelString() */
index de40331e6da65be42b7a746d698baec1901cd15d..a882accaa109bb783a8715fdbfd9ea6b888929e9 100644 (file)
@@ -174,6 +174,7 @@ void KItemListControllerTest::init()
 
 void KItemListControllerTest::cleanup()
 {
+    m_controller->setSelectionModeEnabled(false);
 }
 
 /**
@@ -228,9 +229,11 @@ void KItemListControllerTest::testKeyboardNavigation_data()
     QTest::addColumn<KFileItemListView::ItemLayout>("layout");
     QTest::addColumn<Qt::Orientation>("scrollOrientation");
     QTest::addColumn<int>("columnCount");
-    QTest::addColumn<KItemListController::SelectionBehavior>("selectionBehavior");
+    QTest::addColumn<KItemListController::SelectionBehavior>("selectionBehavior"); // Defines how many items can be selected at the same time.
     QTest::addColumn<bool>("groupingEnabled");
     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
+    QTest::addColumn<bool>(
+        "selectionModeEnabled"); // Don't confuse this with "selectionBehaviour". This is about changing controls for users to help multi-selecting.
     QTest::addColumn<QList<QPair<KeyPress, ViewState>>>("testList");
 
     QList<KFileItemListView::ItemLayout> layoutList;
@@ -265,6 +268,9 @@ void KItemListControllerTest::testKeyboardNavigation_data()
     layoutDirectionList.append(Qt::RightToLeft);
     layoutDirectionNames[Qt::RightToLeft] = "Right-to-Left LayoutDirection";
 
+    bool selectionModeEnabled = false; // For most tests this is kept disabled because it is not really affected by all the other test conditions.
+                                       // We only enable it for a few separate tests at the end.
+
     for (const KFileItemListView::ItemLayout &layout : layoutList) {
         // The following settings depend on the layout.
         // Note that 'columns' are actually 'rows' in
@@ -484,13 +490,92 @@ void KItemListControllerTest::testKeyboardNavigation_data()
 
                         const QByteArray testNameAscii = testName.toLatin1();
 
-                        QTest::newRow(testNameAscii.data())
-                            << layout << scrollOrientation << columnCount << selectionBehavior << groupingEnabled << layoutDirection << testList;
+                        QTest::newRow(testNameAscii.data()) << layout << scrollOrientation << columnCount << selectionBehavior << groupingEnabled
+                                                            << layoutDirection << selectionModeEnabled << testList;
                     }
                 }
             }
         }
     }
+
+    /**
+     * Selection mode tests
+     * We only test for the default icon view mode with typical scrollOrientation, selectionBehaviour, no grouping, left-to-right layoutDirection because none
+     * of this should affect selection mode and special-casing selection mode within the above test would make the above code even more complex than it already
+     * is.
+     */
+    selectionModeEnabled = true;
+    const KFileItemListView::ItemLayout layout = KFileItemListView::IconsLayout;
+    const Qt::Orientation scrollOrientation = Qt::Vertical;
+    const int columnCount = 3;
+    const Qt::Key nextItemKey = Qt::Key_Right;
+    const Qt::Key previousItemKey = Qt::Key_Left;
+    const Qt::Key nextRowKey = Qt::Key_Down;
+    const Qt::Key previousRowKey = Qt::Key_Up;
+
+    const Qt::LayoutDirection layoutDirection = Qt::LeftToRight;
+    const KItemListController::SelectionBehavior &selectionBehavior = KItemListController::MultiSelection;
+    const bool groupingEnabled = false;
+
+    QList<QPair<KeyPress, ViewState>> testList;
+
+    testList << qMakePair(KeyPress(nextItemKey), ViewState(1, KItemSet())) // In selection mode nothing is selected simply by moving with arrow keys.
+             << qMakePair(KeyPress(Qt::Key_Return), ViewState(1, KItemSet() << 1)) // Pressing Return toggles the selection but does not activate.
+             << qMakePair(KeyPress(Qt::Key_Enter), ViewState(1, KItemSet())) // Pressing Enter toggles the selection but does not activate.
+             << qMakePair(KeyPress(nextItemKey), ViewState(2, KItemSet()))
+             << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2 << 3)) // Shift+Arrow key still selects in selection mode.
+             << qMakePair(KeyPress(Qt::Key_Return), ViewState(3, KItemSet() << 2))
+             << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(2, KItemSet() << 2 << 3))
+             << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(3, KItemSet() << 2)) // Shift+Left and then Shift+Right cancel each other out.
+             << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 2))
+             << qMakePair(KeyPress(Qt::Key_Return), ViewState(4, KItemSet() << 2 << 4))
+             << qMakePair(KeyPress(previousItemKey), ViewState(3, KItemSet() << 2 << 4))
+             << qMakePair(KeyPress(Qt::Key_Home, Qt::ShiftModifier), ViewState(0, KItemSet() << 0 << 1 << 2 << 3 << 4))
+             << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3 << 4))
+             << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 2 << 3 << 4))
+             << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(1, KItemSet() << 0 << 1 << 2 << 3 << 4))
+             << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 0 << 1 << 2 << 3 << 4))
+             << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(18, KItemSet() << 0 << 1 << 2 << 3 << 4 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 0 << 1 << 2 << 3 << 4 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet() << 1 << 2 << 3 << 4 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_Enter), ViewState(0, KItemSet() << 0 << 1 << 2 << 3 << 4 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet() << 1 << 2 << 3 << 4 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_Space, Qt::ControlModifier), ViewState(0, KItemSet() << 0 << 1 << 2 << 3 << 4 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_Space), ViewState(0, KItemSet() << 1 << 2 << 3 << 4 << 18 << 19)) // Space toggles selection in selection mode.
+             << qMakePair(KeyPress(Qt::Key_D), ViewState(9, KItemSet() << 1 << 2 << 3 << 4 << 18 << 19)) // No selection change by type-ahead.
+             << qMakePair(KeyPress(Qt::Key_Space), ViewState(9, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19)) // Space is not added to type-ahead.
+             << qMakePair(KeyPress(Qt::Key_4), ViewState(12, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19)) // No selection change by type-ahead.
+             << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+
+             // The following tests assume a columnCount of three and no grouping enabled.
+             << qMakePair(KeyPress(nextRowKey), ViewState(3, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextItemKey, Qt::ControlModifier), ViewState(4, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextRowKey), ViewState(7, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 1 << 2 << 3 << 4 << 7 << 8 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(9, KItemSet() << 1 << 2 << 3 << 4 << 7 << 8 << 9 << 18 << 19))
+             << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(8, KItemSet() << 1 << 2 << 3 << 4 << 7 << 8 << 9 << 18 << 19))
+             << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 1 << 2 << 3 << 4 << 6 << 7 << 9 << 18 << 19))
+             << qMakePair(KeyPress(previousItemKey, Qt::ShiftModifier), ViewState(5, KItemSet() << 1 << 2 << 3 << 4 << 5 << 6 << 7 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(6, KItemSet() << 1 << 2 << 3 << 4 << 6 << 7 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextItemKey, Qt::ShiftModifier), ViewState(7, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextRowKey), ViewState(10, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextItemKey), ViewState(11, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextRowKey), ViewState(14, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextRowKey), ViewState(17, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(nextRowKey), ViewState(19, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(previousRowKey), ViewState(17, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_End), ViewState(19, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(previousRowKey), ViewState(16, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19))
+             << qMakePair(KeyPress(Qt::Key_Home), ViewState(0, KItemSet() << 1 << 2 << 3 << 4 << 9 << 18 << 19));
+
+    const QString testName = "Selection Mode: " + layoutNames[layout] + ", " + QStringLiteral("%1 columns, ").arg(columnCount)
+        + selectionBehaviorNames[selectionBehavior] + ", " + groupingEnabledNames[groupingEnabled] + ", " + layoutDirectionNames[layoutDirection];
+
+    const QByteArray testNameAscii = testName.toLatin1();
+
+    QTest::newRow(testNameAscii.data()) << layout << scrollOrientation << columnCount << selectionBehavior << groupingEnabled << layoutDirection
+                                        << selectionModeEnabled << testList;
 }
 
 /**
@@ -508,6 +593,7 @@ void KItemListControllerTest::testKeyboardNavigation()
     QFETCH(KItemListController::SelectionBehavior, selectionBehavior);
     QFETCH(bool, groupingEnabled);
     QFETCH(Qt::LayoutDirection, layoutDirection);
+    QFETCH(bool, selectionModeEnabled);
     QFETCH(QList<keyPressViewStatePair>, testList);
 
     QApplication::setLayoutDirection(layoutDirection);
@@ -525,6 +611,9 @@ void KItemListControllerTest::testKeyboardNavigation()
     m_model->setGroupedSorting(groupingEnabled);
     QCOMPARE(m_model->groupedSorting(), groupingEnabled);
 
+    m_controller->setSelectionModeEnabled(selectionModeEnabled);
+    QCOMPARE(m_controller->selectionMode(), selectionModeEnabled);
+
     adjustGeometryForColumnCount(columnCount);
     QCOMPARE(m_view->m_layouter->m_columnCount, columnCount);
 
index 6a4ccb70d546d76cd5d09e207ac91013f383ec6f..8302620e424480bcaf4e768d4140f9d9d7159dff 100644 (file)
@@ -320,6 +320,12 @@ void DolphinView::setSelectionModeEnabled(const bool enabled)
         m_view->setEnabledSelectionToggles(DolphinItemListView::SelectionTogglesEnabled::FollowSetting);
     }
     m_container->controller()->setSelectionModeEnabled(enabled);
+#ifndef QT_NO_ACCESSIBILITY
+    if (QAccessible::isActive()) {
+        auto accessibleViewInterface = static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(m_view));
+        accessibleViewInterface->announceSelectionModeEnabled(enabled);
+    }
+#endif
 }
 
 bool DolphinView::selectionMode() const