diff options
author | Jarek Kobus <jaroslaw.kobus@qt.io> | 2023-06-15 13:18:32 +0200 |
---|---|---|
committer | Jarek Kobus <jaroslaw.kobus@qt.io> | 2023-06-20 18:48:24 +0000 |
commit | 2d5a8252807d34ba59d5363e43ea20b34d4c2c13 (patch) | |
tree | cb71728f9bbb2fd5600981b9e94edbfec78d838e | |
parent | 73c20f19bb7be09b10c3d490c22bdf1084082c0a (diff) |
Solutions: Add a Spinner solution
It's a bit modified Utils::ProgressIndicator class,
used e.g. in tasking demo.
The Spinner is a QObject based class, which may be
attached to any widget instance.
Change-Id: I95fba52fe67e7eb7ee2ff3549847ce0ad97991d4
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
-rw-r--r-- | src/libs/solutions/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/libs/solutions/solutions.qbs | 1 | ||||
-rw-r--r-- | src/libs/solutions/spinner/CMakeLists.txt | 9 | ||||
-rw-r--r-- | src/libs/solutions/spinner/icons/spinner_large.png | bin | 0 -> 1346 bytes | |||
-rw-r--r-- | src/libs/solutions/spinner/icons/spinner_medium.png | bin | 0 -> 765 bytes | |||
-rw-r--r-- | src/libs/solutions/spinner/icons/spinner_small.png | bin | 0 -> 315 bytes | |||
-rw-r--r-- | src/libs/solutions/spinner/spinner.cpp | 194 | ||||
-rw-r--r-- | src/libs/solutions/spinner/spinner.h | 36 | ||||
-rw-r--r-- | src/libs/solutions/spinner/spinner.qbs | 13 | ||||
-rw-r--r-- | src/libs/solutions/spinner/spinner.qrc | 7 | ||||
-rw-r--r-- | src/libs/solutions/spinner/spinner_global.h | 14 |
11 files changed, 275 insertions, 0 deletions
diff --git a/src/libs/solutions/CMakeLists.txt b/src/libs/solutions/CMakeLists.txt index 694d940195..2a47fbee5f 100644 --- a/src/libs/solutions/CMakeLists.txt +++ b/src/libs/solutions/CMakeLists.txt @@ -1 +1,2 @@ +add_subdirectory(spinner) add_subdirectory(tasking) diff --git a/src/libs/solutions/solutions.qbs b/src/libs/solutions/solutions.qbs index 6184dce2af..3978235666 100644 --- a/src/libs/solutions/solutions.qbs +++ b/src/libs/solutions/solutions.qbs @@ -2,6 +2,7 @@ Project { name: "Solutions" references: [ + "spinner/spinner.qbs", "tasking/tasking.qbs", ].concat(project.additionalLibs) } diff --git a/src/libs/solutions/spinner/CMakeLists.txt b/src/libs/solutions/spinner/CMakeLists.txt new file mode 100644 index 0000000000..fad094ccb3 --- /dev/null +++ b/src/libs/solutions/spinner/CMakeLists.txt @@ -0,0 +1,9 @@ +add_qtc_library(Spinner OBJECT +# Never add dependencies to non-Qt libraries for this library + DEPENDS Qt::Core Qt::Widgets + PUBLIC_DEFINES SPINNER_LIBRARY + SOURCES + spinner.cpp spinner.h + spinner.qrc + spinner_global.h +) diff --git a/src/libs/solutions/spinner/icons/spinner_large.png b/src/libs/solutions/spinner/icons/spinner_large.png Binary files differnew file mode 100644 index 0000000000..c24ff1b77c --- /dev/null +++ b/src/libs/solutions/spinner/icons/spinner_large.png diff --git a/src/libs/solutions/spinner/icons/spinner_medium.png b/src/libs/solutions/spinner/icons/spinner_medium.png Binary files differnew file mode 100644 index 0000000000..d64cc514e1 --- /dev/null +++ b/src/libs/solutions/spinner/icons/spinner_medium.png diff --git a/src/libs/solutions/spinner/icons/spinner_small.png b/src/libs/solutions/spinner/icons/spinner_small.png Binary files differnew file mode 100644 index 0000000000..254e9c82fc --- /dev/null +++ b/src/libs/solutions/spinner/icons/spinner_small.png diff --git a/src/libs/solutions/spinner/spinner.cpp b/src/libs/solutions/spinner/spinner.cpp new file mode 100644 index 0000000000..20f7664b52 --- /dev/null +++ b/src/libs/solutions/spinner/spinner.cpp @@ -0,0 +1,194 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include "spinner.h" + +#include <QEvent> +#include <QPainter> +#include <QTimer> +#include <QWidget> + +namespace SpinnerSolution { + +class OverlayWidget : public QWidget +{ +public: + using PaintFunction = std::function<void(QWidget *, QPainter &, QPaintEvent *)>; + + explicit OverlayWidget(QWidget *parent = nullptr) + { + setAttribute(Qt::WA_TransparentForMouseEvents); + if (parent) + attachToWidget(parent); + } + + void attachToWidget(QWidget *parent) + { + if (parentWidget()) + parentWidget()->removeEventFilter(this); + setParent(parent); + if (parent) { + parent->installEventFilter(this); + resizeToParent(); + raise(); + } + } + void setPaintFunction(const PaintFunction &paint) { m_paint = paint; } + +protected: + bool eventFilter(QObject *obj, QEvent *ev) override + { + if (obj == parent() && ev->type() == QEvent::Resize) + resizeToParent(); + return QWidget::eventFilter(obj, ev); + } + + void paintEvent(QPaintEvent *ev) override + { + if (m_paint) { + QPainter p(this); + m_paint(this, p, ev); + } + } + +private: + void resizeToParent() { setGeometry(QRect({}, parentWidget()->size())); } + + PaintFunction m_paint; +}; + +class SpinnerPainter +{ +public: + using UpdateCallback = std::function<void()>; + + SpinnerPainter(SpinnerSize size); + + void setSize(SpinnerSize size); + + void setUpdateCallback(const UpdateCallback &cb) { m_callback = cb; } + + QSize size() const { return m_pixmap.size() / m_pixmap.devicePixelRatio(); } + void paint(QPainter &painter, const QRect &rect) const; + void startAnimation() { m_timer.start(); } + void stopAnimation() { m_timer.stop(); } + +protected: + void nextAnimationStep() { m_rotation = (m_rotation + m_rotationStep + 360) % 360; } + +private: + SpinnerSize m_size = SpinnerSize::Small; + int m_rotationStep = 45; + int m_rotation = 0; + QTimer m_timer; + QPixmap m_pixmap; + UpdateCallback m_callback; +}; + +static QString imageFileNameForSpinnerSize(SpinnerSize size) +{ + switch (size) { + case SpinnerSize::Large: + return ":/icons/spinner_large.png"; + case SpinnerSize::Medium: + return ":/icons/spinner_medium.png"; + case SpinnerSize::Small: + return ":/icons/spinner_small.png"; + } + return {}; +} + +SpinnerPainter::SpinnerPainter(SpinnerSize size) +{ + m_timer.setSingleShot(false); + QObject::connect(&m_timer, &QTimer::timeout, &m_timer, [this] { + nextAnimationStep(); + if (m_callback) + m_callback(); + }); + setSize(size); +} + +void SpinnerPainter::setSize(SpinnerSize size) +{ + m_size = size; + m_rotationStep = size == SpinnerSize::Small ? 45 : 30; + m_timer.setInterval(size == SpinnerSize::Small ? 100 : 80); + m_pixmap = QPixmap(imageFileNameForSpinnerSize(size)); +} + +void SpinnerPainter::paint(QPainter &painter, const QRect &rect) const +{ + painter.save(); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + QPoint translate(rect.x() + rect.width() / 2, rect.y() + rect.height() / 2); + QTransform t; + t.translate(translate.x(), translate.y()); + t.rotate(m_rotation); + t.translate(-translate.x(), -translate.y()); + painter.setTransform(t); + QSize pixmapUserSize(m_pixmap.size() / m_pixmap.devicePixelRatio()); + painter.drawPixmap(QPoint(rect.x() + ((rect.width() - pixmapUserSize.width()) / 2), + rect.y() + ((rect.height() - pixmapUserSize.height()) / 2)), + m_pixmap); + painter.restore(); +} + +class SpinnerWidget : public OverlayWidget +{ +public: + explicit SpinnerWidget(SpinnerSize size, QWidget *parent = nullptr) + : OverlayWidget(parent) + , m_paint(size) + { + setPaintFunction( + [this](QWidget *w, QPainter &p, QPaintEvent *) { m_paint.paint(p, w->rect()); }); + m_paint.setUpdateCallback([this] { update(); }); + updateGeometry(); + } + + void setSize(SpinnerSize size) + { + m_paint.setSize(size); + updateGeometry(); + } + QSize sizeHint() const final { return m_paint.size(); } + +protected: + void showEvent(QShowEvent *) final { m_paint.startAnimation(); } + void hideEvent(QHideEvent *) final { m_paint.stopAnimation(); } + +private: + SpinnerPainter m_paint; +}; + +Spinner::Spinner(SpinnerSize size, QWidget *parent) + : QObject(parent) + , m_widget(new SpinnerWidget(size, parent)) {} + +void Spinner::setSize(SpinnerSize size) +{ + m_widget->setSize(size); +} + +void Spinner::show() +{ + m_widget->show(); +} + +void Spinner::hide() +{ + m_widget->hide(); +} + +bool Spinner::isVisible() const +{ + return m_widget->isVisible(); +} + +void Spinner::setVisible(bool visible) +{ + m_widget->setVisible(visible); +} + +} // namespace SpinnerSolution diff --git a/src/libs/solutions/spinner/spinner.h b/src/libs/solutions/spinner/spinner.h new file mode 100644 index 0000000000..567acb3763 --- /dev/null +++ b/src/libs/solutions/spinner/spinner.h @@ -0,0 +1,36 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#ifndef SPINNER_H +#define SPINNER_H + +#include "spinner_global.h" + +#include <QObject> + +namespace SpinnerSolution { + +Q_NAMESPACE_EXPORT(SPINNER_EXPORT) + +enum class SpinnerSize { Small, Medium, Large }; +Q_ENUM_NS(SpinnerSize); + +// TODO: SpinnerOverlay and SpinnerWidget? + +class SPINNER_EXPORT Spinner : public QObject +{ +public: + Spinner(SpinnerSize size, QWidget *parent = nullptr); + void setSize(SpinnerSize size); + void show(); + void hide(); + bool isVisible() const; + void setVisible(bool visible); + +private: + class SpinnerWidget *m_widget = nullptr; +}; + +} // namespace SpinnerSolution + +#endif // SPINNER_H diff --git a/src/libs/solutions/spinner/spinner.qbs b/src/libs/solutions/spinner/spinner.qbs new file mode 100644 index 0000000000..cd830d107e --- /dev/null +++ b/src/libs/solutions/spinner/spinner.qbs @@ -0,0 +1,13 @@ +QtcLibrary { + name: "Spinner" + Depends { name: "Qt"; submodules: ["core", "widgets"] } + cpp.defines: base.concat("SPINNER_LIBRARY") + + files: [ + "spinner.cpp", + "spinner.h", + "spinner.qrc", + "spinner_global.h", + ] +} + diff --git a/src/libs/solutions/spinner/spinner.qrc b/src/libs/solutions/spinner/spinner.qrc new file mode 100644 index 0000000000..5ad85953e8 --- /dev/null +++ b/src/libs/solutions/spinner/spinner.qrc @@ -0,0 +1,7 @@ +<RCC> + <qresource prefix="/" > + <file>icons/spinner_large.png</file> + <file>icons/spinner_medium.png</file> + <file>icons/spinner_small.png</file> + </qresource> +</RCC> diff --git a/src/libs/solutions/spinner/spinner_global.h b/src/libs/solutions/spinner/spinner_global.h new file mode 100644 index 0000000000..f50b09f23f --- /dev/null +++ b/src/libs/solutions/spinner/spinner_global.h @@ -0,0 +1,14 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#pragma once + +#include <qglobal.h> + +#if defined(SPINNER_LIBRARY) +# define SPINNER_EXPORT Q_DECL_EXPORT +#elif defined(SPINNER_STATIC_LIBRARY) +# define SPINNER_EXPORT +#else +# define SPINNER_EXPORT Q_DECL_IMPORT +#endif |