]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kitemlistview.cpp
[kitemlistview]: Animate rubberband fading out
[dolphin.git] / src / kitemviews / kitemlistview.cpp
index ea1ec3f3a5f09054389724e6bb4887da1085535d..96c337de39b02c873e9ed5335f396e5ec88d8367 100644 (file)
@@ -1,50 +1,34 @@
-/***************************************************************************
- *   Copyright (C) 2011 by Peter Penz <peter.penz19@gmail.com>             *
- *                                                                         *
- *   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 <peter.penz19@gmail.com>
+ *
+ * Based on the Itemviews NG project from Trolltech Labs
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
 
 #include "kitemlistview.h"
 
-#include <KDebug>
+#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 <QElapsedTimer>
 #include <QGraphicsSceneMouseEvent>
 #include <QGraphicsView>
-#include <QPainter>
-#include <QStyle>
+#include <QPropertyAnimation>
 #include <QStyleOptionRubberBand>
 #include <QTimer>
+#include <QVariantAnimation>
 
-#include <algorithm>
-
-#include "kitemlistviewaccessible.h"
 
 namespace {
     // Time in ms until reaching the autoscroll margin triggers
@@ -53,6 +37,11 @@ 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
@@ -66,7 +55,7 @@ QAccessibleInterface* accessibleInterfaceFactory(const QString& key, QObject* ob
         return new KItemListViewAccessible(view);
     }
 
-    return 0;
+    return nullptr;
 }
 #endif
 
@@ -79,33 +68,36 @@ 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_layoutTimer(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);
 
@@ -123,6 +115,23 @@ KItemListView::KItemListView(QGraphicsWidget* parent) :
     m_rubberBand = new KItemListRubberBand(this);
     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);
 
@@ -140,13 +149,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)
@@ -227,7 +236,7 @@ void KItemListView::setVisibleRoles(const QList<QByteArray>& 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);
@@ -269,13 +278,13 @@ void KItemListView::setAutoScroll(bool enabled)
         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)
@@ -308,9 +317,7 @@ KItemModelBase* KItemListView::model() const
 
 void KItemListView::setWidgetCreator(KItemListWidgetCreatorBase* widgetCreator)
 {
-    if (m_widgetCreator) {
-        delete m_widgetCreator;
-    }
+    delete m_widgetCreator;
     m_widgetCreator = widgetCreator;
 }
 
@@ -324,9 +331,7 @@ KItemListWidgetCreatorBase* KItemListView::widgetCreator() const
 
 void KItemListView::setGroupHeaderCreator(KItemListGroupHeaderCreatorBase* groupHeaderCreator)
 {
-    if (m_groupHeaderCreator) {
-        delete m_groupHeaderCreator;
-    }
+    delete m_groupHeaderCreator;
     m_groupHeaderCreator = groupHeaderCreator;
 }
 
@@ -447,6 +452,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();
@@ -524,7 +542,7 @@ void KItemListView::scrollToItem(int index)
         }
 
         if (newOffset != scrollOffset()) {
-            emit scrollTo(newOffset);
+            Q_EMIT scrollTo(newOffset);
         }
     }
 }
@@ -542,7 +560,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) {
@@ -614,7 +632,7 @@ QPixmap KItemListView::createDragPixmap(const KItemSet& indexes) const
         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
@@ -627,7 +645,7 @@ QPixmap KItemListView::createDragPixmap(const KItemSet& indexes) const
 
 void KItemListView::editRole(int index, const QByteArray& role)
 {
-    KItemListWidget* widget = m_visibleItems.value(index);
+    KStandardItemListWidget* widget = qobject_cast<KStandardItemListWidget *>(m_visibleItems.value(index));
     if (!widget || m_editingRole) {
         return;
     }
@@ -639,12 +657,39 @@ void KItemListView::editRole(int index, const QByteArray& role)
             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();
@@ -657,7 +702,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();
@@ -667,7 +724,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
@@ -739,6 +796,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;
 
@@ -806,7 +867,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
@@ -816,70 +877,70 @@ 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<QByteArray>& 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<QByteArray>& current, const QList<QByteArray>& 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::onSupportsItemExpandingChanged(bool supportsExpanding)
 {
-    Q_UNUSED(supportsExpanding);
+    Q_UNUSED(supportsExpanding)
 }
 
 void KItemListView::onTransactionBegin()
@@ -998,14 +1059,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;
@@ -1024,7 +1085,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);
@@ -1113,18 +1174,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<int> 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;
@@ -1160,7 +1224,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;
@@ -1238,7 +1302,7 @@ 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;
 
@@ -1312,8 +1376,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);
@@ -1322,8 +1386,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);
@@ -1332,17 +1396,17 @@ void KItemListView::slotSortRoleChanged(const QByteArray& current, const QByteAr
 
 void KItemListView::slotCurrentChanged(int current, int previous)
 {
-    Q_UNUSED(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) {
-        KItemListWidget* previousWidget = m_visibleItems.value(previous, 0);
+        KItemListWidget* previousWidget = m_visibleItems.value(previous, nullptr);
         if (previousWidget) {
             previousWidget->setCurrent(false);
         }
 
-        KItemListWidget* currentWidget = m_visibleItems.value(current, 0);
+        KItemListWidget* currentWidget = m_visibleItems.value(current, nullptr);
         if (currentWidget) {
             currentWidget->setCurrent(true);
         }
@@ -1355,7 +1419,7 @@ void KItemListView::slotCurrentChanged(int current, int previous)
 
 void KItemListView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous)
 {
-    Q_UNUSED(previous);
+    Q_UNUSED(previous)
 
     QHashIterator<int, KItemListWidget*> it(m_visibleItems);
     while (it.hasNext()) {
@@ -1421,6 +1485,30 @@ void KItemListView::slotRubberBandActivationChanged(bool active)
         connect(m_rubberBand, &KItemListRubberBand::endPositionChanged, this, &KItemListView::slotRubberBandPosChanged);
         m_skipAutoScrollForRubberBand = true;
     } else {
+        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;
@@ -1433,9 +1521,9 @@ 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();
@@ -1456,7 +1544,7 @@ void KItemListView::slotHeaderColumnMoved(const QByteArray& role,
 
     setVisibleRoles(current);
 
-    emit visibleRolesChanged(current, previous);
+    Q_EMIT visibleRolesChanged(current, previous);
 }
 
 void KItemListView::triggerAutoScrolling()
@@ -1531,7 +1619,7 @@ void KItemListView::slotRoleEditingCanceled(int index, const QByteArray& role, c
 {
     disconnectRoleEditingSignals(index);
 
-    emit roleEditingCanceled(index, role, value);
+    Q_EMIT roleEditingCanceled(index, role, value);
     m_editingRole = false;
 }
 
@@ -1539,7 +1627,7 @@ void KItemListView::slotRoleEditingFinished(int index, const QByteArray& role, c
 {
     disconnectRoleEditingSignals(index);
 
-    emit roleEditingFinished(index, role, value);
+    Q_EMIT roleEditingFinished(index, role, value);
     m_editingRole = false;
 }
 
@@ -1792,7 +1880,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));
     }
 
@@ -1895,25 +1983,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;
     }
 }
@@ -2072,7 +2160,7 @@ 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, &KItemListWidget::geometryChanged, this, &KItemListView::slotGeometryOfGroupHeaderParentChanged);
@@ -2166,7 +2254,7 @@ QHash<QByteArray, qreal> 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;
         widths.insert(visibleRole, headerWidth);
@@ -2177,12 +2265,12 @@ QHash<QByteArray, qreal> KItemListView::preferredColumnWidths(const KItemRangeLi
     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);
@@ -2224,7 +2312,7 @@ 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));
     }
 }
@@ -2234,13 +2322,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<QByteArray, qreal> preferredWidths = preferredColumnWidths(itemRanges);
-        foreach (const QByteArray& role, m_visibleRoles) {
+        for (const QByteArray& role : qAsConst(m_visibleRoles)) {
             m_headerWidget->setPreferredColumnWidth(role, preferredWidths.value(role));
         }
     } else {
@@ -2295,7 +2383,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);
     }
@@ -2342,7 +2430,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;
@@ -2436,7 +2524,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;
                 }
@@ -2483,7 +2571,7 @@ void KItemListView::updateGroupHeaderHeight()
         groupHeaderHeight += 2 * m_styleOption.horizontalMargin;
         groupHeaderMargin = m_styleOption.horizontalMargin;
     } else if (m_itemSize.isEmpty()){
-        groupHeaderHeight += 4 * m_styleOption.padding;
+        groupHeaderHeight += 2 * m_styleOption.padding;
         groupHeaderMargin = m_styleOption.iconSize / 2;
     } else {
         groupHeaderHeight += 2 * m_styleOption.padding + m_styleOption.verticalMargin;
@@ -2533,7 +2621,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;
                 }
             }
@@ -2624,13 +2712,14 @@ bool KItemListView::hasSiblingSuccessor(int index) const
 
 void KItemListView::disconnectRoleEditingSignals(int index)
 {
-    KItemListWidget* widget = m_visibleItems.value(index);
+    KStandardItemListWidget* widget = qobject_cast<KStandardItemListWidget *>(m_visibleItems.value(index));
     if (!widget) {
         return;
     }
 
     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)
@@ -2695,7 +2784,7 @@ void KItemListCreatorBase::pushRecycleableWidget(QGraphicsWidget* widget)
 QGraphicsWidget* KItemListCreatorBase::popRecycleableWidget()
 {
     if (m_recycleableWidgets.isEmpty()) {
-        return 0;
+        return nullptr;
     }
 
     QGraphicsWidget* widget = m_recycleableWidgets.takeLast();
@@ -2709,7 +2798,7 @@ KItemListWidgetCreatorBase::~KItemListWidgetCreatorBase()
 
 void KItemListWidgetCreatorBase::recycle(KItemListWidget* widget)
 {
-    widget->setParentItem(0);
+    widget->setParentItem(nullptr);
     widget->setOpacity(1.0);
     pushRecycleableWidget(widget);
 }