diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ set(kwidgetsaddons_SRCS kacceleratormanager.cpp kanimatedbutton.cpp + kbusyindicatorwidget.cpp kcharselect.cpp kcharselectdata.cpp kcollapsiblegroupbox.cpp @@ -115,6 +116,7 @@ HEADER_NAMES KAcceleratorManager KAnimatedButton + KBusyIndicatorWidget KCharSelect KCollapsibleGroupBox KColorButton @@ -198,6 +200,7 @@ HEADERS kacceleratormanager.h kanimatedbutton.h + kbusyindicatorwidget.h kcharselect.h kcollapsiblegroupbox.h kcolorbutton.h diff --git a/src/kbusyindicatorwidget.h b/src/kbusyindicatorwidget.h new file mode 100644 --- /dev/null +++ b/src/kbusyindicatorwidget.h @@ -0,0 +1,75 @@ +/* + Copyright 2019 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#ifndef KBUSYINDICATORWIDGET_H +#define KBUSYINDICATORWIDGET_H + +#include + +#include + +/** + * @brief Rotating spinning icon to indicate busyness + * + * When you need to communicate to the user that your application is busy with + * something you'll want to use a KBusyIndicatorWidget to display an infinately + * spinnning indicator icon. + * + * KBusyIndicatorWidget is set apart from KPixmapSequenceWidget in that it + * does not render a pixmap sequence but rather animates a scaled Icon. + * It can support multiple semi-abitrary sizes and quality is only limited + * by the resolution of available icons. It is also easier to use as its use + * is more specific. + */ +class KWIDGETSADDONS_EXPORT KBusyIndicatorWidget : public QWidget +{ + Q_OBJECT + /** + * Whether the indicator is running or not. For the indicator to actually + * animate it must be both visible **and** running. + */ + Q_PROPERTY(bool running READ running WRITE setRunning NOTIFY runningChanged) +public: + explicit KBusyIndicatorWidget(QWidget *parent = nullptr); + virtual ~KBusyIndicatorWidget(); + + /// @returns Whether this widget is running. + bool running() const; + +public Q_SLOTS: + /// Set whether this widget is running. + void setRunning(bool running); + +Q_SIGNALS: + /// Emitted when the running state changes. + void runningChanged(bool running); + +protected: + virtual void showEvent(QShowEvent *event) override; + virtual void hideEvent(QHideEvent *event) override; + virtual void resizeEvent(QResizeEvent *event) override; + virtual void paintEvent(QPaintEvent *) override; + +private: + class Private; + Private *d; +}; + +#endif // KBUSYINDICATORWIDGET_H diff --git a/src/kbusyindicatorwidget.cpp b/src/kbusyindicatorwidget.cpp new file mode 100644 --- /dev/null +++ b/src/kbusyindicatorwidget.cpp @@ -0,0 +1,128 @@ +/* + Copyright 2019 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "kbusyindicatorwidget.h" + +#include +#include +#include +#include + +class KBusyIndicatorWidget::Private : public QObject +{ + Q_OBJECT + Q_PROPERTY(qreal rotation MEMBER rotation WRITE setRotation) +public: + Private(KBusyIndicatorWidget *parent) + : q(parent) + , animation(new QPropertyAnimation(this, "rotation", q)) + { + animation->setLoopCount(-1); + animation->setDuration(1000); + animation->setStartValue(0); + animation->setEndValue(360); + } + + void setRotation(qreal newRotation) + { + rotation = newRotation; + q->update(); // repaint new rotation + } + + // Called when running and/or visibility changes to start/pause the + // animation as necessary. + void maybeToggleAnimation() + { + if (running && q->isVisible()) { + animation->start(); + return; + } + animation->pause(); + } + + KBusyIndicatorWidget *q = nullptr; + QPropertyAnimation *animation = nullptr; + QIcon icon = QIcon::fromTheme(QStringLiteral("view-refresh")); + qreal rotation = 0; + bool running = true; + QPointF paintCenter; +}; + +KBusyIndicatorWidget::KBusyIndicatorWidget(QWidget *parent) + : QWidget(parent) + , d(new Private(this)) +{ +} + +KBusyIndicatorWidget::~KBusyIndicatorWidget() +{ + delete d; +} + +bool KBusyIndicatorWidget::running() const +{ + return d->running; +} + +void KBusyIndicatorWidget::setRunning(bool running) +{ + if (d->running == running) { + return; + } + + d->running = running; + d->maybeToggleAnimation(); + emit runningChanged(d->running); +} + +void KBusyIndicatorWidget::showEvent(QShowEvent *event) +{ + d->maybeToggleAnimation(); + QWidget::showEvent(event); +} + +void KBusyIndicatorWidget::hideEvent(QHideEvent *event) +{ + d->maybeToggleAnimation(); + QWidget::hideEvent(event); +} + +void KBusyIndicatorWidget::resizeEvent(QResizeEvent *event) +{ + QWidget::resizeEvent(event); + + d->paintCenter = QPointF(event->size().width() / 2.0, + event->size().height() / 2.0); +} + +void KBusyIndicatorWidget::paintEvent(QPaintEvent *) +{ + QPainter painter(this); + painter.setRenderHint(QPainter::SmoothPixmapTransform); + + // Rotate around the center and then reset back to origin for icon painting. + painter.translate(d->paintCenter); + painter.rotate(d->rotation); + painter.translate(-d->paintCenter); + + d->icon.paint(&painter, rect()); +} + +#include "kbusyindicatorwidget.moc" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -13,6 +13,7 @@ kwidgetsaddons_executable_tests( kanimatedbuttontest + kbusyindicatorwidgettest kcharselecttest kcollapsiblegroupboxtest kdatepicktest diff --git a/tests/kbusyindicatorwidgettest.cpp b/tests/kbusyindicatorwidgettest.cpp new file mode 100644 --- /dev/null +++ b/tests/kbusyindicatorwidgettest.cpp @@ -0,0 +1,35 @@ +/* + Copyright 2019 Harald Sitter + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) version 3, or any + later version accepted by the membership of KDE e.V. (or its + successor approved by the membership of KDE e.V.), which shall + act as a proxy defined in Section 6 of version 3 of the license. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library. If not, see . +*/ + +#include "kbusyindicatorwidget.h" + +#include + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + KBusyIndicatorWidget w; + w.setBaseSize(128, 128); + w.setRunning(true); + w.show(); + + return app.exec(); +}