diff --git a/docs/pics/kbusyindicatorwidget.png b/docs/pics/kbusyindicatorwidget.png new file mode 100644 index 0000000..da081d0 Binary files /dev/null and b/docs/pics/kbusyindicatorwidget.png differ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cea13e4..0c38da6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,307 +1,310 @@ include(ECMPoQmTools) ecm_create_qm_loader(kwidgetsaddons_QM_LOADER kwidgetsaddons5_qt) qt5_add_resources(kwidgetsaddons_ICON_SRCS icons.qrc) set(kwidgetsaddons_SRCS kacceleratormanager.cpp kanimatedbutton.cpp + kbusyindicatorwidget.cpp kcharselect.cpp kcharselectdata.cpp kcollapsiblegroupbox.cpp kcolorbutton.cpp kcolumnresizer.cpp kdatecombobox.cpp kdatepicker.cpp kdatetable.cpp kdatetimeedit.cpp kfontaction.cpp knewpasswordwidget.cpp fonthelpers.cpp kfontsizeaction.cpp kpopupframe.cpp kselectaction.cpp ktimecombobox.cpp kdragwidgetdecorator.cpp kdualaction.cpp kfontchooser.cpp kguiitem.cpp kled.cpp kmessagebox.cpp kmessagebox_p.cpp kmultitabbar.cpp kstandardguiitem.cpp kurllabel.cpp # Not good enough quality. Needs to use QUrl instead of QString and should not inherit QLabel, but hold it as a member instead. kcapacitybar.cpp kfontrequester.cpp kpassworddialog.cpp kruler.cpp kselector.cpp kxyselector.cpp kseparator.cpp ksqueezedtextlabel.cpp ktitlewidget.cpp ktoggleaction.cpp ktogglefullscreenaction.cpp kviewstateserializer.cpp kviewstatemaintainerbase.cpp keditlistwidget.cpp kcursor.cpp kratingwidget.cpp kratingpainter.cpp kactionselector.cpp kcolorcombo.cpp ktoolbarspaceraction.cpp kpixmapregionselectordialog.cpp kpixmapregionselectorwidget.cpp kactionmenu.cpp ktoolbarlabelaction.cpp ktoolbarpopupaction.cpp kpagedialog.cpp kpagemodel.cpp kpageview.cpp kpageview_p.cpp kpagewidget.cpp kpagewidgetmodel.cpp kassistantdialog.cpp kmessagewidget.cpp knewpassworddialog.cpp lineediturldropeventfilter.cpp kpasswordlineedit.cpp kpixmapsequence.cpp kpixmapsequenceoverlaypainter.cpp kpixmapsequencewidget.cpp kmimetypechooser.cpp kmimetypeeditor.cpp common_helpers.cpp ksplittercollapserbutton.cpp kstyleextensions.cpp ktooltipwidget.cpp ${kwidgetsaddons_QM_LOADER} ${kwidgetsaddons_ICON_SRCS} ) set(kwidgetsaddon_UI kdatetimeedit.ui knewpassworddialog.ui knewpasswordwidget.ui kpassworddialog.ui ) qt5_wrap_ui(kwidgetsaddons_SRCS ${kwidgetsaddon_UI} ) ecm_qt_declare_logging_category(kwidgetsaddons_SRCS HEADER loggingcategory.h IDENTIFIER KWidgetsAddonsLog CATEGORY_NAME kf5.kwidgetsaddons DEFAULT_SEVERITY Warning ) add_library(KF5WidgetsAddons ${kwidgetsaddons_SRCS}) generate_export_header(KF5WidgetsAddons BASE_NAME KWidgetsAddons) add_library(KF5::WidgetsAddons ALIAS KF5WidgetsAddons) target_link_libraries(KF5WidgetsAddons PUBLIC Qt5::Widgets) target_include_directories(KF5WidgetsAddons INTERFACE "$") set_target_properties(KF5WidgetsAddons PROPERTIES VERSION ${KWIDGETSADDONS_VERSION_STRING} SOVERSION ${KWIDGETSADDONS_SOVERSION} EXPORT_NAME WidgetsAddons ) ecm_generate_headers(KWidgetsAddons_HEADERS HEADER_NAMES KAcceleratorManager KAnimatedButton + KBusyIndicatorWidget KCharSelect KCollapsibleGroupBox KColorButton KColumnResizer KDateComboBox KDatePicker KDateTimeEdit KDragWidgetDecorator KDualAction KFontAction KFontChooser KFontSizeAction KGuiItem KLed KMessageBox KMessageBoxDontAskAgainInterface KMultiTabBar,KMultiTabBarButton,KMultiTabBarTab KNewPasswordWidget KPopupFrame KSelectAction KStandardGuiItem KTimeComboBox KUrlLabel KCapacityBar KFontRequester KPasswordDialog KRuler KSelector,KGradientSelector KTitleWidget KXYSelector KSeparator KSqueezedTextLabel KToggleAction KToggleFullScreenAction KViewStateSerializer KViewStateMaintainerBase KEditListWidget KCursor KRatingPainter KRatingWidget KActionSelector KColorCombo KActionMenu KToolBarLabelAction KToolBarPopupAction KToolBarSpacerAction KPageDialog KPageModel KPageView KPageWidget KPageWidgetModel,KPageWidgetItem KAssistantDialog KMessageWidget KNewPasswordDialog LineEditUrlDropEventFilter KPasswordLineEdit KPixmapSequence KPixmapSequenceOverlayPainter KPixmapSequenceWidget KPixmapRegionSelectorDialog KPixmapRegionSelectorWidget KMimeTypeChooser,KMimeTypeChooserDialog KMimeTypeEditor KMessageBoxNotifyInterface KSplitterCollapserButton KStyleExtensions KToolTipWidget REQUIRED_HEADERS KWidgetsAddons_HEADERS ) find_package(PythonModuleGeneration) if (PythonModuleGeneration_FOUND) ecm_generate_python_binding( TARGET KF5::WidgetsAddons PYTHONNAMESPACE PyKF5 MODULENAME KWidgetsAddons RULES_FILE "${CMAKE_SOURCE_DIR}/cmake/rules_PyKF5.py" SIP_DEPENDS QtWidgets/QtWidgetsmod.sip HEADERS kacceleratormanager.h kanimatedbutton.h + kbusyindicatorwidget.h kcharselect.h kcollapsiblegroupbox.h kcolorbutton.h kcolumnresizer.h kdatecombobox.h kdatepicker.h kdatetimeedit.h kdragwidgetdecorator.h kdualaction.h kfontaction.h kfontchooser.h kfontsizeaction.h kguiitem.h kled.h kmessagebox.h kmessageboxdontaskagaininterface.h kmultitabbar.h knewpasswordwidget.h kpopupframe.h kselectaction.h kstandardguiitem.h ktimecombobox.h kurllabel.h kcapacitybar.h kfontrequester.h kpassworddialog.h kruler.h kselector.h ktitlewidget.h kxyselector.h kseparator.h ksqueezedtextlabel.h ktoggleaction.h ktogglefullscreenaction.h kviewstateserializer.h kviewstatemaintainerbase.h keditlistwidget.h kcursor.h kratingpainter.h kratingwidget.h kactionselector.h kcolorcombo.h kactionmenu.h ktoolbarlabelaction.h ktoolbarpopupaction.h ktoolbarspaceraction.h kpagedialog.h kpagemodel.h kpageview.h kpagewidget.h kpagewidgetmodel.h kassistantdialog.h kmessagewidget.h knewpassworddialog.h lineediturldropeventfilter.h kpasswordlineedit.h kpixmapsequence.h kpixmapsequenceoverlaypainter.h kpixmapsequencewidget.h kpixmapregionselectordialog.h kpixmapregionselectorwidget.h kmimetypechooser.h kmimetypeeditor.h kmessageboxnotifyinterface.h ksplittercollapserbutton.h kstyleextensions.h ) endif() install(TARGETS KF5WidgetsAddons EXPORT KF5WidgetsAddonsTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${KWidgetsAddons_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kwidgetsaddons_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KWidgetsAddons COMPONENT Devel ) install(FILES kcharselect-data DESTINATION ${KDE_INSTALL_DATADIR_KF5}/kcharselect) if (BUILD_QCH) ecm_add_qch( KF5WidgetsAddons_QCH NAME KWidgetsAddons BASE_NAME KF5WidgetsAddons VERSION ${KF5_VERSION} ORG_DOMAIN org.kde SOURCES # using only public headers, to cover only public API ${KWidgetsAddons_HEADERS} MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics" LINK_QCHS Qt5Core_QCH Qt5Gui_QCH Qt5Widgets_QCH BLANK_MACROS KWIDGETSADDONS_EXPORT KWIDGETSADDONS_DEPRECATED_EXPORT KWIDGETSADDONS_DEPRECATED TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} COMPONENT Devel ) endif() include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KWidgetsAddons LIB_NAME KF5WidgetsAddons DEPS "widgets" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KWidgetsAddons) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/kbusyindicatorwidget.cpp b/src/kbusyindicatorwidget.cpp new file mode 100644 index 0000000..ee1e12a --- /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/src/kbusyindicatorwidget.h b/src/kbusyindicatorwidget.h new file mode 100644 index 0000000..db6c0c0 --- /dev/null +++ b/src/kbusyindicatorwidget.h @@ -0,0 +1,74 @@ +/* + 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. + * + * 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/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9975049..c5ca8f6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,57 +1,58 @@ include(ECMMarkAsTest) find_package(Qt5 ${REQUIRED_QT_VERSION} CONFIG REQUIRED Test) macro(kwidgetsaddons_executable_tests) foreach(_testname ${ARGN}) add_executable(${_testname} ${_testname}.cpp) target_link_libraries(${_testname} Qt5::Test KF5::WidgetsAddons) ecm_mark_as_test(${_testname}) endforeach(_testname) endmacro() kwidgetsaddons_executable_tests( kanimatedbuttontest + kbusyindicatorwidgettest kcharselecttest kcollapsiblegroupboxtest kdatepicktest kdatetimeedittestapp kledtest kmessageboxtest kmessageboxwidtest kselectactiontest kseparatortest ksqueezedtextlabeltest ktitlewidgettest kfontrequestertest kpassworddialogtest keditlistwidgettest kratingwidgettest kactionselectortest kcolorcombotest kpixmapregionselectordialogtest ktoolbarlabelactiontest kpagedialogtest kpagewidgettest kassistantdialogtest kmessagewidgettest knewpassworddialogtest knewpasswordwidget_test kpixmapsequenceoverlaypaintertest kmimetypechoosertest ksplittercollapserbuttongui_test ktooltipwidget_test kpasswordlineedit_test ) add_executable(kdatetabletest kdatetabletest.cpp ../src/kdatetable.cpp) target_include_directories(kdatetabletest PRIVATE ../src) target_link_libraries(kdatetabletest Qt5::Widgets) ecm_mark_as_test(kdatetabletest) set(kcolumnresizertestapp_SRCS kcolumnresizertestapp.cpp) qt5_wrap_ui(kcolumnresizertestapp_SRCS kcolumnresizertestapp.ui) add_executable(kcolumnresizertestapp ${kcolumnresizertestapp_SRCS}) target_link_libraries(kcolumnresizertestapp KF5::WidgetsAddons Qt5::Widgets) ecm_mark_as_test(kcolumnresizertestapp) diff --git a/tests/kbusyindicatorwidgettest.cpp b/tests/kbusyindicatorwidgettest.cpp new file mode 100644 index 0000000..911f5ec --- /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(); +}