aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJarek Kobus <jaroslaw.kobus@qt.io>2023-06-15 13:18:32 +0200
committerJarek Kobus <jaroslaw.kobus@qt.io>2023-06-20 18:48:24 +0000
commit2d5a8252807d34ba59d5363e43ea20b34d4c2c13 (patch)
treecb71728f9bbb2fd5600981b9e94edbfec78d838e
parent73c20f19bb7be09b10c3d490c22bdf1084082c0a (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.txt1
-rw-r--r--src/libs/solutions/solutions.qbs1
-rw-r--r--src/libs/solutions/spinner/CMakeLists.txt9
-rw-r--r--src/libs/solutions/spinner/icons/spinner_large.pngbin0 -> 1346 bytes
-rw-r--r--src/libs/solutions/spinner/icons/spinner_medium.pngbin0 -> 765 bytes
-rw-r--r--src/libs/solutions/spinner/icons/spinner_small.pngbin0 -> 315 bytes
-rw-r--r--src/libs/solutions/spinner/spinner.cpp194
-rw-r--r--src/libs/solutions/spinner/spinner.h36
-rw-r--r--src/libs/solutions/spinner/spinner.qbs13
-rw-r--r--src/libs/solutions/spinner/spinner.qrc7
-rw-r--r--src/libs/solutions/spinner/spinner_global.h14
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
new file mode 100644
index 0000000000..c24ff1b77c
--- /dev/null
+++ b/src/libs/solutions/spinner/icons/spinner_large.png
Binary files differ
diff --git a/src/libs/solutions/spinner/icons/spinner_medium.png b/src/libs/solutions/spinner/icons/spinner_medium.png
new file mode 100644
index 0000000000..d64cc514e1
--- /dev/null
+++ b/src/libs/solutions/spinner/icons/spinner_medium.png
Binary files differ
diff --git a/src/libs/solutions/spinner/icons/spinner_small.png b/src/libs/solutions/spinner/icons/spinner_small.png
new file mode 100644
index 0000000000..254e9c82fc
--- /dev/null
+++ b/src/libs/solutions/spinner/icons/spinner_small.png
Binary files differ
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