]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kitemlistview.cpp
Overhaul main view accessibility
[dolphin.git] / src / kitemviews / kitemlistview.cpp
index cf14836592ffe680a7cccb640a361f769eefa5c0..b0ea32940f2e9b80b59f409c7e78cdb8cc25a8e1 100644 (file)
@@ -8,12 +8,16 @@
 
 #include "kitemlistview.h"
 
+#ifndef QT_NO_ACCESSIBILITY
+#include "accessibility/kitemlistcontaineraccessible.h"
+#include "accessibility/kitemlistdelegateaccessible.h"
+#include "accessibility/kitemlistviewaccessible.h"
+#endif
 #include "dolphindebug.h"
 #include "kitemlistcontainer.h"
 #include "kitemlistcontroller.h"
 #include "kitemlistheader.h"
 #include "kitemlistselectionmanager.h"
-#include "kitemlistviewaccessible.h"
 #include "kstandarditemlistwidget.h"
 
 #include "private/kitemlistheaderwidget.h"
@@ -402,6 +406,10 @@ qreal KItemListView::verticalPageStep() const
 
 std::optional<int> KItemListView::itemAt(const QPointF &pos) const
 {
+    if (headerBoundaries().contains(pos)) {
+        return std::nullopt;
+    }
+
     QHashIterator<int, KItemListWidget *> it(m_visibleItems);
     while (it.hasNext()) {
         it.next();
@@ -546,64 +554,88 @@ void KItemListView::scrollToItem(int index, ViewItemPosition viewItemPosition)
     }
     QRectF currentRect = itemRect(index);
 
+    if (layoutDirection() == Qt::RightToLeft && scrollOrientation() == Qt::Horizontal) {
+        currentRect.moveLeft(m_layouter->size().width() - currentRect.right());
+    }
+
     // Fix for Bug 311099 - View the underscore when using Ctrl + PageDown
     currentRect.adjust(-m_styleOption.horizontalMargin, -m_styleOption.verticalMargin, m_styleOption.horizontalMargin, m_styleOption.verticalMargin);
 
-    qreal newOffset = scrollOffset();
-    if (scrollOrientation() == Qt::Vertical && (currentRect.top() < viewGeometry.top() || currentRect.bottom() > viewGeometry.bottom())) {
-        switch (viewItemPosition) {
-        case Beginning:
-            newOffset += currentRect.top() - viewGeometry.top();
-            break;
-        case Middle:
-            newOffset += 0.5 * (currentRect.top() + currentRect.bottom() - (viewGeometry.top() + viewGeometry.bottom()));
-            break;
-        case End:
-            newOffset += currentRect.bottom() - viewGeometry.bottom();
-            break;
-        case Nearest:
-            if (currentRect.top() < viewGeometry.top()) {
-                newOffset += currentRect.top() - viewGeometry.top();
-            } else {
-                newOffset += currentRect.bottom() - viewGeometry.bottom();
-            }
-            break;
-        default:
-            Q_UNREACHABLE();
-        }
-    } else if (scrollOrientation() == Qt::Horizontal && (currentRect.left() < viewGeometry.left() || currentRect.right() > viewGeometry.right())) {
-        switch (viewItemPosition) {
-        case Beginning:
-            if (layoutDirection() == Qt::RightToLeft) {
-                newOffset += currentRect.right() - viewGeometry.right();
-            } else {
-                newOffset += currentRect.left() - viewGeometry.left();
-            }
-            break;
-        case Middle:
-            newOffset += 0.5 * (currentRect.left() + currentRect.right() - (viewGeometry.left() + viewGeometry.right()));
-            break;
-        case End:
-            if (layoutDirection() == Qt::RightToLeft) {
-                newOffset += currentRect.left() - viewGeometry.left();
-            } else {
-                newOffset += currentRect.right() - viewGeometry.right();
+    qreal offset = 0;
+    switch (scrollOrientation()) {
+    case Qt::Vertical:
+        if (currentRect.top() < viewGeometry.top() || currentRect.bottom() > viewGeometry.bottom()) {
+            switch (viewItemPosition) {
+            case Beginning:
+                offset = currentRect.top() - viewGeometry.top();
+                break;
+            case Middle:
+                offset = 0.5 * (currentRect.top() + currentRect.bottom() - (viewGeometry.top() + viewGeometry.bottom()));
+                break;
+            case End:
+                offset = currentRect.bottom() - viewGeometry.bottom();
+                break;
+            case Nearest:
+                if (currentRect.top() < viewGeometry.top()) {
+                    offset = currentRect.top() - viewGeometry.top();
+                }
+                if (currentRect.bottom() > viewGeometry.bottom() + offset) {
+                    offset += currentRect.bottom() - viewGeometry.bottom() - offset;
+                }
+                break;
+            default:
+                Q_UNREACHABLE();
             }
-            break;
-        case Nearest:
-            if (currentRect.left() < viewGeometry.left()) {
-                newOffset += currentRect.left() - viewGeometry.left();
-            } else {
-                newOffset += currentRect.right() - viewGeometry.right();
+        }
+        break;
+    case Qt::Horizontal:
+        if (currentRect.left() < viewGeometry.left() || currentRect.right() > viewGeometry.right()) {
+            switch (viewItemPosition) {
+            case Beginning:
+                if (layoutDirection() == Qt::RightToLeft) {
+                    offset = currentRect.right() - viewGeometry.right();
+                } else {
+                    offset = currentRect.left() - viewGeometry.left();
+                }
+                break;
+            case Middle:
+                offset = 0.5 * (currentRect.left() + currentRect.right() - (viewGeometry.left() + viewGeometry.right()));
+                break;
+            case End:
+                if (layoutDirection() == Qt::RightToLeft) {
+                    offset = currentRect.left() - viewGeometry.left();
+                } else {
+                    offset = currentRect.right() - viewGeometry.right();
+                }
+                break;
+            case Nearest:
+                if (layoutDirection() == Qt::RightToLeft) {
+                    if (currentRect.left() < viewGeometry.left()) {
+                        offset = currentRect.left() - viewGeometry.left();
+                    }
+                    if (currentRect.right() > viewGeometry.right() + offset) {
+                        offset += currentRect.right() - viewGeometry.right() - offset;
+                    }
+                } else {
+                    if (currentRect.right() > viewGeometry.right()) {
+                        offset = currentRect.right() - viewGeometry.right();
+                    }
+                    if (currentRect.left() < viewGeometry.left() + offset) {
+                        offset += currentRect.left() - viewGeometry.left() - offset;
+                    }
+                }
+                break;
+            default:
+                Q_UNREACHABLE();
             }
-            break;
-        default:
-            Q_UNREACHABLE();
         }
+        break;
+    default:
+        Q_UNREACHABLE();
     }
 
-    if (newOffset != scrollOffset()) {
-        Q_EMIT scrollTo(newOffset);
+    if (!qFuzzyIsNull(offset)) {
+        Q_EMIT scrollTo(scrollOffset() + offset);
         return;
     }
 
@@ -1212,6 +1244,11 @@ void KItemListView::slotItemsInserted(const KItemRangeList &itemRanges)
     if (useAlternateBackgrounds()) {
         updateAlternateBackgrounds();
     }
+#ifndef QT_NO_ACCESSIBILITY
+    if (QAccessible::isActive()) { // Announce that the count of items has changed.
+        static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(this))->announceDescriptionChange();
+    }
+#endif
 }
 
 void KItemListView::slotItemsRemoved(const KItemRangeList &itemRanges)
@@ -1330,6 +1367,11 @@ void KItemListView::slotItemsRemoved(const KItemRangeList &itemRanges)
     if (useAlternateBackgrounds()) {
         updateAlternateBackgrounds();
     }
+#ifndef QT_NO_ACCESSIBILITY
+    if (QAccessible::isActive()) { // Announce that the count of items has changed.
+        static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(this))->announceDescriptionChange();
+    }
+#endif
 }
 
 void KItemListView::slotItemsMoved(const KItemRange &itemRange, const QList<int> &movedToIndexes)
@@ -1388,10 +1430,12 @@ void KItemListView::slotItemsChanged(const KItemRangeList &itemRanges, const QSe
             doLayout(NoAnimation);
         }
 
+#ifndef QT_NO_ACCESSIBILITY
         QAccessibleTableModelChangeEvent ev(this, QAccessibleTableModelChangeEvent::DataChanged);
         ev.setFirstRow(itemRange.index);
         ev.setLastRow(itemRange.index + itemRange.count);
         QAccessible::updateAccessibility(&ev);
+#endif
     }
 
     doLayout(NoAnimation);
@@ -1455,8 +1499,6 @@ void KItemListView::slotSortRoleChanged(const QByteArray &current, const QByteAr
 
 void KItemListView::slotCurrentChanged(int current, int previous)
 {
-    Q_UNUSED(previous)
-
     // In SingleSelection mode (e.g., in the Places Panel), the current item is
     // always the selected item. It is not necessary to highlight the current item then.
     if (m_controller->selectionBehavior() != KItemListController::SingleSelection) {
@@ -1468,25 +1510,52 @@ void KItemListView::slotCurrentChanged(int current, int previous)
         KItemListWidget *currentWidget = m_visibleItems.value(current, nullptr);
         if (currentWidget) {
             currentWidget->setCurrent(true);
+            if (hasFocus() || (previousWidget && previousWidget->hasFocus())) {
+                currentWidget->setFocus(); // Mostly for accessibility, because keyboard events are handled correctly either way.
+            }
         }
     }
-
-    QAccessibleEvent ev(this, QAccessible::Focus);
-    ev.setChild(current);
-    QAccessible::updateAccessibility(&ev);
+#ifndef QT_NO_ACCESSIBILITY
+    if (QAccessible::isActive()) {
+        if (current >= 0) {
+            QAccessibleEvent accessibleFocusCurrentItemEvent(this, QAccessible::Focus);
+            accessibleFocusCurrentItemEvent.setChild(current);
+            QAccessible::updateAccessibility(&accessibleFocusCurrentItemEvent);
+        }
+        static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(this))->announceDescriptionChange();
+    }
+#endif
 }
 
 void KItemListView::slotSelectionChanged(const KItemSet &current, const KItemSet &previous)
 {
-    Q_UNUSED(previous)
-
     QHashIterator<int, KItemListWidget *> it(m_visibleItems);
     while (it.hasNext()) {
         it.next();
         const int index = it.key();
         KItemListWidget *widget = it.value();
-        widget->setSelected(current.contains(index));
+        const bool isSelected(current.contains(index));
+        widget->setSelected(isSelected);
+
+#ifndef QT_NO_ACCESSIBILITY
+        if (!QAccessible::isActive()) {
+            continue;
+        }
+        // Let the screen reader announce "selected" or "not selected" for the active item.
+        const bool wasSelected(previous.contains(index));
+        if (isSelected != wasSelected) {
+            QAccessibleEvent accessibleSelectionChangedEvent(this, QAccessible::Selection);
+            accessibleSelectionChangedEvent.setChild(index);
+            QAccessible::updateAccessibility(&accessibleSelectionChangedEvent);
+        }
+    }
+    // Usually the below does not have an effect because the view will not have focus at this moment but one of its list items. Still we announce the
+    // change of the accessibility description just in case the user manually moved focus up by one.
+    static_cast<KItemListViewAccessible *>(QAccessible::queryAccessibleInterface(this))->announceDescriptionChange();
+#else
     }
+    Q_UNUSED(previous)
+#endif
 }
 
 void KItemListView::slotAnimationFinished(QGraphicsWidget *widget, KItemListViewAnimation::AnimationType type)