diff --git a/docs/pics/kbusyindicatorwidget.png b/docs/pics/kbusyindicatorwidget.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ + + 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. + * + * A way of using this widget is to combine it with a QLabel to construct a + * status line: + * + * ``` + * auto layout = new QHBoxLayout; + * layout->addWidget(new KBusyIndicatorWidget); + * layout->addWidget(new QLabel(QStringLiteral("Waterig the flowers..."))); + * ``` + * + * @image html kbusyindicatorwidget.png "KBusyIndicatorWidget with label" + * + * 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. + * + * @since 5.61.0 + */ +class KWIDGETSADDONS_EXPORT KBusyIndicatorWidget : public QWidget +{ + Q_OBJECT +public: + explicit KBusyIndicatorWidget(QWidget *parent = nullptr); + ~KBusyIndicatorWidget() override; + + QSize minimumSizeHint() const override; + +protected: + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; + void resizeEvent(QResizeEvent *event) override; + void paintEvent(QPaintEvent *) override; + bool event(QEvent *event) override; + +private: + class KBusyIndicatorWidgetPrivate *const 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,109 @@ +/* + 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 +#include +#include + +class KBusyIndicatorWidgetPrivate +{ +public: + KBusyIndicatorWidgetPrivate(KBusyIndicatorWidget *parent) + : q(parent) + { + animation.setLoopCount(-1); + animation.setDuration(1500); + animation.setStartValue(0); + animation.setEndValue(360); + QObject::connect(&animation, &QVariantAnimation::valueChanged, + q, [=](QVariant value) { + rotation = value.toReal(); + q->update(); // repaint new rotation + }); + } + + KBusyIndicatorWidget *q = nullptr; + QVariantAnimation animation; + QIcon icon = QIcon::fromTheme(QStringLiteral("view-refresh")); + qreal rotation = 0; + QPointF paintCenter; +}; + +KBusyIndicatorWidget::KBusyIndicatorWidget(QWidget *parent) + : QWidget(parent) + , d(new KBusyIndicatorWidgetPrivate(this)) +{ +} + +KBusyIndicatorWidget::~KBusyIndicatorWidget() +{ + delete d; +} + +QSize KBusyIndicatorWidget::minimumSizeHint() const +{ + const auto extent = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize); + return QSize(extent, extent); +} + +void KBusyIndicatorWidget::showEvent(QShowEvent *event) +{ + QWidget::showEvent(event); + d->animation.start(); +} + +void KBusyIndicatorWidget::hideEvent(QHideEvent *event) +{ + QWidget::hideEvent(event); + d->animation.pause(); +} + +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()); +} + +bool KBusyIndicatorWidget::event(QEvent *event) +{ + // Only overridden to be flexible WRT binary compatible in the future. + // Overriding later has potential to change the call going through + // the vtable or not. + return QWidget::event(event); +} 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,56 @@ +/* + 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 + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + QWidget window; + window.setBaseSize(128, 128); + auto layout = new QVBoxLayout(&window); + + auto busyWidget = new QWidget(&window); + auto busyLayout = new QHBoxLayout(busyWidget); + auto busyIndicator = new KBusyIndicatorWidget(&window); + auto busyLabel = new QLabel(QStringLiteral("Busy..."), &window); + busyLayout->addWidget(busyIndicator); + busyLayout->addWidget(busyLabel); + + auto toggle = new QPushButton(QStringLiteral("Toggle Visible"), &window); + + QObject::connect(toggle, &QPushButton::clicked, + busyWidget, [=] { + busyWidget->setVisible(!busyWidget->isVisible()); + }); + + layout->addWidget(toggle); + layout->addWidget(busyWidget); + layout->setAlignment(Qt::AlignTop); + + window.show(); + return app.exec(); +}