mirror of
https://github.com/Linloir/SceneEditor.git
synced 2025-12-16 23:18:12 +08:00
[UI][ADD] Scroll List widget
- transplanted from previous work
This commit is contained in:
parent
be3eda82f2
commit
0e1edcc588
@ -1 +0,0 @@
|
||||
#pragma once
|
||||
422
FinalProject/scrolllistwidget.cpp
Normal file
422
FinalProject/scrolllistwidget.cpp
Normal file
@ -0,0 +1,422 @@
|
||||
#include "scrolllistwidget.h"
|
||||
|
||||
ScrollListWidget::ScrollListWidget(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
//initialize list container and timer
|
||||
container = new ScrollListContainer(this);
|
||||
container->move(0, 0);
|
||||
container->resize(this->width(), 3);
|
||||
getCord = new QTimer;
|
||||
getCord->setSingleShot(true);
|
||||
rfrshView = new QTimer;
|
||||
getCord->setSingleShot(true);
|
||||
|
||||
indicator = new ScrollListIndicator(this);
|
||||
indicator->resize(indicator->width(), (int)((double)this->height() * this->height() / (double)container->height()));
|
||||
indicator->move(this->width() - indicator->width() - 3, 0);
|
||||
|
||||
this->setMouseTracking(true);
|
||||
container->setMouseTracking(true);
|
||||
indicator->setMouseTracking(true);
|
||||
|
||||
bounce = new QPropertyAnimation(container, "pos");
|
||||
|
||||
QObject::connect(getCord, SIGNAL(timeout()), this, SLOT(updateSpd()));
|
||||
QObject::connect(rfrshView, SIGNAL(timeout()), this, SLOT(scrollContainer()));
|
||||
QObject::connect(indicator, SIGNAL(scrollPage(int)), this, SLOT(scrollIndicator(int)));
|
||||
}
|
||||
|
||||
void ScrollListWidget::paintEvent(QPaintEvent* event) {
|
||||
container->resize(this->width(), container->height());
|
||||
if (container->height() > this->height() && container->y() < this->height() - container->height() && curSpd == 0 && bounce->state() == QAbstractAnimation::Stopped)
|
||||
container->move(container->x(), this->height() - container->height());
|
||||
if (container->height() <= this->height()) {
|
||||
container->move(container->x(), 0);
|
||||
indicator->hide();
|
||||
}
|
||||
else {
|
||||
indicator->show();
|
||||
}
|
||||
indicator->resize(indicator->width(), (int)((double)this->height() * this->height() / (double)container->height()));
|
||||
indicator->move(this->width() - indicator->width() - 3, -container->y() * this->height() / container->height());
|
||||
}
|
||||
|
||||
void ScrollListWidget::mousePressEvent(QMouseEvent* event) {
|
||||
if (container->height() > this->height()) {
|
||||
if (container->y() <= 0 && container->y() + container->height() >= this->height())
|
||||
pressed = true;
|
||||
lastY = event->pos().y();
|
||||
}
|
||||
getCord->stop();
|
||||
rfrshView->stop();
|
||||
curSpd = 0;
|
||||
outOfEdge = false;
|
||||
moveStored = 0;
|
||||
nextMove = 1;
|
||||
}
|
||||
|
||||
void ScrollListWidget::mouseMoveEvent(QMouseEvent* event) {
|
||||
setCursor(Qt::ArrowCursor);
|
||||
if (pressed) {
|
||||
//start scroll
|
||||
if (!getCord->isActive() && event->pos().y() - lastY != 0) {
|
||||
//start 30ms timer
|
||||
getCord->start(30);
|
||||
strtY = event->pos().y();
|
||||
}
|
||||
if (container->y() <= 0 && container->y() + container->height() >= this->height())
|
||||
container->move(container->x(), container->y() + event->pos().y() - lastY);
|
||||
else {
|
||||
if (!outOfEdge) {
|
||||
bfEdgeY = event->pos().y();
|
||||
container->move(container->x(), container->y() + event->pos().y() - lastY);
|
||||
outOfEdge = true;
|
||||
}
|
||||
else {
|
||||
int pos = container->y() >= 0 ? 1 : -1;
|
||||
int dp = event->pos().y() - bfEdgeY;
|
||||
if (dp == 0) {
|
||||
outOfEdge = false;
|
||||
nextMove = 1;
|
||||
moveStored = 0;
|
||||
if (container->y() >= 0)
|
||||
container->move(container->x(), 0);
|
||||
else
|
||||
container->move(container->x(), this->height() - container->height());
|
||||
}
|
||||
else if (dp / abs(dp) != pos) {
|
||||
outOfEdge = false;
|
||||
container->move(container->x(), this->y() + event->pos().y() - bfEdgeY);
|
||||
nextMove = 1;
|
||||
moveStored = 0;
|
||||
}
|
||||
else {
|
||||
while (abs(moveStored) + nextMove <= abs(event->pos().y() - bfEdgeY)) {
|
||||
moveStored += nextMove * pos;
|
||||
container->move(container->x(), container->y() + pos);
|
||||
nextMove++;
|
||||
}
|
||||
while (nextMove > 1 && abs(moveStored) > abs(event->pos().y() - bfEdgeY)) {
|
||||
nextMove--;
|
||||
moveStored -= nextMove * pos;
|
||||
container->move(container->x(), container->y() - pos);
|
||||
}
|
||||
if (moveStored == 0) {
|
||||
outOfEdge = false;
|
||||
if (container->y() >= 0)
|
||||
container->move(container->x(), 0);
|
||||
else
|
||||
container->move(container->x(), this->height() - container->height());
|
||||
nextMove = 1;
|
||||
moveStored = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lastY = event->pos().y();
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollListWidget::mouseReleaseEvent(QMouseEvent* event) {
|
||||
//start scrolling
|
||||
if (container->y() > 0 || container->y() + container->height() < this->height())
|
||||
bounceBack();
|
||||
else
|
||||
rfrshView->start(30);
|
||||
pressed = false;
|
||||
}
|
||||
|
||||
void ScrollListWidget::bounceBack() {
|
||||
rfrshView->stop();
|
||||
getCord->stop();
|
||||
bounce->setDuration(500);
|
||||
bounce->setStartValue(container->pos());
|
||||
if (container->y() > 0)
|
||||
bounce->setEndValue(QPoint(container->x(), 0));
|
||||
else
|
||||
bounce->setEndValue(QPoint(container->x(), this->height() - container->height()));
|
||||
bounce->setEasingCurve(QEasingCurve::OutQuad);
|
||||
bounce->start();
|
||||
}
|
||||
|
||||
void ScrollListWidget::scrollContainer() {
|
||||
//scroll
|
||||
if (curSpd > 0) {
|
||||
if (curSpd > MAXSPEED && !ignoreMaxSpeed)
|
||||
curSpd = MAXSPEED;
|
||||
else if (curSpd <= MAXSPEED) ignoreMaxSpeed = false;
|
||||
int dp = scrollDown ? curSpd : -curSpd;
|
||||
container->move(container->x(), container->y() + dp);
|
||||
}
|
||||
else
|
||||
return;
|
||||
if (container->y() <= 0 && container->y() + container->height() >= this->height()) {
|
||||
curSpd -= damp;
|
||||
curSpd = curSpd < 0 ? 0 : curSpd;
|
||||
}
|
||||
else
|
||||
curSpd /= 2;
|
||||
if (curSpd == 0 && (container->y() > 0 || container->y() + container->height() < this->height()))
|
||||
bounceBack();
|
||||
else
|
||||
rfrshView->start(30);
|
||||
}
|
||||
|
||||
void ScrollListWidget::updateSpd() {
|
||||
int spd = lastY - strtY;
|
||||
scrollDown = spd >= 0;
|
||||
strtY = lastY;
|
||||
curSpd = abs(spd);
|
||||
}
|
||||
|
||||
void ScrollListWidget::addWidget(QWidget* newWidget, bool setAnimation) {
|
||||
newWidget->setParent(container);
|
||||
container->AddWidget(newWidget, setAnimation);
|
||||
}
|
||||
|
||||
void ScrollListWidget::removeWidget(QWidget* w) {
|
||||
container->RemoveWidget(w);
|
||||
}
|
||||
|
||||
void ScrollListWidget::scrollToTop() {
|
||||
curSpd = sqrt(8 * (-container->pos().y()) + 2) / 2;
|
||||
scrollDown = true;
|
||||
getCord->stop();
|
||||
rfrshView->stop();
|
||||
outOfEdge = false;
|
||||
moveStored = 0;
|
||||
nextMove = 1;
|
||||
ignoreMaxSpeed = true;
|
||||
rfrshView->start(30);
|
||||
}
|
||||
|
||||
void ScrollListWidget::updateHeight() {
|
||||
container->updateHeight();
|
||||
}
|
||||
|
||||
void ScrollListWidget::clear() {
|
||||
container->clear();
|
||||
}
|
||||
|
||||
void ScrollListWidget::scrollIndicator(int dp) {
|
||||
int newY = container->y() - dp * container->height() / this->height();
|
||||
if (newY > 0)
|
||||
newY = 0;
|
||||
else if (newY < this->height() - container->height())
|
||||
newY = this->height() - container->height();
|
||||
container->move(container->x(), newY);
|
||||
update();
|
||||
}
|
||||
|
||||
void ScrollListWidget::wheelEvent(QWheelEvent* event) {
|
||||
if (container->y() > 0 || container->y() + container->height() < this->height())
|
||||
return;
|
||||
curSpd += 5;
|
||||
bool newDirection = event->angleDelta().y() > 0;
|
||||
if (newDirection != scrollDown)
|
||||
curSpd = 5;
|
||||
if (curSpd > MAXSPEED)
|
||||
curSpd = MAXSPEED;
|
||||
scrollDown = newDirection;
|
||||
if (!rfrshView->isActive())
|
||||
rfrshView->start(30);
|
||||
update();
|
||||
}
|
||||
|
||||
ScrollListContainer::ScrollListContainer(QWidget* parent) : QWidget(parent) {}
|
||||
|
||||
void ScrollListContainer::paintEvent(QPaintEvent* event) {
|
||||
for (int i = 0; i < widgets.size(); i++) {
|
||||
widgets[i]->resize(this->width(), widgets[i]->height());
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollListContainer::AddWidget(QWidget* widget, bool setAnimation) {
|
||||
//Add animation for all widgets current
|
||||
this->resize(this->width(), this->height() + widget->height() + spacing);
|
||||
widgets.push_back(widget);
|
||||
size++;
|
||||
ys.push_back(0);
|
||||
widget->resize(this->width(), widget->height());
|
||||
widget->show();
|
||||
|
||||
if (setAnimation) {
|
||||
QGraphicsOpacityEffect* widgetOpac = new QGraphicsOpacityEffect(widget);
|
||||
widgetOpac->setOpacity(0);
|
||||
widget->setGraphicsEffect(widgetOpac);
|
||||
QParallelAnimationGroup* dpGroup = new QParallelAnimationGroup;
|
||||
QSequentialAnimationGroup* newWidgetFadeIn = new QSequentialAnimationGroup;
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
ys[i] += widget->height() + spacing;
|
||||
QPropertyAnimation* move = new QPropertyAnimation(widgets[i], "pos");
|
||||
move->setDuration(750);
|
||||
move->setStartValue(widgets[i]->pos());
|
||||
move->setEndValue(QPoint(widgets[i]->x(), ys[i]));
|
||||
move->setEasingCurve(QEasingCurve::InOutQuart);
|
||||
dpGroup->addAnimation(move);
|
||||
}
|
||||
newWidgetFadeIn->addPause(300);
|
||||
QPropertyAnimation* fade = new QPropertyAnimation(widgetOpac, "opacity", widget);
|
||||
fade->setDuration(300);
|
||||
fade->setStartValue(0);
|
||||
fade->setEndValue(0.99);
|
||||
newWidgetFadeIn->addAnimation(fade);
|
||||
dpGroup->addAnimation(newWidgetFadeIn);
|
||||
dpGroup->start();
|
||||
connect(dpGroup, &QPropertyAnimation::stateChanged, [=]() {
|
||||
if (dpGroup->state() == QAbstractAnimation::Stopped) {
|
||||
if (widgetOpac->opacity() != 0.99) {
|
||||
fade->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
connect(fade, &QPropertyAnimation::finished, [=]() {widgetOpac->deleteLater(); });
|
||||
}
|
||||
else {
|
||||
dpGroup->deleteLater();
|
||||
widgetOpac->deleteLater();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
for (int i = 0; i < size - 1; i++) {
|
||||
ys[i] += widget->height() + spacing;
|
||||
widgets[i]->move(QPoint(widgets[i]->pos().x(), ys[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollListContainer::RemoveWidget(QWidget* widget) {
|
||||
int index;
|
||||
if (widget == nullptr) {
|
||||
index = size - 1;
|
||||
if (index != -1)
|
||||
widget = widgets[index];
|
||||
}
|
||||
else
|
||||
index = widgets.indexOf(widget);
|
||||
if (index == -1 || widget == nullptr) {
|
||||
return;
|
||||
}
|
||||
this->resize(this->width(), this->height() - widget->height() - spacing);
|
||||
this->parentWidget()->update();
|
||||
widget->hide();
|
||||
widget->setParent(nullptr);
|
||||
QParallelAnimationGroup* dpGroup = new QParallelAnimationGroup;
|
||||
for (int i = index - 1; i >= 0; i--) {
|
||||
ys[i] -= (widget->height() + spacing);
|
||||
QPropertyAnimation* move = new QPropertyAnimation(widgets[i], "pos");
|
||||
move->setDuration(750);
|
||||
move->setStartValue(widgets[i]->pos());
|
||||
move->setEndValue(QPoint(widgets[i]->x(), ys[i]));
|
||||
move->setEasingCurve(QEasingCurve::InOutQuart);
|
||||
dpGroup->addAnimation(move);
|
||||
}
|
||||
dpGroup->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
widgets.remove(index);
|
||||
size--;
|
||||
ys.remove(index);
|
||||
}
|
||||
|
||||
void ScrollListContainer::updateHeight() {
|
||||
for (int i = size - 2; i >= 0; i--) {
|
||||
ys[i] = ys[i + 1] + widgets[i + 1]->height() + spacing;
|
||||
widgets[i]->move(widgets[i]->pos().x(), ys[i]);
|
||||
}
|
||||
this->resize(this->width(), ys[0] + widgets[0]->height() + 3);
|
||||
}
|
||||
|
||||
void ScrollListContainer::clear() {
|
||||
int n = size;
|
||||
for (int i = 0; i < n; i++)
|
||||
RemoveWidget();
|
||||
}
|
||||
|
||||
ScrollListIndicator::ScrollListIndicator(QWidget* parent) : QWidget(parent)
|
||||
{
|
||||
this->resize(defaultWidth, 0);
|
||||
hovTimer = new QTimer(this);
|
||||
hovTimer->setSingleShot(true);
|
||||
aniPause = new QTimer(this);
|
||||
aniPause->setSingleShot(true);
|
||||
QObject::connect(hovTimer, SIGNAL(timeout()), this, SLOT(setHoverActive()));
|
||||
this->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
this->curColor = defaultColor;
|
||||
|
||||
this->setMouseTracking(true);
|
||||
}
|
||||
|
||||
void ScrollListIndicator::paintEvent(QPaintEvent* event) {
|
||||
QPainter painter(this);
|
||||
painter.setPen(Qt::NoPen);
|
||||
painter.setBrush(curColor);
|
||||
painter.drawRect(this->rect());
|
||||
}
|
||||
|
||||
void ScrollListIndicator::enterEvent(QEnterEvent* event) {
|
||||
if (!pressed) {
|
||||
hovTimer->start(100);
|
||||
curColor = hoverColor;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollListIndicator::leaveEvent(QEvent* event) {
|
||||
hovTimer->stop();
|
||||
curColor = defaultColor;
|
||||
QPropertyAnimation* narrow = new QPropertyAnimation(this, "geometry");
|
||||
narrow->setDuration(300);
|
||||
narrow->setStartValue(QRect(this->x(), this->y(), this->width(), this->height()));
|
||||
narrow->setEndValue(QRect(this->parentWidget()->width() - margin - defaultWidth, this->y(), defaultWidth, this->height()));
|
||||
narrow->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
narrow->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
aniPause->start(300);
|
||||
update();
|
||||
}
|
||||
|
||||
void ScrollListIndicator::mousePressEvent(QMouseEvent* event) {
|
||||
curColor = pressColor;
|
||||
pressed = true;
|
||||
//>note: globalPos -> globalPosition here due to deprecation
|
||||
//> may cause issues
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
|
||||
lastY = event->globalPosition().y();
|
||||
#else
|
||||
lastY = event->globalPos().y();
|
||||
#endif
|
||||
update();
|
||||
}
|
||||
|
||||
void ScrollListIndicator::mouseMoveEvent(QMouseEvent* event) {
|
||||
if (pressed && !aniPause->isActive()) {
|
||||
//>note: globalPos -> globalPosition here due to deprecation
|
||||
//> may cause issues
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
|
||||
int dp = event->globalPosition().y() - lastY;
|
||||
#else
|
||||
int dp = event->globalPos().y() - lastY;
|
||||
#endif
|
||||
emit scrollPage(dp);
|
||||
//>note: globalPos -> globalPosition here due to deprecation
|
||||
//> may cause issues
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6,0,0))
|
||||
lastY = event->globalPosition().y();
|
||||
#else
|
||||
lastY = event->globalPos().y();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void ScrollListIndicator::mouseReleaseEvent(QMouseEvent* event) {
|
||||
pressed = false;
|
||||
curColor = hoverColor;
|
||||
update();
|
||||
}
|
||||
|
||||
void ScrollListIndicator::setHoverActive() {
|
||||
QPropertyAnimation* widen = new QPropertyAnimation(this, "geometry");
|
||||
widen->setDuration(300);
|
||||
widen->setStartValue(QRect(this->x(), this->y(), this->width(), this->height()));
|
||||
widen->setEndValue(QRect(this->parentWidget()->width() - margin - defaultWidthAtFocus, this->y(), defaultWidthAtFocus, this->height()));
|
||||
widen->setEasingCurve(QEasingCurve::InOutQuad);
|
||||
widen->start(QAbstractAnimation::DeleteWhenStopped);
|
||||
aniPause->start(300);
|
||||
}
|
||||
142
FinalProject/scrolllistwidget.h
Normal file
142
FinalProject/scrolllistwidget.h
Normal file
@ -0,0 +1,142 @@
|
||||
/* This Module has not yet been rewritten */
|
||||
/* Behavior and performance is not guaranteed*/
|
||||
/* @Linloir */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <qwidget.h>
|
||||
#include <qboxlayout.h>
|
||||
#include <qpainter.h>
|
||||
#include <qpropertyanimation.h>
|
||||
#include <qparallelanimationgroup.h>
|
||||
#include <qsequentialanimationgroup.h>
|
||||
#include <qgraphicseffect.h>
|
||||
#include <qvector.h>
|
||||
#include <qevent.h>
|
||||
#include <qtimer.h>
|
||||
#include <qmath.h>
|
||||
|
||||
#define MAXSPEED 70
|
||||
|
||||
class ScrollListWidget;
|
||||
class ScrollListContainer;
|
||||
class ScrollListIndicator;
|
||||
|
||||
class ScrollListWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QTimer* getCord;
|
||||
QTimer* rfrshView;
|
||||
|
||||
ScrollListContainer* container;
|
||||
ScrollListIndicator* indicator;
|
||||
|
||||
QPropertyAnimation* bounce;
|
||||
|
||||
bool pressed = false;
|
||||
bool scrollDown = true;
|
||||
bool outOfEdge = false;
|
||||
bool ignoreMaxSpeed = false;
|
||||
|
||||
int strtY;
|
||||
int lastY;
|
||||
int bfEdgeY; //last y value before out of edge
|
||||
|
||||
int curSpd = 0;
|
||||
int damp = 1;
|
||||
int moveStored = 0;
|
||||
int nextMove = 1;
|
||||
|
||||
void paintEvent(QPaintEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
void wheelEvent(QWheelEvent* event);
|
||||
void bounceBack();
|
||||
|
||||
|
||||
public:
|
||||
explicit ScrollListWidget(QWidget* parent = nullptr);
|
||||
void addWidget(QWidget* newWidget, bool setAnimation = true);
|
||||
void addWidgets(QVector<QWidget*> widgets) { for (int i = 0; i < widgets.size(); i++)addWidget(widgets[i], false); }
|
||||
void removeWidget(QWidget* w = nullptr);
|
||||
void scrollToTop();
|
||||
void updateHeight();
|
||||
void clear();
|
||||
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
void scrollContainer();
|
||||
void updateSpd();
|
||||
void scrollIndicator(int dp);
|
||||
|
||||
};
|
||||
|
||||
class ScrollListContainer : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ScrollListContainer(QWidget* parent = nullptr);
|
||||
void AddWidget(QWidget* widget, bool setAnimation = true);
|
||||
void RemoveWidget(QWidget* widget = nullptr);
|
||||
void updateHeight();
|
||||
void clear();
|
||||
//void RemoveWidget(QWidget* widget);
|
||||
|
||||
private:
|
||||
//QTimer* newWidgetFade;
|
||||
int spacing = 3;
|
||||
QVector<QWidget*> widgets;
|
||||
int size = 0;
|
||||
QVector<int> ys;
|
||||
|
||||
void paintEvent(QPaintEvent* event);
|
||||
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
|
||||
};
|
||||
|
||||
class ScrollListIndicator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private:
|
||||
QColor curColor;
|
||||
QColor defaultColor = QColor(100, 100, 100, 130);
|
||||
QColor hoverColor = QColor(70, 70, 70, 150);
|
||||
QColor pressColor = QColor(50, 50, 50, 170);
|
||||
|
||||
QTimer* hovTimer;
|
||||
QTimer* aniPause;
|
||||
|
||||
int lastY;
|
||||
|
||||
int defaultWidth = 2;
|
||||
int defaultWidthAtFocus = 9;
|
||||
int margin = 3;
|
||||
|
||||
bool pressed = false;
|
||||
|
||||
public:
|
||||
explicit ScrollListIndicator(QWidget* parent = nullptr);
|
||||
|
||||
private:
|
||||
void paintEvent(QPaintEvent* event);
|
||||
void enterEvent(QEnterEvent* event);
|
||||
void leaveEvent(QEvent* event);
|
||||
void mousePressEvent(QMouseEvent* event);
|
||||
void mouseMoveEvent(QMouseEvent* event);
|
||||
void mouseReleaseEvent(QMouseEvent* event);
|
||||
|
||||
signals:
|
||||
void scrollPage(int);
|
||||
|
||||
private slots:
|
||||
void setHoverActive();
|
||||
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user