diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/gui/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/gui/kwindowstatesaver.cpp | 136 | ||||
| -rw-r--r-- | src/gui/kwindowstatesaver.h | 144 | 
3 files changed, 282 insertions, 0 deletions
| diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index c7474893..e717c1c1 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -11,6 +11,7 @@ target_sources(KF5ConfigGui PRIVATE      kstandardshortcut.cpp      kstandardshortcutwatcher.cpp      kwindowconfig.cpp +    kwindowstatesaver.cpp  )  ecm_qt_declare_logging_category(KF5ConfigGui @@ -48,6 +49,7 @@ ecm_generate_headers(KConfigGui_HEADERS    KStandardShortcut    KStandardShortcutWatcher    KWindowConfig +  KWindowStateSaver     REQUIRED_HEADERS KConfigGui_HEADERS  ) diff --git a/src/gui/kwindowstatesaver.cpp b/src/gui/kwindowstatesaver.cpp new file mode 100644 index 00000000..d3eaa99e --- /dev/null +++ b/src/gui/kwindowstatesaver.cpp @@ -0,0 +1,136 @@ +/* +    SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org> +    SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#include "kwindowstatesaver.h" +#include "ksharedconfig.h" +#include "kwindowconfig.h" + +#include <QWindow> + +class KWindowStateSaverPrivate +{ +public: +    QWindow *window = nullptr; +    KConfigGroup configGroup; +    std::function<QWindow *()> windowHandleCallback; +    int timerId = 0; + +    void init(KWindowStateSaver *q); +    void initWidget(QObject *widget, KWindowStateSaver *q); +}; + +void KWindowStateSaverPrivate::init(KWindowStateSaver *q) +{ +    if (!window) { +        return; +    } + +    KWindowConfig::restoreWindowSize(window, configGroup); +    KWindowConfig::restoreWindowPosition(window, configGroup); + +    const auto deferredSave = [q, this]() { +        if (!timerId) { +            timerId = q->startTimer(250); +        } +    }; +    QObject::connect(window, &QWindow::widthChanged, q, deferredSave); +    QObject::connect(window, &QWindow::heightChanged, q, deferredSave); +    QObject::connect(window, &QWindow::xChanged, q, deferredSave); +    QObject::connect(window, &QWindow::yChanged, q, deferredSave); +} + +void KWindowStateSaverPrivate::initWidget(QObject *widget, KWindowStateSaver *q) +{ +    if (!window && windowHandleCallback) { +        window = windowHandleCallback(); +    } +    if (window) { +        init(q); +    } else { +        widget->installEventFilter(q); +    } +} + +KWindowStateSaver::KWindowStateSaver(QWindow *window, const KConfigGroup &configGroup) +    : QObject(window) +    , d(new KWindowStateSaverPrivate) +{ +    Q_ASSERT(window); +    d->window = window; +    d->configGroup = configGroup; +    d->init(this); +} + +KWindowStateSaver::KWindowStateSaver(QWindow *window, const QString &configGroupName) +    : QObject(window) +    , d(new KWindowStateSaverPrivate) +{ +    Q_ASSERT(window); +    d->window = window; +    d->configGroup = KConfigGroup(KSharedConfig::openStateConfig(), configGroupName); +    d->init(this); +} + +KWindowStateSaver::KWindowStateSaver(QWindow *window, const char *configGroupName) +    : QObject(window) +    , d(new KWindowStateSaverPrivate) +{ +    Q_ASSERT(window); +    d->window = window; +    d->configGroup = KConfigGroup(KSharedConfig::openStateConfig(), configGroupName); +    d->init(this); +} + +KWindowStateSaver::~KWindowStateSaver() +{ +    delete d; +} + +void KWindowStateSaver::timerEvent(QTimerEvent *event) +{ +    killTimer(event->timerId()); +    KWindowConfig::saveWindowPosition(d->window, d->configGroup); +    KWindowConfig::saveWindowSize(d->window, d->configGroup); +    d->timerId = 0; +} + +bool KWindowStateSaver::eventFilter(QObject *watched, QEvent *event) +{ +    // QEvent::PlatformSurface would give us a valid window, but if there are +    // intial resizings (explicitly or via layout constraints) those would then +    // already overwrite our restored values. So wait until all that is done +    // and only restore afterwards. +    if (event->type() == QEvent::ShowToParent && !d->window) { +        watched->removeEventFilter(this); +        d->window = d->windowHandleCallback(); +        d->init(this); +    } + +    return QObject::eventFilter(watched, event); +} + +void KWindowStateSaver::initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const KConfigGroup &configGroup) +{ +    d = new KWindowStateSaverPrivate; +    d->windowHandleCallback = windowHandleCallback; +    d->configGroup = configGroup; +    d->initWidget(widget, this); +} + +void KWindowStateSaver::initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const QString &configGroupName) +{ +    d = new KWindowStateSaverPrivate; +    d->windowHandleCallback = windowHandleCallback; +    d->configGroup = KConfigGroup(KSharedConfig::openStateConfig(), configGroupName); +    d->initWidget(widget, this); +} + +void KWindowStateSaver::initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const char *configGroupName) +{ +    d = new KWindowStateSaverPrivate; +    d->windowHandleCallback = windowHandleCallback; +    d->configGroup = KConfigGroup(KSharedConfig::openStateConfig(), configGroupName); +    d->initWidget(widget, this); +} 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 <vkrause@kde.org> +    SPDX-License-Identifier: LGPL-2.0-or-later +*/ + +#ifndef KWINDOWSTATESAVER_H +#define KWINDOWSTATESAVER_H + +#include <kconfiggroup.h> +#include <kconfiggui_export.h> + +#include <QObject> + +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<typename Widget> +    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<typename Widget> +    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<typename Widget> +    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<QWindow *()> &windowHandleCallback, const KConfigGroup &configGroup); +    void initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const QString &configGroupName); +    void initWidget(QObject *widget, const std::function<QWindow *()> &windowHandleCallback, const char *configGroupName); + +    // cannot use std::unique_ptr due to the template ctors +    // not seeing the full private class +    KWindowStateSaverPrivate *d = nullptr; +}; + +template<typename Widget> +KWindowStateSaver::KWindowStateSaver(Widget *widget, const KConfigGroup &configGroup) +    : QObject(widget) +{ +    initWidget( +        widget, +        [widget]() { +            return widget->windowHandle(); +        }, +        configGroup); +} + +template<typename Widget> +KWindowStateSaver::KWindowStateSaver(Widget *widget, const QString &configGroupName) +    : QObject(widget) +{ +    initWidget( +        widget, +        [widget]() { +            return widget->windowHandle(); +        }, +        configGroupName); +} + +template<typename Widget> +KWindowStateSaver::KWindowStateSaver(Widget *widget, const char *configGroupName) +    : QObject(widget) +{ +    initWidget( +        widget, +        [widget]() { +            return widget->windowHandle(); +        }, +        configGroupName); +} + +#endif // KWINDOWSTATESAVER_H | 
