X-Git-Url: https://cloud.milkyroute.net/gitweb/dolphin.git/blobdiff_plain/68520f55f0b6b2fc450f646477bf3c535324aa2b..3bf471e0:/src/kitemviews/kitemlistview.cpp diff --git a/src/kitemviews/kitemlistview.cpp b/src/kitemviews/kitemlistview.cpp index 80a4ba7e3..77114735b 100644 --- a/src/kitemviews/kitemlistview.cpp +++ b/src/kitemviews/kitemlistview.cpp @@ -1,52 +1,34 @@ -/*************************************************************************** - * Copyright (C) 2011 by Peter Penz * - * * - * Based on the Itemviews NG project from Trolltech Labs: * - * http://qt.gitorious.org/qt-labs/itemviews-ng * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * - ***************************************************************************/ +/* + * SPDX-FileCopyrightText: 2011 Peter Penz + * + * Based on the Itemviews NG project from Trolltech Labs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ #include "kitemlistview.h" -#include +#include "dolphindebug.h" #include "kitemlistcontainer.h" #include "kitemlistcontroller.h" #include "kitemlistheader.h" #include "kitemlistselectionmanager.h" -#include "kitemlistwidget.h" +#include "kitemlistviewaccessible.h" +#include "kstandarditemlistwidget.h" #include "private/kitemlistheaderwidget.h" #include "private/kitemlistrubberband.h" #include "private/kitemlistsizehintresolver.h" #include "private/kitemlistviewlayouter.h" -#include "private/kitemlistviewanimation.h" -#include +#include #include #include -#include #include -#include #include #include +#include -#include - -#include "kitemlistviewaccessible.h" namespace { // Time in ms until reaching the autoscroll margin triggers @@ -55,10 +37,15 @@ namespace { // Delay in ms for triggering the next autoscroll const int RepeatingAutoScrollDelay = 1000 / 60; + + // Copied from the Kirigami.Units.shortDuration + const int RubberFadeSpeed = 150; + + const char* RubberPropertyName = "_kitemviews_rubberBandPosition"; } #ifndef QT_NO_ACCESSIBILITY -QAccessibleInterface* accessibleInterfaceFactory(const QString &key, QObject *object) +QAccessibleInterface* accessibleInterfaceFactory(const QString& key, QObject* object) { Q_UNUSED(key) @@ -68,7 +55,7 @@ QAccessibleInterface* accessibleInterfaceFactory(const QString &key, QObject *ob return new KItemListViewAccessible(view); } - return 0; + return nullptr; } #endif @@ -81,50 +68,63 @@ KItemListView::KItemListView(QGraphicsWidget* parent) : m_activeTransactions(0), m_endTransactionAnimationHint(Animation), m_itemSize(), - m_controller(0), - m_model(0), + m_controller(nullptr), + m_model(nullptr), m_visibleRoles(), - m_widgetCreator(0), - m_groupHeaderCreator(0), + m_widgetCreator(nullptr), + m_groupHeaderCreator(nullptr), m_styleOption(), m_visibleItems(), m_visibleGroups(), m_visibleCells(), - m_sizeHintResolver(0), - m_layouter(0), - m_animation(0), - m_layoutTimer(0), + m_sizeHintResolver(nullptr), + m_layouter(nullptr), + m_animation(nullptr), m_oldScrollOffset(0), m_oldMaximumScrollOffset(0), m_oldItemOffset(0), m_oldMaximumItemOffset(0), m_skipAutoScrollForRubberBand(false), - m_rubberBand(0), + m_rubberBand(nullptr), + m_tapAndHoldIndicator(nullptr), m_mousePos(), m_autoScrollIncrement(0), - m_autoScrollTimer(0), - m_header(0), - m_headerWidget(0), + m_autoScrollTimer(nullptr), + m_header(nullptr), + m_headerWidget(nullptr), + m_indicatorAnimation(nullptr), m_dropIndicator() { setAcceptHoverEvents(true); + setAcceptTouchEvents(true); m_sizeHintResolver = new KItemListSizeHintResolver(this); - m_layouter = new KItemListViewLayouter(this); - m_layouter->setSizeHintResolver(m_sizeHintResolver); + m_layouter = new KItemListViewLayouter(m_sizeHintResolver, this); m_animation = new KItemListViewAnimation(this); - connect(m_animation, SIGNAL(finished(QGraphicsWidget*,KItemListViewAnimation::AnimationType)), - this, SLOT(slotAnimationFinished(QGraphicsWidget*,KItemListViewAnimation::AnimationType))); - - m_layoutTimer = new QTimer(this); - m_layoutTimer->setInterval(300); - m_layoutTimer->setSingleShot(true); - connect(m_layoutTimer, SIGNAL(timeout()), this, SLOT(slotLayoutTimerFinished())); + connect(m_animation, &KItemListViewAnimation::finished, + this, &KItemListView::slotAnimationFinished); m_rubberBand = new KItemListRubberBand(this); - connect(m_rubberBand, SIGNAL(activationChanged(bool)), this, SLOT(slotRubberBandActivationChanged(bool))); + connect(m_rubberBand, &KItemListRubberBand::activationChanged, this, &KItemListView::slotRubberBandActivationChanged); + + m_tapAndHoldIndicator = new KItemListRubberBand(this); + m_indicatorAnimation = new QPropertyAnimation(m_tapAndHoldIndicator, "endPosition", this); + connect(m_tapAndHoldIndicator, &KItemListRubberBand::activationChanged, this, [this](bool active) { + if (active) { + m_indicatorAnimation->setDuration(150); + m_indicatorAnimation->setStartValue(QPointF(1, 1)); + m_indicatorAnimation->setEndValue(QPointF(40, 40)); + m_indicatorAnimation->start(); + } + update(); + }); + connect(m_tapAndHoldIndicator, &KItemListRubberBand::endPositionChanged, this, [this]() { + if (m_tapAndHoldIndicator->isActive()) { + update(); + } + }); m_headerWidget = new KItemListHeaderWidget(this); m_headerWidget->setVisible(false); @@ -143,13 +143,13 @@ KItemListView::~KItemListView() // widgetCreator(). So it is mandatory to delete the group headers // first. delete m_groupHeaderCreator; - m_groupHeaderCreator = 0; + m_groupHeaderCreator = nullptr; delete m_widgetCreator; - m_widgetCreator = 0; + m_widgetCreator = nullptr; delete m_sizeHintResolver; - m_sizeHintResolver = 0; + m_sizeHintResolver = nullptr; } void KItemListView::setScrollOffset(qreal offset) @@ -230,7 +230,7 @@ void KItemListView::setVisibleRoles(const QList& roles) if (!m_headerWidget->automaticColumnResizing()) { // The column-width of new roles are still 0. Apply the preferred // column-width as default with. - foreach (const QByteArray& role, m_visibleRoles) { + for (const QByteArray& role : qAsConst(m_visibleRoles)) { if (m_headerWidget->columnWidth(role) == 0) { const qreal width = m_headerWidget->preferredColumnWidth(role); m_headerWidget->setColumnWidth(role, width); @@ -268,17 +268,17 @@ void KItemListView::setAutoScroll(bool enabled) if (enabled && !m_autoScrollTimer) { m_autoScrollTimer = new QTimer(this); m_autoScrollTimer->setSingleShot(true); - connect(m_autoScrollTimer, SIGNAL(timeout()), this, SLOT(triggerAutoScrolling())); + connect(m_autoScrollTimer, &QTimer::timeout, this, &KItemListView::triggerAutoScrolling); m_autoScrollTimer->start(InitialAutoScrollDelay); } else if (!enabled && m_autoScrollTimer) { delete m_autoScrollTimer; - m_autoScrollTimer = 0; + m_autoScrollTimer = nullptr; } } bool KItemListView::autoScroll() const { - return m_autoScrollTimer != 0; + return m_autoScrollTimer != nullptr; } void KItemListView::setEnabledSelectionToggles(bool enabled) @@ -311,9 +311,7 @@ KItemModelBase* KItemListView::model() const void KItemListView::setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator) { - if (m_widgetCreator) { - delete m_widgetCreator; - } + delete m_widgetCreator; m_widgetCreator = widgetCreator; } @@ -327,9 +325,7 @@ KItemListWidgetCreatorBase* KItemListView::widgetCreator() const void KItemListView::setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator) { - if (m_groupHeaderCreator) { - delete m_groupHeaderCreator; - } + delete m_groupHeaderCreator; m_groupHeaderCreator = groupHeaderCreator; } @@ -370,29 +366,12 @@ void KItemListView::setGeometry(const QRectF& rect) m_itemSize.height()); m_layouter->setItemSize(dynamicItemSize); } - - // Triggering a synchronous layout is fine from a performance point of view, - // as with dynamic item sizes no moving animation must be done. - m_layouter->setSize(newSize); - doLayout(NoAnimation); - } else { - const bool animate = !changesItemGridLayout(newSize, - m_layouter->itemSize(), - m_layouter->itemMargin()); - m_layouter->setSize(newSize); - - if (animate) { - // Trigger an asynchronous relayout with m_layoutTimer to prevent - // performance bottlenecks. If the timer is exceeded, an animated layout - // will be triggered. - if (!m_layoutTimer->isActive()) { - m_layoutTimer->start(); - } - } else { - m_layoutTimer->stop(); - doLayout(NoAnimation); - } } + + m_layouter->setSize(newSize); + // We don't animate the moving of the items here because + // it would look like the items are slow to find their position. + doLayout(NoAnimation); } qreal KItemListView::verticalPageStep() const @@ -404,7 +383,7 @@ qreal KItemListView::verticalPageStep() const return size().height() - headerHeight; } -int KItemListView::itemAt(const QPointF& pos) const +std::optional KItemListView::itemAt(const QPointF& pos) const { QHashIterator it(m_visibleItems); while (it.hasNext()) { @@ -412,12 +391,12 @@ int KItemListView::itemAt(const QPointF& pos) const const KItemListWidget* widget = it.value(); const QPointF mappedPos = widget->mapFromItem(this, pos); - if (widget->contains(mappedPos)) { + if (widget->contains(mappedPos) || widget->selectionRect().contains(mappedPos)) { return it.key(); } } - return -1; + return std::nullopt; } bool KItemListView::isAboveSelectionToggle(int index, const QPointF& pos) const @@ -450,6 +429,19 @@ bool KItemListView::isAboveExpansionToggle(int index, const QPointF& pos) const return false; } +bool KItemListView::isAboveText(int index, const QPointF &pos) const +{ + const KItemListWidget* widget = m_visibleItems.value(index); + if (widget) { + const QRectF &textRect = widget->textRect(); + if (!textRect.isEmpty()) { + const QPointF mappedPos = widget->mapFromItem(this, pos); + return textRect.contains(mappedPos); + } + } + return false; +} + int KItemListView::firstVisibleIndex() const { return m_layouter->firstVisibleIndex(); @@ -460,9 +452,9 @@ int KItemListView::lastVisibleIndex() const return m_layouter->lastVisibleIndex(); } -QSizeF KItemListView::itemSizeHint(int index) const +void KItemListView::calculateItemSizeHints(QVector>& logicalHeightHints, qreal& logicalWidthHint) const { - return widgetCreator()->itemSizeHint(index, this); + widgetCreator()->calculateItemSizeHints(logicalHeightHints, logicalWidthHint, this); } void KItemListView::setSupportsItemExpanding(bool supportsExpanding) @@ -479,6 +471,32 @@ bool KItemListView::supportsItemExpanding() const return m_supportsItemExpanding; } +void KItemListView::setHighlightEntireRow(bool highlightEntireRow) +{ + if (m_highlightEntireRow != highlightEntireRow) { + m_highlightEntireRow = highlightEntireRow; + onHighlightEntireRowChanged(highlightEntireRow); + } +} + +bool KItemListView::highlightEntireRow() const +{ + return m_highlightEntireRow; +} + +void KItemListView::setAlternateBackgrounds(bool alternate) +{ + if (m_alternateBackgrounds != alternate) { + m_alternateBackgrounds = alternate; + updateAlternateBackgrounds(); + } +} + +bool KItemListView::alternateBackgrounds() const +{ + return m_alternateBackgrounds; +} + QRectF KItemListView::itemRect(int index) const { return m_layouter->itemRect(index); @@ -497,6 +515,11 @@ QRectF KItemListView::itemContextRect(int index) const return contextRect; } +bool KItemListView::isElided(int index) const +{ + return m_sizeHintResolver->isElided(index); +} + void KItemListView::scrollToItem(int index) { QRectF viewGeometry = geometry(); @@ -504,7 +527,11 @@ void KItemListView::scrollToItem(int index) const qreal headerHeight = m_headerWidget->size().height(); viewGeometry.adjust(0, headerHeight, 0, 0); } - const QRectF currentRect = itemRect(index); + QRectF currentRect = itemRect(index); + + // Fix for Bug 311099 - View the underscore when using Ctrl + PagDown + currentRect.adjust(-m_styleOption.horizontalMargin, -m_styleOption.verticalMargin, + m_styleOption.horizontalMargin, m_styleOption.verticalMargin); if (!viewGeometry.contains(currentRect)) { qreal newOffset = scrollOffset(); @@ -523,9 +550,12 @@ void KItemListView::scrollToItem(int index) } if (newOffset != scrollOffset()) { - emit scrollTo(newOffset); + Q_EMIT scrollTo(newOffset); + return; } } + + Q_EMIT scrollingStopped(); } void KItemListView::beginTransaction() @@ -541,7 +571,7 @@ void KItemListView::endTransaction() --m_activeTransactions; if (m_activeTransactions < 0) { m_activeTransactions = 0; - kWarning() << "Mismatch between beginTransaction()/endTransaction()"; + qCWarning(DolphinDebug) << "Mismatch between beginTransaction()/endTransaction()"; } if (m_activeTransactions == 0) { @@ -569,26 +599,30 @@ void KItemListView::setHeaderVisible(bool visible) m_headerWidget->setColumns(m_visibleRoles); m_headerWidget->setZValue(1); - connect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), - this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal))); - connect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)), - this, SLOT(slotHeaderColumnMoved(QByteArray,int,int))); - connect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), - this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder))); - connect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), - this, SIGNAL(sortRoleChanged(QByteArray,QByteArray))); + connect(m_headerWidget, &KItemListHeaderWidget::columnWidthChanged, + this, &KItemListView::slotHeaderColumnWidthChanged); + connect(m_headerWidget, &KItemListHeaderWidget::leadingPaddingChanged, + this, &KItemListView::slotLeadingPaddingChanged); + connect(m_headerWidget, &KItemListHeaderWidget::columnMoved, + this, &KItemListView::slotHeaderColumnMoved); + connect(m_headerWidget, &KItemListHeaderWidget::sortOrderChanged, + this, &KItemListView::sortOrderChanged); + connect(m_headerWidget, &KItemListHeaderWidget::sortRoleChanged, + this, &KItemListView::sortRoleChanged); m_layouter->setHeaderHeight(headerSize.height()); m_headerWidget->setVisible(true); } else if (!visible && m_headerWidget->isVisible()) { - disconnect(m_headerWidget, SIGNAL(columnWidthChanged(QByteArray,qreal,qreal)), - this, SLOT(slotHeaderColumnWidthChanged(QByteArray,qreal,qreal))); - disconnect(m_headerWidget, SIGNAL(columnMoved(QByteArray,int,int)), - this, SLOT(slotHeaderColumnMoved(QByteArray,int,int))); - disconnect(m_headerWidget, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), - this, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder))); - disconnect(m_headerWidget, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), - this, SIGNAL(sortRoleChanged(QByteArray,QByteArray))); + disconnect(m_headerWidget, &KItemListHeaderWidget::columnWidthChanged, + this, &KItemListView::slotHeaderColumnWidthChanged); + disconnect(m_headerWidget, &KItemListHeaderWidget::leadingPaddingChanged, + this, &KItemListView::slotLeadingPaddingChanged); + disconnect(m_headerWidget, &KItemListHeaderWidget::columnMoved, + this, &KItemListView::slotHeaderColumnMoved); + disconnect(m_headerWidget, &KItemListHeaderWidget::sortOrderChanged, + this, &KItemListView::sortOrderChanged); + disconnect(m_headerWidget, &KItemListHeaderWidget::sortRoleChanged, + this, &KItemListView::sortRoleChanged); m_layouter->setHeaderHeight(0); m_headerWidget->setVisible(false); @@ -605,15 +639,15 @@ KItemListHeader* KItemListView::header() const return m_header; } -QPixmap KItemListView::createDragPixmap(const QSet& indexes) const +QPixmap KItemListView::createDragPixmap(const KItemSet& indexes) const { QPixmap pixmap; if (indexes.count() == 1) { - KItemListWidget* item = m_visibleItems.value(indexes.toList().first()); + KItemListWidget* item = m_visibleItems.value(indexes.first()); QGraphicsView* graphicsView = scene()->views()[0]; if (item && graphicsView) { - pixmap = item->createDragPixmap(0, graphicsView); + pixmap = item->createDragPixmap(nullptr, graphicsView); } } else { // TODO: Not implemented yet. Probably extend the interface @@ -626,7 +660,7 @@ QPixmap KItemListView::createDragPixmap(const QSet& indexes) const void KItemListView::editRole(int index, const QByteArray& role) { - KItemListWidget* widget = m_visibleItems.value(index); + KStandardItemListWidget* widget = qobject_cast(m_visibleItems.value(index)); if (!widget || m_editingRole) { return; } @@ -634,16 +668,43 @@ void KItemListView::editRole(int index, const QByteArray& role) m_editingRole = true; widget->setEditedRole(role); - connect(widget, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), - this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant))); - connect(widget, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), - this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant))); + connect(widget, &KItemListWidget::roleEditingCanceled, + this, &KItemListView::slotRoleEditingCanceled); + connect(widget, &KItemListWidget::roleEditingFinished, + this, &KItemListView::slotRoleEditingFinished); + + connect(this, &KItemListView::scrollOffsetChanged, + widget, &KStandardItemListWidget::finishRoleEditing); } void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { QGraphicsWidget::paint(painter, option, widget); + for (auto animation : qAsConst(m_rubberBandAnimations)) { + QRectF rubberBandRect = animation->property(RubberPropertyName).toRectF(); + + const QPointF topLeft = rubberBandRect.topLeft(); + if (scrollOrientation() == Qt::Vertical) { + rubberBandRect.moveTo(topLeft.x(), topLeft.y() - scrollOffset()); + } else { + rubberBandRect.moveTo(topLeft.x() - scrollOffset(), topLeft.y()); + } + + QStyleOptionRubberBand opt; + initStyleOption(&opt); + opt.shape = QRubberBand::Rectangle; + opt.opaque = false; + opt.rect = rubberBandRect.toRect(); + + painter->save(); + + painter->setOpacity(animation->currentValue().toReal()); + style()->drawControl(QStyle::CE_RubberBand, &opt, painter); + + painter->restore(); + } + if (m_rubberBand->isActive()) { QRectF rubberBandRect = QRectF(m_rubberBand->startPosition(), m_rubberBand->endPosition()).normalized(); @@ -656,7 +717,19 @@ void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* opt } QStyleOptionRubberBand opt; - opt.initFrom(widget); + initStyleOption(&opt); + opt.shape = QRubberBand::Rectangle; + opt.opaque = false; + opt.rect = rubberBandRect.toRect(); + style()->drawControl(QStyle::CE_RubberBand, &opt, painter); + } + + if (m_tapAndHoldIndicator->isActive()) { + const QPointF indicatorSize = m_tapAndHoldIndicator->endPosition(); + const QRectF rubberBandRect = QRectF(m_tapAndHoldIndicator->startPosition() - indicatorSize, + (m_tapAndHoldIndicator->startPosition()) + indicatorSize).normalized(); + QStyleOptionRubberBand opt; + initStyleOption(&opt); opt.shape = QRubberBand::Rectangle; opt.opaque = false; opt.rect = rubberBandRect.toRect(); @@ -666,7 +739,7 @@ void KItemListView::paint(QPainter* painter, const QStyleOptionGraphicsItem* opt if (!m_dropIndicator.isEmpty()) { const QRectF r = m_dropIndicator.toRect(); - QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color(); + QColor color = palette().brush(QPalette::Normal, QPalette::Text).color(); painter->setPen(color); // TODO: The following implementation works only for a vertical scroll-orientation @@ -704,7 +777,7 @@ void KItemListView::setItemSize(const QSizeF& size) size, m_layouter->itemMargin()); - const bool alternateBackgroundsChanged = (m_visibleRoles.count() > 1) && + const bool alternateBackgroundsChanged = m_alternateBackgrounds && (( m_itemSize.isEmpty() && !size.isEmpty()) || (!m_itemSize.isEmpty() && size.isEmpty())); @@ -738,6 +811,10 @@ void KItemListView::setItemSize(const QSizeF& size) void KItemListView::setStyleOption(const KItemListStyleOption& option) { + if (m_styleOption == option) { + return; + } + const KItemListStyleOption previousOption = m_styleOption; m_styleOption = option; @@ -757,7 +834,8 @@ void KItemListView::setStyleOption(const KItemListStyleOption& option) updateGroupHeaderHeight(); } - if (animate && previousOption.maxTextSize != option.maxTextSize) { + if (animate && + (previousOption.maxTextLines != option.maxTextLines || previousOption.maxTextWidth != option.maxTextWidth)) { // Animating a change of the maximum text size just results in expensive // temporary eliding and clipping operations and does not look good visually. animate = false; @@ -804,7 +882,7 @@ void KItemListView::setScrollOrientation(Qt::Orientation orientation) doLayout(NoAnimation); onScrollOrientationChanged(orientation, previousOrientation); - emit scrollOrientationChanged(orientation, previousOrientation); + Q_EMIT scrollOrientationChanged(orientation, previousOrientation); } Qt::Orientation KItemListView::scrollOrientation() const @@ -814,70 +892,75 @@ Qt::Orientation KItemListView::scrollOrientation() const KItemListWidgetCreatorBase* KItemListView::defaultWidgetCreator() const { - return 0; + return nullptr; } KItemListGroupHeaderCreatorBase* KItemListView::defaultGroupHeaderCreator() const { - return 0; + return nullptr; } void KItemListView::initializeItemListWidget(KItemListWidget* item) { - Q_UNUSED(item); + Q_UNUSED(item) } bool KItemListView::itemSizeHintUpdateRequired(const QSet& changedRoles) const { - Q_UNUSED(changedRoles); + Q_UNUSED(changedRoles) return true; } void KItemListView::onControllerChanged(KItemListController* current, KItemListController* previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) } void KItemListView::onModelChanged(KItemModelBase* current, KItemModelBase* previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) } void KItemListView::onScrollOrientationChanged(Qt::Orientation current, Qt::Orientation previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) } void KItemListView::onItemSizeChanged(const QSizeF& current, const QSizeF& previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) } void KItemListView::onScrollOffsetChanged(qreal current, qreal previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) } void KItemListView::onVisibleRolesChanged(const QList& current, const QList& previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) } void KItemListView::onStyleOptionChanged(const KItemListStyleOption& current, const KItemListStyleOption& previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) +} + +void KItemListView::onHighlightEntireRowChanged(bool highlightEntireRow) +{ + Q_UNUSED(highlightEntireRow) } void KItemListView::onSupportsItemExpandingChanged(bool supportsExpanding) { - Q_UNUSED(supportsExpanding); + Q_UNUSED(supportsExpanding) } void KItemListView::onTransactionBegin() @@ -890,11 +973,23 @@ void KItemListView::onTransactionEnd() bool KItemListView::event(QEvent* event) { - // Forward all events to the controller and handle them there - if (!m_editingRole && m_controller && m_controller->processEvent(event, transform())) { - event->accept(); - return true; + switch (event->type()) { + case QEvent::PaletteChange: + updatePalette(); + break; + + case QEvent::FontChange: + updateFont(); + break; + + default: + // Forward all other events to the controller and handle them there + if (!m_editingRole && m_controller && m_controller->processEvent(event, transform())) { + event->accept(); + return true; + } } + return QGraphicsWidget::event(event); } @@ -920,7 +1015,7 @@ void KItemListView::dragEnterEvent(QGraphicsSceneDragDropEvent* event) setAutoScroll(true); } -void KItemListView::dragMoveEvent(QGraphicsSceneDragDropEvent *event) +void KItemListView::dragMoveEvent(QGraphicsSceneDragDropEvent* event) { QGraphicsWidget::dragMoveEvent(event); @@ -930,7 +1025,7 @@ void KItemListView::dragMoveEvent(QGraphicsSceneDragDropEvent *event) } } -void KItemListView::dragLeaveEvent(QGraphicsSceneDragDropEvent *event) +void KItemListView::dragLeaveEvent(QGraphicsSceneDragDropEvent* event) { QGraphicsWidget::dragLeaveEvent(event); setAutoScroll(false); @@ -947,6 +1042,27 @@ QList KItemListView::visibleItemListWidgets() const return m_visibleItems.values(); } +void KItemListView::updateFont() +{ + if (scene() && !scene()->views().isEmpty()) { + KItemListStyleOption option = styleOption(); + option.font = scene()->views().first()->font(); + option.fontMetrics = QFontMetrics(option.font); + + setStyleOption(option); + } +} + +void KItemListView::updatePalette() +{ + if (scene() && !scene()->views().isEmpty()) { + KItemListStyleOption option = styleOption(); + option.palette = scene()->views().first()->palette(); + + setStyleOption(option); + } +} + void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) { if (m_itemSize.isEmpty()) { @@ -963,14 +1079,14 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) m_sizeHintResolver->itemsInserted(itemRanges); int previouslyInsertedCount = 0; - foreach (const KItemRange& range, itemRanges) { + for (const KItemRange& range : itemRanges) { // range.index is related to the model before anything has been inserted. // As in each loop the current item-range gets inserted the index must // be increased by the already previously inserted items. const int index = range.index + previouslyInsertedCount; const int count = range.count; if (index < 0 || count <= 0) { - kWarning() << "Invalid item range (index:" << index << ", count:" << count << ")"; + qCWarning(DolphinDebug) << "Invalid item range (index:" << index << ", count:" << count << ")"; continue; } previouslyInsertedCount += count; @@ -989,7 +1105,7 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) // Update the indexes of all KItemListWidget instances that are located // after the inserted items. It is important to adjust the indexes in the order // from the highest index to the lowest index to prevent overlaps when setting the new index. - qSort(itemsToMove); + std::sort(itemsToMove.begin(), itemsToMove.end()); for (int i = itemsToMove.count() - 1; i >= 0; --i) { KItemListWidget* widget = m_visibleItems.value(itemsToMove[i]); Q_ASSERT(widget); @@ -1011,11 +1127,17 @@ void KItemListView::slotItemsInserted(const KItemRangeList& itemRanges) (!verticalScrollOrientation && maximumScrollOffset() > size().width()); if (decreaseLayouterSize) { const int scrollBarExtent = style()->pixelMetric(QStyle::PM_ScrollBarExtent); + + int scrollbarSpacing = 0; + if (style()->styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents)) { + scrollbarSpacing = style()->pixelMetric(QStyle::PM_ScrollView_ScrollBarSpacing); + } + QSizeF layouterSize = m_layouter->size(); if (verticalScrollOrientation) { - layouterSize.rwidth() -= scrollBarExtent; + layouterSize.rwidth() -= scrollBarExtent + scrollbarSpacing; } else { - layouterSize.rheight() -= scrollBarExtent; + layouterSize.rheight() -= scrollBarExtent + scrollbarSpacing; } m_layouter->setSize(layouterSize); } @@ -1072,18 +1194,21 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) const int index = range.index; const int count = range.count; if (index < 0 || count <= 0) { - kWarning() << "Invalid item range (index:" << index << ", count:" << count << ")"; + qCWarning(DolphinDebug) << "Invalid item range (index:" << index << ", count:" << count << ")"; continue; } const int firstRemovedIndex = index; const int lastRemovedIndex = index + count - 1; - // Remeber which items have to be moved because they are behind the removed range. + // Remember which items have to be moved because they are behind the removed range. QVector itemsToMove; // Remove all KItemListWidget instances that got deleted - foreach (KItemListWidget* widget, m_visibleItems) { + // Iterate over a const copy because the container is mutated within the loop + // directly and in `recycleWidget()` (https://bugs.kde.org/show_bug.cgi?id=428374) + const auto visibleItems = m_visibleItems; + for (KItemListWidget* widget : visibleItems) { const int i = widget->index(); if (i < firstRemovedIndex) { continue; @@ -1119,7 +1244,7 @@ void KItemListView::slotItemsRemoved(const KItemRangeList& itemRanges) // after the deleted items. It is important to update them in ascending // order to prevent overlaps when setting the new index. std::sort(itemsToMove.begin(), itemsToMove.end()); - foreach (int i, itemsToMove) { + for (int i : qAsConst(itemsToMove)) { KItemListWidget* widget = m_visibleItems.value(i); Q_ASSERT(widget); const int newIndex = i - count; @@ -1197,17 +1322,13 @@ void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges, updatePreferredColumnWidths(itemRanges); } - foreach (const KItemRange& itemRange, itemRanges) { + for (const KItemRange& itemRange : itemRanges) { const int index = itemRange.index; const int count = itemRange.count; if (updateSizeHints) { m_sizeHintResolver->itemsChanged(index, count, roles); m_layouter->markAsDirty(); - - if (!m_layoutTimer->isActive()) { - m_layoutTimer->start(); - } } // Apply the changed roles to the visible item-widgets @@ -1225,8 +1346,14 @@ void KItemListView::slotItemsChanged(const KItemRangeList& itemRanges, updateVisibleGroupHeaders(); doLayout(NoAnimation); } + + QAccessibleTableModelChangeEvent ev(this, QAccessibleTableModelChangeEvent::DataChanged); + ev.setFirstRow(itemRange.index); + ev.setLastRow(itemRange.index + itemRange.count); + QAccessible::updateAccessibility(&ev); } - QAccessible::updateAccessibility(this, 0, QAccessible::TableModelChanged); + + doLayout(NoAnimation); } void KItemListView::slotGroupsChanged() @@ -1244,8 +1371,10 @@ void KItemListView::slotGroupedSortingChanged(bool current) if (m_grouped) { updateGroupHeaderHeight(); } else { - // Clear all visible headers - QMutableHashIterator it (m_visibleGroups); + // Clear all visible headers. Note that the QHashIterator takes a copy of + // m_visibleGroups. Therefore, it remains valid even if items are removed + // from m_visibleGroups in recycleGroupHeaderForWidget(). + QHashIterator it(m_visibleGroups); while (it.hasNext()) { it.next(); recycleGroupHeaderForWidget(it.key()); @@ -1265,8 +1394,8 @@ void KItemListView::slotGroupedSortingChanged(bool current) void KItemListView::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) if (m_grouped) { updateVisibleGroupHeaders(); doLayout(NoAnimation); @@ -1275,8 +1404,8 @@ void KItemListView::slotSortOrderChanged(Qt::SortOrder current, Qt::SortOrder pr void KItemListView::slotSortRoleChanged(const QByteArray& current, const QByteArray& previous) { - Q_UNUSED(current); - Q_UNUSED(previous); + Q_UNUSED(current) + Q_UNUSED(previous) if (m_grouped) { updateVisibleGroupHeaders(); doLayout(NoAnimation); @@ -1285,23 +1414,30 @@ void KItemListView::slotSortRoleChanged(const QByteArray& current, const QByteAr void KItemListView::slotCurrentChanged(int current, int previous) { - Q_UNUSED(previous); + Q_UNUSED(previous) - KItemListWidget* previousWidget = m_visibleItems.value(previous, 0); - if (previousWidget) { - previousWidget->setCurrent(false); - } + // 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) { + KItemListWidget* previousWidget = m_visibleItems.value(previous, nullptr); + if (previousWidget) { + previousWidget->setCurrent(false); + } - KItemListWidget* currentWidget = m_visibleItems.value(current, 0); - if (currentWidget) { - currentWidget->setCurrent(true); + KItemListWidget* currentWidget = m_visibleItems.value(current, nullptr); + if (currentWidget) { + currentWidget->setCurrent(true); + } } - QAccessible::updateAccessibility(this, current+1, QAccessible::Focus); + + QAccessibleEvent ev(this, QAccessible::Focus); + ev.setChild(current); + QAccessible::updateAccessibility(&ev); } -void KItemListView::slotSelectionChanged(const QSet& current, const QSet& previous) +void KItemListView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous) { - Q_UNUSED(previous); + Q_UNUSED(previous) QHashIterator it(m_visibleItems); while (it.hasNext()) { @@ -1318,8 +1454,7 @@ void KItemListView::slotAnimationFinished(QGraphicsWidget* widget, KItemListWidget* itemListWidget = qobject_cast(widget); Q_ASSERT(itemListWidget); - switch (type) { - case KItemListViewAnimation::DeleteAnimation: { + if (type == KItemListViewAnimation::DeleteAnimation) { // As we recycle the widget in this case it is important to assure that no // other animation has been started. This is a convention in KItemListView and // not a requirement defined by KItemListViewAnimation. @@ -1330,31 +1465,16 @@ void KItemListView::slotAnimationFinished(QGraphicsWidget* widget, // been finished. recycleGroupHeaderForWidget(itemListWidget); widgetCreator()->recycle(itemListWidget); - break; - } - - case KItemListViewAnimation::CreateAnimation: - case KItemListViewAnimation::MovingAnimation: - case KItemListViewAnimation::ResizeAnimation: { + } else { const int index = itemListWidget->index(); const bool invisible = (index < m_layouter->firstVisibleIndex()) || (index > m_layouter->lastVisibleIndex()); if (invisible && !m_animation->isStarted(itemListWidget)) { recycleWidget(itemListWidget); } - break; - } - - default: break; } } -void KItemListView::slotLayoutTimerFinished() -{ - m_layouter->setSize(geometry().size()); - doLayout(Animation); -} - void KItemListView::slotRubberBandPosChanged() { update(); @@ -1363,12 +1483,36 @@ void KItemListView::slotRubberBandPosChanged() void KItemListView::slotRubberBandActivationChanged(bool active) { if (active) { - connect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); - connect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); + connect(m_rubberBand, &KItemListRubberBand::startPositionChanged, this, &KItemListView::slotRubberBandPosChanged); + connect(m_rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListView::slotRubberBandPosChanged); m_skipAutoScrollForRubberBand = true; } else { - disconnect(m_rubberBand, SIGNAL(startPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); - disconnect(m_rubberBand, SIGNAL(endPositionChanged(QPointF,QPointF)), this, SLOT(slotRubberBandPosChanged())); + QRectF rubberBandRect = QRectF(m_rubberBand->startPosition(), + m_rubberBand->endPosition()).normalized(); + + auto animation = new QVariantAnimation(this); + animation->setStartValue(1.0); + animation->setEndValue(0.0); + animation->setDuration(RubberFadeSpeed); + animation->setProperty(RubberPropertyName, rubberBandRect); + + QEasingCurve curve; + curve.setType(QEasingCurve::BezierSpline); + 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&) { + update(); + }); + connect(animation, &QVariantAnimation::finished, this, [=]() { + m_rubberBandAnimations.removeAll(animation); + delete animation; + }); + animation->start(); + m_rubberBandAnimations << animation; + + disconnect(m_rubberBand, &KItemListRubberBand::startPositionChanged, this, &KItemListView::slotRubberBandPosChanged); + disconnect(m_rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListView::slotRubberBandPosChanged); m_skipAutoScrollForRubberBand = false; } @@ -1379,15 +1523,25 @@ void KItemListView::slotHeaderColumnWidthChanged(const QByteArray& role, qreal currentWidth, qreal previousWidth) { - Q_UNUSED(role); - Q_UNUSED(currentWidth); - Q_UNUSED(previousWidth); + Q_UNUSED(role) + Q_UNUSED(currentWidth) + Q_UNUSED(previousWidth) m_headerWidget->setAutomaticColumnResizing(false); applyColumnWidthsFromHeader(); doLayout(NoAnimation); } +void KItemListView::slotLeadingPaddingChanged(qreal width) +{ + Q_UNUSED(width) + if (m_headerWidget->automaticColumnResizing()) { + applyAutomaticColumnWidths(); + } + applyColumnWidthsFromHeader(); + doLayout(NoAnimation); +} + void KItemListView::slotHeaderColumnMoved(const QByteArray& role, int currentIndex, int previousIndex) @@ -1402,7 +1556,7 @@ void KItemListView::slotHeaderColumnMoved(const QByteArray& role, setVisibleRoles(current); - emit visibleRolesChanged(current, previous); + Q_EMIT visibleRolesChanged(current, previous); } void KItemListView::triggerAutoScrolling() @@ -1477,16 +1631,16 @@ void KItemListView::slotRoleEditingCanceled(int index, const QByteArray& role, c { disconnectRoleEditingSignals(index); - emit roleEditingCanceled(index, role, value); m_editingRole = false; + Q_EMIT roleEditingCanceled(index, role, value); } void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) { disconnectRoleEditingSignals(index); - emit roleEditingFinished(index, role, value); m_editingRole = false; + Q_EMIT roleEditingFinished(index, role, value); } void KItemListView::setController(KItemListController* controller) @@ -1495,16 +1649,16 @@ void KItemListView::setController(KItemListController* controller) KItemListController* previous = m_controller; if (previous) { KItemListSelectionManager* selectionManager = previous->selectionManager(); - disconnect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int))); - disconnect(selectionManager, SIGNAL(selectionChanged(QSet,QSet)), this, SLOT(slotSelectionChanged(QSet,QSet))); + disconnect(selectionManager, &KItemListSelectionManager::currentChanged, this, &KItemListView::slotCurrentChanged); + disconnect(selectionManager, &KItemListSelectionManager::selectionChanged, this, &KItemListView::slotSelectionChanged); } m_controller = controller; if (controller) { KItemListSelectionManager* selectionManager = controller->selectionManager(); - connect(selectionManager, SIGNAL(currentChanged(int,int)), this, SLOT(slotCurrentChanged(int,int))); - connect(selectionManager, SIGNAL(selectionChanged(QSet,QSet)), this, SLOT(slotSelectionChanged(QSet,QSet))); + connect(selectionManager, &KItemListSelectionManager::currentChanged, this, &KItemListView::slotCurrentChanged); + connect(selectionManager, &KItemListSelectionManager::selectionChanged, this, &KItemListView::slotSelectionChanged); } onControllerChanged(controller, previous); @@ -1520,22 +1674,22 @@ void KItemListView::setModel(KItemModelBase* model) KItemModelBase* previous = m_model; if (m_model) { - disconnect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); - disconnect(m_model, SIGNAL(itemsInserted(KItemRangeList)), - this, SLOT(slotItemsInserted(KItemRangeList))); - disconnect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), - this, SLOT(slotItemsRemoved(KItemRangeList))); - disconnect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), - this, SLOT(slotItemsMoved(KItemRange,QList))); - disconnect(m_model, SIGNAL(groupsChanged()), - this, SLOT(slotGroupsChanged())); - disconnect(m_model, SIGNAL(groupedSortingChanged(bool)), - this, SLOT(slotGroupedSortingChanged(bool))); - disconnect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), - this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); - disconnect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), - this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + disconnect(m_model, &KItemModelBase::itemsChanged, + this, &KItemListView::slotItemsChanged); + disconnect(m_model, &KItemModelBase::itemsInserted, + this, &KItemListView::slotItemsInserted); + disconnect(m_model, &KItemModelBase::itemsRemoved, + this, &KItemListView::slotItemsRemoved); + disconnect(m_model, &KItemModelBase::itemsMoved, + this, &KItemListView::slotItemsMoved); + disconnect(m_model, &KItemModelBase::groupsChanged, + this, &KItemListView::slotGroupsChanged); + disconnect(m_model, &KItemModelBase::groupedSortingChanged, + this, &KItemListView::slotGroupedSortingChanged); + disconnect(m_model, &KItemModelBase::sortOrderChanged, + this, &KItemListView::slotSortOrderChanged); + disconnect(m_model, &KItemModelBase::sortRoleChanged, + this, &KItemListView::slotSortRoleChanged); m_sizeHintResolver->itemsRemoved(KItemRangeList() << KItemRange(0, m_model->count())); } @@ -1545,22 +1699,22 @@ void KItemListView::setModel(KItemModelBase* model) m_grouped = model->groupedSorting(); if (m_model) { - connect(m_model, SIGNAL(itemsChanged(KItemRangeList,QSet)), - this, SLOT(slotItemsChanged(KItemRangeList,QSet))); - connect(m_model, SIGNAL(itemsInserted(KItemRangeList)), - this, SLOT(slotItemsInserted(KItemRangeList))); - connect(m_model, SIGNAL(itemsRemoved(KItemRangeList)), - this, SLOT(slotItemsRemoved(KItemRangeList))); - connect(m_model, SIGNAL(itemsMoved(KItemRange,QList)), - this, SLOT(slotItemsMoved(KItemRange,QList))); - connect(m_model, SIGNAL(groupsChanged()), - this, SLOT(slotGroupsChanged())); - connect(m_model, SIGNAL(groupedSortingChanged(bool)), - this, SLOT(slotGroupedSortingChanged(bool))); - connect(m_model, SIGNAL(sortOrderChanged(Qt::SortOrder,Qt::SortOrder)), - this, SLOT(slotSortOrderChanged(Qt::SortOrder,Qt::SortOrder))); - connect(m_model, SIGNAL(sortRoleChanged(QByteArray,QByteArray)), - this, SLOT(slotSortRoleChanged(QByteArray,QByteArray))); + connect(m_model, &KItemModelBase::itemsChanged, + this, &KItemListView::slotItemsChanged); + connect(m_model, &KItemModelBase::itemsInserted, + this, &KItemListView::slotItemsInserted); + connect(m_model, &KItemModelBase::itemsRemoved, + this, &KItemListView::slotItemsRemoved); + connect(m_model, &KItemModelBase::itemsMoved, + this, &KItemListView::slotItemsMoved); + connect(m_model, &KItemModelBase::groupsChanged, + this, &KItemListView::slotGroupsChanged); + connect(m_model, &KItemModelBase::groupedSortingChanged, + this, &KItemListView::slotGroupedSortingChanged); + connect(m_model, &KItemModelBase::sortOrderChanged, + this, &KItemListView::slotSortOrderChanged); + connect(m_model, &KItemModelBase::sortRoleChanged, + this, &KItemListView::slotSortRoleChanged); const int itemCount = m_model->count(); if (itemCount > 0) { @@ -1578,10 +1732,6 @@ KItemListRubberBand* KItemListView::rubberBand() const void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int changedCount) { - if (m_layoutTimer->isActive()) { - m_layoutTimer->stop(); - } - if (m_activeTransactions > 0) { if (hint == NoAnimation) { // As soon as at least one property change should be done without animation, @@ -1625,13 +1775,11 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha const bool animate = (hint == Animation); for (int i = firstVisibleIndex; i <= lastVisibleIndex; ++i) { bool applyNewPos = true; - bool wasHidden = false; const QRectF itemBounds = m_layouter->itemRect(i); const QPointF newPos = itemBounds.topLeft(); KItemListWidget* widget = m_visibleItems.value(i); if (!widget) { - wasHidden = true; if (!reusableItems.isEmpty()) { // Reuse a KItemListWidget instance from an invisible item const int oldIndex = reusableItems.takeLast(); @@ -1698,9 +1846,6 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha // prevents a "move animation mess" when inserting several ranges in parallel. applyNewPos = !moveWidget(widget, newPos); } - } else if (!itemsRemoved && !itemsInserted && !wasHidden) { - // The size of the view might have been changed. Animate the moving of the position. - applyNewPos = !moveWidget(widget, newPos); } } else { m_animation->stop(widget); @@ -1731,6 +1876,15 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha } } + const int newIconSize = widget->styleOption().iconSize; + if (widget->iconSize() != newIconSize) { + if (animate) { + m_animation->start(widget, KItemListViewAnimation::IconResizeAnimation, newIconSize); + } else { + widget->setIconSize(newIconSize); + } + } + // Updating the cell-information must be done as last step: The decision whether the // moving-animation should be started at all is based on the previous cell-information. const Cell cell(m_layouter->itemColumn(i), m_layouter->itemRow(i)); @@ -1738,7 +1892,7 @@ void KItemListView::doLayout(LayoutAnimationHint hint, int changedIndex, int cha } // Delete invisible KItemListWidget instances that have not been reused - foreach (int index, reusableItems) { + for (int index : qAsConst(reusableItems)) { recycleWidget(m_visibleItems.value(index)); } @@ -1841,25 +1995,25 @@ void KItemListView::emitOffsetChanges() { const qreal newScrollOffset = m_layouter->scrollOffset(); if (m_oldScrollOffset != newScrollOffset) { - emit scrollOffsetChanged(newScrollOffset, m_oldScrollOffset); + Q_EMIT scrollOffsetChanged(newScrollOffset, m_oldScrollOffset); m_oldScrollOffset = newScrollOffset; } const qreal newMaximumScrollOffset = m_layouter->maximumScrollOffset(); if (m_oldMaximumScrollOffset != newMaximumScrollOffset) { - emit maximumScrollOffsetChanged(newMaximumScrollOffset, m_oldMaximumScrollOffset); + Q_EMIT maximumScrollOffsetChanged(newMaximumScrollOffset, m_oldMaximumScrollOffset); m_oldMaximumScrollOffset = newMaximumScrollOffset; } const qreal newItemOffset = m_layouter->itemOffset(); if (m_oldItemOffset != newItemOffset) { - emit itemOffsetChanged(newItemOffset, m_oldItemOffset); + Q_EMIT itemOffsetChanged(newItemOffset, m_oldItemOffset); m_oldItemOffset = newItemOffset; } const qreal newMaximumItemOffset = m_layouter->maximumItemOffset(); if (m_oldMaximumItemOffset != newMaximumItemOffset) { - emit maximumItemOffsetChanged(newMaximumItemOffset, m_oldMaximumItemOffset); + Q_EMIT maximumItemOffsetChanged(newMaximumItemOffset, m_oldMaximumItemOffset); m_oldMaximumItemOffset = newMaximumItemOffset; } } @@ -1933,7 +2087,12 @@ void KItemListView::updateWidgetProperties(KItemListWidget* widget, int index) widget->setStyleOption(m_styleOption); const KItemListSelectionManager* selectionManager = m_controller->selectionManager(); - widget->setCurrent(index == selectionManager->currentItem()); + + // 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) { + widget->setCurrent(index == selectionManager->currentItem()); + } widget->setSelected(selectionManager->isSelected(index)); widget->setHovered(false); widget->setEnabledSelectionToggle(enabledSelectionToggles()); @@ -1969,7 +2128,7 @@ void KItemListView::updateGroupHeaderForWidget(KItemListWidget* widget) groupHeader = groupHeaderCreator()->create(this); groupHeader->setParentItem(widget); m_visibleGroups.insert(widget, groupHeader); - connect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); + connect(widget, &KItemListWidget::geometryChanged, this, &KItemListView::slotGeometryOfGroupHeaderParentChanged); } Q_ASSERT(groupHeader->parentItem() == widget); @@ -2013,10 +2172,10 @@ void KItemListView::recycleGroupHeaderForWidget(KItemListWidget* widget) { KItemListGroupHeader* header = m_visibleGroups.value(widget); if (header) { - header->setParentItem(0); + header->setParentItem(nullptr); groupHeaderCreator()->recycle(header); m_visibleGroups.remove(widget); - disconnect(widget, SIGNAL(geometryChanged()), this, SLOT(slotGeometryOfGroupHeaderParentChanged())); + disconnect(widget, &KItemListWidget::geometryChanged, this, &KItemListView::slotGeometryOfGroupHeaderParentChanged); } } @@ -2092,7 +2251,7 @@ void KItemListView::updateAlternateBackgroundForWidget(KItemListWidget* widget) bool KItemListView::useAlternateBackgrounds() const { - return m_itemSize.isEmpty() && m_visibleRoles.count() > 1; + return m_alternateBackgrounds && m_itemSize.isEmpty(); } QHash KItemListView::preferredColumnWidths(const KItemRangeList& itemRanges) const @@ -2107,23 +2266,23 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi const QFontMetricsF fontMetrics(m_headerWidget->font()); const int gripMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderGripMargin); const int headerMargin = m_headerWidget->style()->pixelMetric(QStyle::PM_HeaderMargin); - foreach (const QByteArray& visibleRole, visibleRoles()) { + for (const QByteArray& visibleRole : qAsConst(m_visibleRoles)) { const QString headerText = m_model->roleDescription(visibleRole); - const qreal headerWidth = fontMetrics.width(headerText) + gripMargin + headerMargin * 2; + const qreal headerWidth = fontMetrics.horizontalAdvance(headerText) + gripMargin + headerMargin * 2; widths.insert(visibleRole, headerWidth); } - // Calculate the preferred column withs for each item and ignore values + // Calculate the preferred column widths for each item and ignore values // smaller than the width for showing the headline unclipped. const KItemListWidgetCreatorBase* creator = widgetCreator(); int calculatedItemCount = 0; bool maxTimeExceeded = false; - foreach (const KItemRange& itemRange, itemRanges) { + for (const KItemRange& itemRange : itemRanges) { const int startIndex = itemRange.index; const int endIndex = startIndex + itemRange.count - 1; for (int i = startIndex; i <= endIndex; ++i) { - foreach (const QByteArray& visibleRole, visibleRoles()) { + for (const QByteArray& visibleRole : qAsConst(m_visibleRoles)) { qreal maxWidth = widths.value(visibleRole, 0); const qreal width = creator->preferredRoleColumnWidth(visibleRole, i, this); maxWidth = qMax(width, maxWidth); @@ -2150,7 +2309,7 @@ QHash KItemListView::preferredColumnWidths(const KItemRangeLi void KItemListView::applyColumnWidthsFromHeader() { // Apply the new size to the layouter - const qreal requiredWidth = columnWidthsSum(); + const qreal requiredWidth = columnWidthsSum() + m_headerWidget->leadingPadding(); const QSizeF dynamicItemSize(qMax(size().width(), requiredWidth), m_itemSize.height()); m_layouter->setItemSize(dynamicItemSize); @@ -2165,9 +2324,10 @@ void KItemListView::applyColumnWidthsFromHeader() void KItemListView::updateWidgetColumnWidths(KItemListWidget* widget) { - foreach (const QByteArray& role, m_visibleRoles) { + for (const QByteArray& role : qAsConst(m_visibleRoles)) { widget->setColumnWidth(role, m_headerWidget->columnWidth(role)); } + widget->setLeadingPadding(m_headerWidget->leadingPadding()); } void KItemListView::updatePreferredColumnWidths(const KItemRangeList& itemRanges) @@ -2175,13 +2335,13 @@ void KItemListView::updatePreferredColumnWidths(const KItemRangeList& itemRanges Q_ASSERT(m_itemSize.isEmpty()); const int itemCount = m_model->count(); int rangesItemCount = 0; - foreach (const KItemRange& range, itemRanges) { + for (const KItemRange& range : itemRanges) { rangesItemCount += range.count; } if (itemCount == rangesItemCount) { const QHash preferredWidths = preferredColumnWidths(itemRanges); - foreach (const QByteArray& role, m_visibleRoles) { + for (const QByteArray& role : qAsConst(m_visibleRoles)) { m_headerWidget->setPreferredColumnWidth(role, preferredWidths.value(role)); } } else { @@ -2236,7 +2396,7 @@ void KItemListView::applyAutomaticColumnWidths() // size does not use the available view-size the size of the // first role will get stretched. - foreach (const QByteArray& role, m_visibleRoles) { + for (const QByteArray& role : qAsConst(m_visibleRoles)) { const qreal preferredWidth = m_headerWidget->preferredColumnWidth(role); m_headerWidget->setColumnWidth(role, preferredWidth); } @@ -2245,7 +2405,9 @@ void KItemListView::applyAutomaticColumnWidths() qreal firstColumnWidth = m_headerWidget->columnWidth(firstRole); QSizeF dynamicItemSize = m_itemSize; - qreal requiredWidth = columnWidthsSum(); + qreal requiredWidth = columnWidthsSum() + m_headerWidget->leadingPadding() + + m_headerWidget->leadingPadding(); // 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. const qreal availableWidth = size().width(); if (requiredWidth < availableWidth) { // Stretch the first column to use the whole remaining width @@ -2283,7 +2445,7 @@ void KItemListView::applyAutomaticColumnWidths() qreal KItemListView::columnWidthsSum() const { qreal widthsSum = 0; - foreach (const QByteArray& role, m_visibleRoles) { + for (const QByteArray& role : qAsConst(m_visibleRoles)) { widthsSum += m_headerWidget->columnWidth(role); } return widthsSum; @@ -2377,7 +2539,7 @@ int KItemListView::showDropIndicator(const QPointF& pos) if (mappedPos.y() >= 0 && mappedPos.y() <= rect.height()) { if (m_model->supportsDropping(widget->index())) { // Keep 30% of the rectangle as the gap instead of always having a fixed gap - const int gap = qMax(4.0, 0.3 * rect.height()); + const int gap = qMax(qreal(4.0), qreal(0.3) * rect.height()); if (mappedPos.y() >= gap && mappedPos.y() <= rect.height() - gap) { return -1; } @@ -2474,7 +2636,7 @@ void KItemListView::updateSiblingsInformation(int firstIndex, int lastIndex) const int parents = m_model->expandedParentsCount(lastIndex + 1); for (int i = lastIndex; i >= firstIndex; --i) { if (m_model->expandedParentsCount(i) != parents) { - widget = 0; + widget = nullptr; break; } } @@ -2565,13 +2727,14 @@ bool KItemListView::hasSiblingSuccessor(int index) const void KItemListView::disconnectRoleEditingSignals(int index) { - KItemListWidget* widget = m_visibleItems.value(index); + KStandardItemListWidget* widget = qobject_cast(m_visibleItems.value(index)); if (!widget) { return; } - widget->disconnect(SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)), this); - widget->disconnect(SIGNAL(roleEditingFinished(int,QByteArray,QVariant)), this); + disconnect(widget, &KItemListWidget::roleEditingCanceled, this, nullptr); + disconnect(widget, &KItemListWidget::roleEditingFinished, this, nullptr); + disconnect(this, &KItemListView::scrollOffsetChanged, widget, nullptr); } int KItemListView::calculateAutoScrollingIncrement(int pos, int range, int oldInc) @@ -2636,7 +2799,7 @@ void KItemListCreatorBase::pushRecycleableWidget(QGraphicsWidget* widget) QGraphicsWidget* KItemListCreatorBase::popRecycleableWidget() { if (m_recycleableWidgets.isEmpty()) { - return 0; + return nullptr; } QGraphicsWidget* widget = m_recycleableWidgets.takeLast(); @@ -2650,7 +2813,7 @@ KItemListWidgetCreatorBase::~KItemListWidgetCreatorBase() void KItemListWidgetCreatorBase::recycle(KItemListWidget* widget) { - widget->setParentItem(0); + widget->setParentItem(nullptr); widget->setOpacity(1.0); pushRecycleableWidget(widget); } @@ -2665,4 +2828,3 @@ void KItemListGroupHeaderCreatorBase::recycle(KItemListGroupHeader* header) pushRecycleableWidget(header); } -#include "kitemlistview.moc"