From a7f4e4f14a9a891764562e183437adfea9a63a13 Mon Sep 17 00:00:00 2001 From: Sergio Martins Date: Sat, 14 Dec 2024 10:56:49 +0000 Subject: [PATCH] Fix restoring maximized window corner case For issue #553 On Windows it's possible to have normal geometry in a different screen than the maximized window if you drag a window across monitors and touch an aerosnap edge. While restoring, we need a fake setGeometry() call to set the normal geometry, since Qt doesn't provide QWindow::setNormalGeometry(), that resulted in the window being maximized on the wrong window. The solution is to patch the normal geometry to be in the first screen. --- Changelog | 1 + src/LayoutSaver.cpp | 12 ++++++++++++ src/core/Platform.h | 4 ++++ src/core/Window_p.h | 5 +++++ src/qtcommon/Platform.cpp | 11 +++++++++++ src/qtcommon/Platform.h | 1 + src/qtcommon/Window.cpp | 27 +++++++++++++++++++++++++++ src/qtcommon/Window_p.h | 3 +++ 8 files changed, 64 insertions(+) diff --git a/Changelog b/Changelog index 4a74bad07..76b5d3309 100644 --- a/Changelog +++ b/Changelog @@ -10,6 +10,7 @@ - Fix case where persistent central widget would detach when dragged - Allow to build against external KDBindings - Fix restore layout of nested main windows (#508) + - Fix restore maximized window corner case (#553) * v2.1.0 (08 May 2024) - Added standalone layouting example using Slint diff --git a/src/LayoutSaver.cpp b/src/LayoutSaver.cpp index 7cb5da4c8..cb50b6425 100644 --- a/src/LayoutSaver.cpp +++ b/src/LayoutSaver.cpp @@ -642,6 +642,18 @@ void LayoutSaver::Private::deserializeWindowGeometry(const T &saved, Window::Ptr // The window will be maximized. We first set its geometry to normal // Later it's maximized and will remember this value geometry = saved.normalGeometry; + + if (saved.screenIndex != Platform::instance()->screenNumberForPoint(geometry.topLeft())) { + // Workaround bug #553. Window is maximized on screen 2 but its normal + // geometry is on screen 1. Restoring normal geometry would move it to screen 2 + // To avoid that, we move its normal geometry to screen 2. Could be fixed + // without workarounds if Qt supported QWindow::setNormalGeometry() + window->setScreen(saved.screenIndex); + if (auto screen = window->screen()) { + // center is as good as any + geometry.moveCenter(screen->geometry().center()); + } + } } Core::FloatingWindow::ensureRectIsOnScreen(geometry); diff --git a/src/core/Platform.h b/src/core/Platform.h index c11d3181f..9f4149658 100644 --- a/src/core/Platform.h +++ b/src/core/Platform.h @@ -83,6 +83,10 @@ class DOCKS_EXPORT Platform /// @brief Returns the size of the screen where this view is in virtual Size screenSizeFor(View *) const = 0; + /// @brief Returns which screen the point is in + /// -1 if not found + virtual int screenNumberForPoint(Point) const = 0; + /// @brief Create an empty view /// For Qt this would just returns a empty QWidget or QQuickItem /// other frontends can return something as basic. diff --git a/src/core/Window_p.h b/src/core/Window_p.h index d98b21726..266f657a3 100644 --- a/src/core/Window_p.h +++ b/src/core/Window_p.h @@ -96,6 +96,11 @@ class DOCKS_EXPORT Window /// @brief Returns the screen this window is on virtual Screen::Ptr screen() const = 0; + /// @brief Sets the screen this window belongs to + /// Does not move it + virtual void setScreen(int) = 0; + virtual int screenIndex() const = 0; + /// Deletes the underlying window. Only used during tests. virtual void destroy() = 0; diff --git a/src/qtcommon/Platform.cpp b/src/qtcommon/Platform.cpp index 3209ff719..8d5e0065a 100644 --- a/src/qtcommon/Platform.cpp +++ b/src/qtcommon/Platform.cpp @@ -216,6 +216,17 @@ int Platform_qt::screenNumberForWindow(std::shared_ptr window) con return screenNumberForQWindow(static_cast(window.get())->qtWindow()); } +int Platform_qt::screenNumberForPoint(Point pt) const +{ + const auto screens = qApp->screens(); + for (int i = 0; i < screens.size(); ++i) { + if (screens[i]->geometry().contains(pt)) + return i; + } + + return -1; +} + int Platform_qt::screenNumberForQWindow(QWindow *window) const { if (QScreen *screen = window->screen()) { diff --git a/src/qtcommon/Platform.h b/src/qtcommon/Platform.h index 9ab7cfd20..12b0387b5 100644 --- a/src/qtcommon/Platform.h +++ b/src/qtcommon/Platform.h @@ -39,6 +39,7 @@ class DOCKS_EXPORT Platform_qt : public Core::Platform QVector> windows() const override; virtual std::shared_ptr windowFromQWindow(QWindow *) const = 0; int screenNumberForWindow(std::shared_ptr) const override; + int screenNumberForPoint(Point) const override; void sendEvent(Core::View *, QEvent *) const override; diff --git a/src/qtcommon/Window.cpp b/src/qtcommon/Window.cpp index 3bd8fac31..4dcf2fd3e 100644 --- a/src/qtcommon/Window.cpp +++ b/src/qtcommon/Window.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -178,3 +179,29 @@ bool Window::isFullScreen() const { return m_window->windowStates() & Qt::WindowFullScreen; } + +void Window::setScreen(int index) +{ + const auto screens = qApp->screens(); + const int numScreens = screens.size(); + if (index >= numScreens || index < 0) { + qWarning() << Q_FUNC_INFO << "index out of bounds" << index << numScreens; + return; + } + + if (m_window) { + m_window->setScreen(screens[index]); + } else { + qWarning() << Q_FUNC_INFO << "window is nullptr"; + } +} + +int Window::screenIndex() const +{ + if (!m_window) { + qWarning() << Q_FUNC_INFO << "window is nullptr"; + return -1; + } + + return qApp->screens().indexOf(m_window->screen()); +} diff --git a/src/qtcommon/Window_p.h b/src/qtcommon/Window_p.h index 40157ca28..f29b209a5 100644 --- a/src/qtcommon/Window_p.h +++ b/src/qtcommon/Window_p.h @@ -54,6 +54,9 @@ class DOCKS_EXPORT Window : public Core::Window QPoint mapFromGlobal(QPoint globalPos) const override; QPoint mapToGlobal(QPoint localPos) const override; Screen_qt::Ptr screen() const override; + void setScreen(int) override; + int screenIndex() const override; + void destroy() override; QSize minSize() const override; QSize maxSize() const override;