diff --git a/include/DWidget/DBounceAnimation b/include/DWidget/DBounceAnimation new file mode 100644 index 000000000..b088a7338 --- /dev/null +++ b/include/DWidget/DBounceAnimation @@ -0,0 +1 @@ +#include "dbounceanimation.h" diff --git a/include/widgets/dbounceanimation.h b/include/widgets/dbounceanimation.h new file mode 100644 index 000000000..b5ff98fa5 --- /dev/null +++ b/include/widgets/dbounceanimation.h @@ -0,0 +1,31 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef DBOUNCEANIMATION_H +#define DBOUNCEANIMATION_H + +#include +#include + +class QPropertyAnimation; +class QAbstractScrollArea; +class DBounceAnimationPrivate; +class DBounceAnimation : public QObject, public DTK_CORE_NAMESPACE::DObject +{ + Q_OBJECT +public: + explicit DBounceAnimation(QObject *parent = nullptr); + + void setAnimationTarget(QAbstractScrollArea *w); + void setAniMationEnable(bool enable); + +protected: + bool eventFilter(QObject *o, QEvent *e) override; + void bounceBack(Qt::Orientation orientation); + +private: + D_DECLARE_PRIVATE(DBounceAnimation) + +}; + +#endif // DBOUNCEANIMATION_H diff --git a/src/widgets/dbounceanimation.cpp b/src/widgets/dbounceanimation.cpp new file mode 100644 index 000000000..d574375a7 --- /dev/null +++ b/src/widgets/dbounceanimation.cpp @@ -0,0 +1,107 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#include "private/dbounceanimation_p.h" +#include +#include +#include +#include +#include +#include +#include + +DBounceAnimationPrivate::DBounceAnimationPrivate(DBounceAnimation *qq) + : DObjectPrivate (qq) + , m_animation(nullptr) + , m_animationTarget(nullptr) + , m_deltaSum(0) +{ +} + +DBounceAnimation::DBounceAnimation(QObject *parent) + : QObject(parent) + , DObject(*new DBounceAnimationPrivate(this)) +{ +} + +void DBounceAnimation::setAnimationTarget(QAbstractScrollArea *w) +{ + D_D(DBounceAnimation); + if (!w) + return; + + if (d->m_animationTarget == w) + return; + + d->m_animationTarget = w; +} + +void DBounceAnimation::setAniMationEnable(bool enable) +{ + D_D(DBounceAnimation); + enable ? d->m_animationTarget->installEventFilter(this) + : d->m_animationTarget->removeEventFilter(this); +} + +bool DBounceAnimation::eventFilter(QObject *o, QEvent *e) +{ + D_D(DBounceAnimation); + if (e->type() == QEvent::Wheel) { + if (auto absscroll = dynamic_cast(o)) { + if (auto wheelEvent = dynamic_cast(e)) { + if (absscroll->verticalScrollBar()->value() <= 0 || absscroll->verticalScrollBar()->value() >= absscroll->verticalScrollBar()->maximum()) { + d->m_deltaSum += wheelEvent->delta(); + bounceBack(wheelEvent->angleDelta().x() == 0 ? Qt::Vertical : Qt::Horizontal); + } + } + } + } + + return false; +} + +void DBounceAnimation::bounceBack(Qt::Orientation orientation) +{ + D_D(DBounceAnimation); + if (d->m_animation) + return; + + if (orientation & Qt::Vertical && d->m_animationTarget->verticalScrollBar()->maximum() == d->m_animationTarget->verticalScrollBar()->minimum()) + return; + + if (orientation & Qt::Horizontal && d->m_animationTarget->horizontalScrollBar()->maximum() == d->m_animationTarget->horizontalScrollBar()->minimum()) + return; + + d->m_animation = new QPropertyAnimation(this); + d->m_animation->setTargetObject(d->m_animationTarget->viewport()); + d->m_animation->setPropertyName("pos"); + d->m_animation->setDuration(100); + d->m_animation->setEasingCurve(QEasingCurve::InQuart); + d->m_animation->setStartValue(QPoint(d->m_animationTarget->viewport()->x(), d->m_animationTarget->viewport()->y())); + + QTimer::singleShot(100, this, [this, d, orientation]() { + + if (orientation & Qt::Vertical) { + d->m_animation->setEndValue( + QPoint(d->m_animationTarget->viewport()->x(), d->m_animationTarget->viewport()->y() + d->m_deltaSum / 16)); + } else { + d->m_animation->setEndValue( + QPoint(d->m_animationTarget->viewport()->x() + d->m_deltaSum / 16, d->m_animationTarget->viewport()->y())); + } + + d->m_animation->start(); + + connect(d->m_animation, &QPropertyAnimation::finished, this, [d]() { + if (d->m_animation->direction() == QPropertyAnimation::Backward) { + delete d->m_animation; + d->m_animation = nullptr; + return; + } + + d->m_animation->setDirection(QPropertyAnimation::Direction::Backward); + d->m_animation->setDuration(1000); + d->m_animation->start(QPropertyAnimation::DeleteWhenStopped); + d->m_deltaSum = 0; + }); + }); +} diff --git a/src/widgets/dlistview.cpp b/src/widgets/dlistview.cpp index 16672ba05..8b7bff3d6 100644 --- a/src/widgets/dlistview.cpp +++ b/src/widgets/dlistview.cpp @@ -13,6 +13,8 @@ #include "dstyleditemdelegate.h" #include "dstyle.h" +#include + DWIDGET_BEGIN_NAMESPACE DVariantListModel::DVariantListModel(QObject *parent) : @@ -196,6 +198,11 @@ DListView::DListView(QWidget *parent) : DObject(*new DListViewPrivate(this)) { d_func()->init(); + if (!qEnvironmentVariableIsSet("DTK_DISABLE_LISTVIEW_ANIMATION")) { + auto ani = new DBounceAnimation(this); + ani->setAnimationTarget(this); + ani->setAniMationEnable(true); + } } /*! diff --git a/src/widgets/private/dbounceanimation_p.h b/src/widgets/private/dbounceanimation_p.h new file mode 100644 index 000000000..09e2d5b3b --- /dev/null +++ b/src/widgets/private/dbounceanimation_p.h @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later +#ifndef DBOUNCEANIMATION_P_H +#define DBOUNCEANIMATION_P_H + +#include "dbounceanimation.h" +#include + +class DBounceAnimationPrivate : public DTK_CORE_NAMESPACE::DObjectPrivate +{ +public: + DBounceAnimationPrivate(DBounceAnimation *qq); + + QPropertyAnimation *m_animation; + QAbstractScrollArea *m_animationTarget; + int m_deltaSum; + +private: + D_DECLARE_PUBLIC(DBounceAnimation) +}; + +#endif // DBOUNCEANIMATION_P_H