From f446af2aa592997f6bc4aa3b5559cf477f9259f8 Mon Sep 17 00:00:00 2001 From: Volker Krause Date: Mon, 14 Feb 2022 18:12:24 +0100 Subject: Add KWindowStateSaver This is basically the C++ counter-part to https://invent.kde.org/frameworks/kconfig/-/merge_requests/94 and allows to easily retrofit window size persistence on existing windows/ dialogs, replacing e.g. code like https://invent.kde.org/pim/pimcommon/-/blob/master/src/pimcommon/widgets/kpimprintpreviewdialog.cpp. This is a bit more complicated than one might expect, as KWindowConfig works with QWindows, but that's something freshly created QWidget windows/ dialogs don't have yet. Additionally, we are in a library here that doesn't depend on Qt::Widgets. To overcome this we move the widget-dependent code (basically just a call to QWidget::windowHandle()) to inline template code (and thus into the consumer), use std::function's type erasure to pass it into the library code, and an event filter on the widget to wait for the QWindow to become available. --- src/gui/kwindowstatesaver.h | 144 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 src/gui/kwindowstatesaver.h (limited to 'src/gui/kwindowstatesaver.h') diff --git a/src/gui/kwindowstatesaver.h b/src/gui/kwindowstatesaver.h new file mode 100644 index 00000000..47b6ae95 --- /dev/null +++ b/src/gui/kwindowstatesaver.h @@ -0,0 +1,144 @@ +/* + SPDX-FileCopyrightText: 2022 Volker Krause + SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KWINDOWSTATESAVER_H +#define KWINDOWSTATESAVER_H + +#include +#include + +#include + +class QWindow; +class KWindowStateSaverPrivate; + +/** + * Saves and restores a window size and (when possible) position. + * + * This is useful for retrofitting persisting window geometry on existing windows or dialogs, + * without having to modify those classes themselves, or having to inherit from them. + * For this, create a new instance of KWindowStateSaver for every window that should have it's + * state persisted, and pass it the window or widget as well as the config group the state + * should be stored in. The KWindowStateSaver will restore an existing state and then monitor + * the window for subsequent changes to persist. It will delete itself once the window is + * deleted. + * + * @code + * QPrintPreviewDialog dlg = ... + * new KWindowStateSaver(&dlg, "printPreviewDialogState"); + * ... + * dlg.exec(); + * @endcode + * + * Note that freshly created top-level QWidgets (such as the dialog in the above example) + * do not have an associated QWindow yet (ie. windowHandle() return @c nullptr). KWindowStateSaver + * supports this with its QWidget constructors which will monitor the widget for having + * its associated QWindow created before continuing with that. + * + * When implementing your own windows/dialogs, using KWindowConfig directly can be an + * alternative. + * + * @see KWindowConfig + * @since 5.92 + */ +class KCONFIGGUI_EXPORT KWindowStateSaver : public QObject +{ + Q_OBJECT +public: + /** + * Create a new window state saver for @p window. + * @param configGroup A KConfigGroup that holds the window state. + */ + explicit KWindowStateSaver(QWindow *window, const KConfigGroup &configGroup); + /** + * Create a new window state saver for @p window. + * @param configGroupName The name of a KConfigGroup in the default state + * configuration (see KSharedConfig::openStateConfig) that holds the window state. + */ + explicit KWindowStateSaver(QWindow *window, const QString &configGroupName); + /** + * Create a new window state saver for @p window. + * @param configGroupName The name of a KConfigGroup in the default state + * configuration (see KSharedConfig::openStateConfig) that holds the window state. + */ + explicit KWindowStateSaver(QWindow *window, const char *configGroupName); + + /** + * Create a new window state saver for @p widget. + * Use this for widgets that aren't shown yet and would still return @c nullptr from windowHandle(). + * @param configGroup A KConfigGroup that holds the window state. + */ + template + explicit inline KWindowStateSaver(Widget *widget, const KConfigGroup &configGroup); + /** + * Create a new window state saver for @p widget. + * Use this for widgets that aren't shown yet and would still return @c nullptr from windowHandle(). + * @param configGroupName The name of a KConfigGroup in the default state + * configuration (see KSharedConfig::openStateConfig) that holds the window state. + */ + template + explicit inline KWindowStateSaver(Widget *widget, const QString &configGroupName); + /** + * Create a new window state saver for @p widget. + * Use this for widgets that aren't shown yet and would still return @c nullptr from windowHandle(). + * @param configGroupName The name of a KConfigGroup in the default state + * configuration (see KSharedConfig::openStateConfig) that holds the window state. + */ + template + explicit inline KWindowStateSaver(Widget *widget, const char *configGroupName); + + ~KWindowStateSaver(); + +private: + void timerEvent(QTimerEvent *event) override; + bool eventFilter(QObject *watched, QEvent *event) override; + + // API used by template code, so technically part of the ABI + void initWidget(QObject *widget, const std::function &windowHandleCallback, const KConfigGroup &configGroup); + void initWidget(QObject *widget, const std::function &windowHandleCallback, const QString &configGroupName); + void initWidget(QObject *widget, const std::function &windowHandleCallback, const char *configGroupName); + + // cannot use std::unique_ptr due to the template ctors + // not seeing the full private class + KWindowStateSaverPrivate *d = nullptr; +}; + +template +KWindowStateSaver::KWindowStateSaver(Widget *widget, const KConfigGroup &configGroup) + : QObject(widget) +{ + initWidget( + widget, + [widget]() { + return widget->windowHandle(); + }, + configGroup); +} + +template +KWindowStateSaver::KWindowStateSaver(Widget *widget, const QString &configGroupName) + : QObject(widget) +{ + initWidget( + widget, + [widget]() { + return widget->windowHandle(); + }, + configGroupName); +} + +template +KWindowStateSaver::KWindowStateSaver(Widget *widget, const char *configGroupName) + : QObject(widget) +{ + initWidget( + widget, + [widget]() { + return widget->windowHandle(); + }, + configGroupName); +} + +#endif // KWINDOWSTATESAVER_H -- cgit v1.2.1