X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/d9c11ee99906558ae853fc3b42167e884d2a940e..94e08f04f4524fc4993b921a1ae1dd8cc6f8725c:/src/kitemviews/kitemlistview.cpp diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index cf1483659..75d85be35 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -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" @@ -21,6 +25,8 @@ #include "private/kitemlistsizehintresolver.h" #include "private/kitemlistviewlayouter.h" +#include + #include #include #include @@ -100,6 +106,7 @@ KItemListView::KItemListView(QGraphicsWidget *parent) , m_header(nullptr) , m_headerWidget(nullptr) , m_indicatorAnimation(nullptr) + , m_statusBarOffset(0) , m_dropIndicator() , m_sizeHintResolver(nullptr) { @@ -186,7 +193,7 @@ qreal KItemListView::scrollOffset() const qreal KItemListView::maximumScrollOffset() const { - return m_layouter->maximumScrollOffset(); + return m_layouter->maximumScrollOffset() + m_statusBarOffset; } void KItemListView::setItemOffset(qreal offset) @@ -379,7 +386,7 @@ void KItemListView::setGeometry(const QRectF &rect) if (m_headerWidget->automaticColumnResizing()) { applyAutomaticColumnWidths(); } else { - const qreal requiredWidth = columnWidthsSum() + 2 * m_headerWidget->sidePadding(); + const qreal requiredWidth = m_headerWidget->leftPadding() + columnWidthsSum() + m_headerWidget->rightPadding(); const QSizeF dynamicItemSize(qMax(newSize.width(), requiredWidth), m_itemSize.height()); m_layouter->setItemSize(dynamicItemSize); } @@ -391,6 +398,12 @@ void KItemListView::setGeometry(const QRectF &rect) doLayout(NoAnimation); } +qreal KItemListView::scrollSingleStep() const +{ + const QFontMetrics metrics(font()); + return metrics.height(); +} + qreal KItemListView::verticalPageStep() const { qreal headerHeight = 0; @@ -402,13 +415,17 @@ qreal KItemListView::verticalPageStep() const std::optional KItemListView::itemAt(const QPointF &pos) const { + if (headerBoundaries().contains(pos)) { + return std::nullopt; + } + QHashIterator it(m_visibleItems); while (it.hasNext()) { it.next(); const KItemListWidget *widget = it.value(); const QPointF mappedPos = widget->mapFromItem(this, pos); - if (widget->contains(mappedPos) || widget->selectionRect().contains(mappedPos)) { + if (widget->contains(mappedPos)) { return it.key(); } } @@ -525,7 +542,7 @@ QRectF KItemListView::itemContextRect(int index) const const KItemListWidget *widget = m_visibleItems.value(index); if (widget) { - contextRect = widget->iconRect() | widget->textRect(); + contextRect = widget->selectionRectCore(); contextRect.translate(itemRect(index).topLeft()); } @@ -544,66 +561,93 @@ void KItemListView::scrollToItem(int index, ViewItemPosition viewItemPosition) const qreal headerHeight = m_headerWidget->size().height(); viewGeometry.adjust(0, headerHeight, 0, 0); } + if (m_statusBarOffset != 0) { + viewGeometry.adjust(0, 0, 0, -m_statusBarOffset); + } 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; } @@ -706,11 +750,16 @@ QPixmap KItemListView::createDragPixmap(const KItemSet &indexes) const void KItemListView::editRole(int index, const QByteArray &role) { KStandardItemListWidget *widget = qobject_cast(m_visibleItems.value(index)); - if (!widget || m_editingRole) { + if (!widget) { + return; + } + if (m_editingRole || m_animation->isStarted(widget)) { + Q_EMIT widget->roleEditingCanceled(index, role, QVariant()); return; } m_editingRole = true; + m_controller->selectionManager()->setCurrentItem(index); widget->setEditedRole(role); connect(widget, &KItemListWidget::roleEditingCanceled, this, &KItemListView::slotRoleEditingCanceled); @@ -794,6 +843,16 @@ void KItemListView::paint(QPainter *painter, const QStyleOptionGraphicsItem *opt } } +void KItemListView::setStatusBarOffset(int offset) +{ + if (m_statusBarOffset != offset) { + m_statusBarOffset = offset; + if (m_layouter) { + m_layouter->setStatusBarOffset(offset); + } + } +} + QVariant KItemListView::itemChange(GraphicsItemChange change, const QVariant &value) { if (change == QGraphicsItem::ItemSceneHasChanged && scene()) { @@ -1344,9 +1403,20 @@ void KItemListView::slotItemsMoved(const KItemRange &itemRange, const QList const int firstVisibleMovedIndex = qMax(firstVisibleIndex(), itemRange.index); const int lastVisibleMovedIndex = qMin(lastVisibleIndex(), itemRange.index + itemRange.count - 1); + /// Represents an item that was moved while being edited. + struct MovedEditedItem { + int movedToIndex; + QByteArray editedRole; + }; + std::optional movedEditedItem; for (int index = firstVisibleMovedIndex; index <= lastVisibleMovedIndex; ++index) { KItemListWidget *widget = m_visibleItems.value(index); if (widget) { + if (m_editingRole && !widget->editedRole().isEmpty()) { + movedEditedItem = {movedToIndexes[index - itemRange.index], widget->editedRole()}; + disconnectRoleEditingSignals(index); + m_editingRole = false; + } updateWidgetProperties(widget, index); initializeItemListWidget(widget); } @@ -1354,6 +1424,10 @@ void KItemListView::slotItemsMoved(const KItemRange &itemRange, const QList doLayout(NoAnimation); updateSiblingsInformation(); + + if (movedEditedItem) { + editRole(movedEditedItem->movedToIndex, movedEditedItem->editedRole); + } } void KItemListView::slotItemsChanged(const KItemRangeList &itemRanges, const QSet &roles) @@ -1387,13 +1461,7 @@ void KItemListView::slotItemsChanged(const KItemRangeList &itemRanges, const QSe updateVisibleGroupHeaders(); doLayout(NoAnimation); } - - QAccessibleTableModelChangeEvent ev(this, QAccessibleTableModelChangeEvent::DataChanged); - ev.setFirstRow(itemRange.index); - ev.setLastRow(itemRange.index + itemRange.count); - QAccessible::updateAccessibility(&ev); } - doLayout(NoAnimation); } @@ -1455,8 +1523,6 @@ void KItemListView::slotSortRoleChanged(const QByteArray ¤t, 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) { @@ -1470,23 +1536,39 @@ void KItemListView::slotCurrentChanged(int current, int previous) currentWidget->setCurrent(true); } } - - QAccessibleEvent ev(this, QAccessible::Focus); - ev.setChild(current); - QAccessible::updateAccessibility(&ev); +#ifndef QT_NO_ACCESSIBILITY + if (current != previous && QAccessible::isActive()) { + static_cast(QAccessible::queryAccessibleInterface(this))->announceCurrentItem(); + } +#endif } void KItemListView::slotSelectionChanged(const KItemSet ¤t, const KItemSet &previous) { - Q_UNUSED(previous) - QHashIterator 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::SelectionAdd); + accessibleSelectionChangedEvent.setChild(index); + QAccessible::updateAccessibility(&accessibleSelectionChangedEvent); + } } +#else + } + Q_UNUSED(previous) +#endif } void KItemListView::slotAnimationFinished(QGraphicsWidget *widget, KItemListViewAnimation::AnimationType type) @@ -1539,10 +1621,10 @@ void KItemListView::slotRubberBandActivationChanged(bool active) curve.addCubicBezierSegment(QPointF(0.4, 0.0), QPointF(1.0, 1.0), QPointF(1.0, 1.0)); animation->setEasingCurve(curve); - connect(animation, &QVariantAnimation::valueChanged, this, [=](const QVariant &) { + connect(animation, &QVariantAnimation::valueChanged, this, [=, this](const QVariant &) { update(); }); - connect(animation, &QVariantAnimation::finished, this, [=]() { + connect(animation, &QVariantAnimation::finished, this, [=, this]() { m_rubberBandAnimations.removeAll(animation); delete animation; }); @@ -1838,6 +1920,9 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha if (animate) { if (m_animation->isStarted(widget, KItemListViewAnimation::MovingAnimation)) { + if (m_editingRole) { + Q_EMIT widget->roleEditingCanceled(widget->index(), QByteArray(), QVariant()); + } m_animation->start(widget, KItemListViewAnimation::MovingAnimation, newPos); applyNewPos = false; } @@ -2331,7 +2416,7 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi void KItemListView::applyColumnWidthsFromHeader() { // Apply the new size to the layouter - const qreal requiredWidth = columnWidthsSum() + 2 * m_headerWidget->sidePadding(); + const qreal requiredWidth = m_headerWidget->leftPadding() + columnWidthsSum() + m_headerWidget->rightPadding(); const QSizeF dynamicItemSize(qMax(size().width(), requiredWidth), m_itemSize.height()); m_layouter->setItemSize(dynamicItemSize); @@ -2348,7 +2433,7 @@ void KItemListView::updateWidgetColumnWidths(KItemListWidget *widget) for (const QByteArray &role : std::as_const(m_visibleRoles)) { widget->setColumnWidth(role, m_headerWidget->columnWidth(role)); } - widget->setSidePadding(m_headerWidget->sidePadding()); + widget->setSidePadding(m_headerWidget->leftPadding(), m_headerWidget->rightPadding()); } void KItemListView::updatePreferredColumnWidths(const KItemRangeList &itemRanges) @@ -2426,9 +2511,9 @@ void KItemListView::applyAutomaticColumnWidths() qreal firstColumnWidth = m_headerWidget->columnWidth(firstRole); QSizeF dynamicItemSize = m_itemSize; - qreal requiredWidth = columnWidthsSum() + 2 * m_headerWidget->sidePadding(); // Adding the padding a second time so we have the same padding - // symmetrically on both sides of the view. This improves UX, looks better and increases the chances of users figuring out that the padding - // area can be used for deselecting and dropping files. + qreal requiredWidth = m_headerWidget->leftPadding() + columnWidthsSum() + m_headerWidget->rightPadding(); + // By default we want the same padding symmetrically on both sides of the view. This improves UX, looks better and increases the chances of users figuring + // out that the padding area can be used for deselecting and dropping files. const qreal availableWidth = size().width(); if (requiredWidth < availableWidth) { // Stretch the first column to use the whole remaining width