KConfig

kwindowconfig.cpp
1 /*
2  This file is part of the KDE libraries
3  SPDX-FileCopyrightText: 2012 Benjamin Port <[email protected]>
4 
5  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6 */
7 
8 #include "kwindowconfig.h"
9 #include "ksharedconfig.h"
10 
11 #include <QGuiApplication>
12 #include <QScreen>
13 #include <QWindow>
14 
15 // QScreen::name() returns garbage on Windows; see https://bugreports.qt.io/browse/QTBUG-74317
16 // So we use the screens' serial numbers to identify them instead
17 // FIXME: remove this once we can depend on Qt 6.4, where this is fixed
18 #if defined(Q_OS_WINDOWS) && QT_VERSION < QT_VERSION_CHECK(6, 4, 0)
19 #define SCREENNAME serialNumber
20 #else
21 #define SCREENNAME name
22 #endif
23 
24 static const char s_initialSizePropertyName[] = "_kconfig_initial_size";
25 static const char s_initialScreenSizePropertyName[] = "_kconfig_initial_screen_size";
26 
27 // Convenience function to get a space-separated list of all connected screens
28 static QString allConnectedScreens()
29 {
30  QStringList names;
31  const auto screens = QGuiApplication::screens();
32  names.reserve(screens.length());
33  for (auto screen : screens) {
34  names << screen->SCREENNAME();
35  }
36  // A string including the connector names is used in the config file key for
37  // storing per-screen-arrangement size and position data, which means we
38  // need this string to be consistent for the same screen arrangement. But
39  // connector order is non-deterministic. We need to sort the list to keep a
40  // consistent order and avoid losing multi-screen size and position data.
41  names.sort();
42  return names.join(QLatin1Char(' '));
43 }
44 
45 // Convenience function to return screen by its name from window screen siblings
46 // returns current window screen if not found
47 static QScreen *findScreenByName(const QWindow *window, const QString screenName)
48 {
49  if (screenName == window->screen()->SCREENNAME()) {
50  return window->screen();
51  }
52  for (QScreen *s : window->screen()->virtualSiblings()) {
53  if (s->SCREENNAME() == screenName) {
54  return s;
55  }
56  }
57  return window->screen();
58 }
59 
60 // Convenience function to get an appropriate config file key under which to
61 // save window size, position, or maximization information.
62 static QString configFileString(const QScreen *screen, const QString &key)
63 {
64  Q_UNUSED(screen);
65  QString returnString;
66  const int numberOfScreens = QGuiApplication::screens().length();
67 
68  if (numberOfScreens == 1) {
69  // For single-screen setups, we save data on a per-resolution basis.
70  const QRect screenGeometry = QGuiApplication::primaryScreen()->geometry();
71  returnString = QStringLiteral("%1x%2 screen: %3").arg(QString::number(screenGeometry.width()), QString::number(screenGeometry.height()), key);
72  } else {
73  // For multi-screen setups, we save data based on the number of screens.
74  // Distinguishing individual screens based on their names is unreliable
75  // due to name strings being inherently volatile.
76  returnString = QStringLiteral("%1 screens: %2").arg(QString::number(numberOfScreens), key);
77  }
78  return returnString;
79 }
80 
81 // Convenience function for "window is maximized" string
82 static QString screenMaximizedString(const QScreen *screen)
83 {
84  return configFileString(screen, QStringLiteral("Window-Maximized"));
85 }
86 // Convenience function for window width string
87 static QString windowWidthString(const QScreen *screen)
88 {
89  return configFileString(screen, QStringLiteral("Width"));
90 }
91 // Convenience function for window height string
92 static QString windowHeightString(const QScreen *screen)
93 {
94  return configFileString(screen, QStringLiteral("Height"));
95 }
96 // Convenience function for window X position string
97 static QString windowXPositionString(const QScreen *screen)
98 {
99  return configFileString(screen, QStringLiteral("XPosition"));
100 }
101 // Convenience function for window Y position string
102 static QString windowYPositionString(const QScreen *screen)
103 {
104  return configFileString(screen, QStringLiteral("YPosition"));
105 }
106 static QString windowScreenPositionString()
107 {
108  return QStringLiteral("%1").arg(allConnectedScreens());
109 }
110 
112 {
113  // QWindow::screen() shouldn't return null, but it sometimes does due to bugs.
114  if (!window || !window->screen()) {
115  return;
116  }
117  const QScreen *screen = window->screen();
118 
119  const QSize sizeToSave = window->size();
120  const bool isMaximized = window->windowState() & Qt::WindowMaximized;
121 
122  // Save size only if window is not maximized
123  if (!isMaximized) {
124  const QSize defaultSize(window->property(s_initialSizePropertyName).toSize());
125  const QSize defaultScreenSize(window->property(s_initialScreenSizePropertyName).toSize());
126  const bool sizeValid = defaultSize.isValid() && defaultScreenSize.isValid();
127  if (!sizeValid || (sizeValid && (defaultSize != sizeToSave || defaultScreenSize != screen->geometry().size()))) {
128  config.writeEntry(windowWidthString(screen), sizeToSave.width(), options);
129  config.writeEntry(windowHeightString(screen), sizeToSave.height(), options);
130  // Don't keep the maximized string in the file since the window is
131  // no longer maximized at this point
132  config.deleteEntry(screenMaximizedString(screen));
133  }
134  }
135  if ((isMaximized == false) && !config.hasDefault(screenMaximizedString(screen))) {
136  config.revertToDefault(screenMaximizedString(screen));
137  } else {
138  config.writeEntry(screenMaximizedString(screen), isMaximized, options);
139  }
140 }
141 
143 {
144  if (!window) {
145  return;
146  }
147 
148  const QString screenName = config.readEntry(windowScreenPositionString(), window->screen()->SCREENNAME());
149  const QScreen *screen = findScreenByName(window, screenName);
150 
151  // Fall back to non-per-screen-arrangement info if it's available but
152  // per-screen-arrangement information is not
153  // TODO: Remove in KF6 or maybe even KF5.80 or something. It really only needs
154  // to be here to transition existing users once they upgrade from 5.73 -> 5.74
155  const int fallbackWidth = config.readEntry(QStringLiteral("Width %1").arg(screen->geometry().width()), window->size().width());
156  const int fallbackHeight = config.readEntry(QStringLiteral("Height %1").arg(screen->geometry().height()), window->size().height());
157 
158  const int width = config.readEntry(windowWidthString(screen), fallbackWidth);
159  const int height = config.readEntry(windowHeightString(screen), fallbackHeight);
160  const bool isMaximized = config.readEntry(configFileString(screen, QStringLiteral("Window-Maximized")), false);
161 
162  // Check default size
163  const QSize defaultSize(window->property(s_initialSizePropertyName).toSize());
164  const QSize defaultScreenSize(window->property(s_initialScreenSizePropertyName).toSize());
165  if (!defaultSize.isValid() || !defaultScreenSize.isValid()) {
166  window->setProperty(s_initialSizePropertyName, window->size());
167  window->setProperty(s_initialScreenSizePropertyName, screen->geometry().size());
168  }
169 
170  // If window is maximized set maximized state and in all case set the size
171  window->resize(width, height);
172  if (isMaximized) {
174  }
175 }
176 
178 {
179  // On Wayland, the compositor is solely responsible for window positioning,
180  // So this needs to be a no-op
181  if (!window || QGuiApplication::platformName() == QLatin1String{"wayland"}) {
182  return;
183  }
184 
185  // If the window is maximized, saving the position will only serve to mis-position
186  // it once de-maximized, so let's not do that
187  if (window->windowState() & Qt::WindowMaximized) {
188  return;
189  }
190 
191  const QScreen *screen = window->screen();
192  config.writeEntry(windowXPositionString(screen), window->x(), options);
193  config.writeEntry(windowYPositionString(screen), window->y(), options);
194  config.writeEntry(windowScreenPositionString(), screen->SCREENNAME(), options);
195 }
196 
198 {
199  // On Wayland, the compositor is solely responsible for window positioning,
200  // So this needs to be a no-op
201  if (!window || QGuiApplication::platformName() == QLatin1String{"wayland"}) {
202  return;
203  }
204 
205  const QScreen *screen = window->screen();
206  const bool isMaximized = config.readEntry(configFileString(screen, QStringLiteral("Window-Maximized")), false);
207 
208  // Don't need to restore position if the window was maximized
209  if (isMaximized) {
211  return;
212  }
213 
214  // Move window to proper screen
215  const QString screenName = config.readEntry(windowScreenPositionString(), screen->SCREENNAME());
216  if (screenName != screen->SCREENNAME()) {
217  QScreen *screenConf = findScreenByName(window, screenName);
218  window->setScreen(screenConf);
219  restoreWindowScreenPosition(window, screenConf, config);
220  return;
221  }
222  restoreWindowScreenPosition(window, screen, config);
223 }
224 
225 void KWindowConfig::restoreWindowScreenPosition(QWindow *window, const QScreen *screen, const KConfigGroup &config)
226 {
227  const QRect desk = window->screen()->geometry();
228  // Fall back to non-per-resolution info if it's available but
229  // per-resolution information is not
230  // TODO: Remove in KF6 or maybe even KF5.85 or something. It really only needs
231  // to be here to transition existing users once they upgrade from 5.78 -> 5.79
232  const int fallbackXPosition = config.readEntry(QStringLiteral("%1 XPosition %2").arg(allConnectedScreens(), QString::number(desk.width())), -1);
233  const int fallbackYPosition = config.readEntry(QStringLiteral("%1 YPosition %2").arg(allConnectedScreens(), QString::number(desk.height())), -1);
234  const int xPos = config.readEntry(windowXPositionString(screen), fallbackXPosition);
235  const int yPos = config.readEntry(windowYPositionString(screen), fallbackYPosition);
236 
237  if (xPos == -1 || yPos == -1) {
238  return;
239  }
240 
241  window->setX(xPos);
242  window->setY(yPos);
243 }
KCONFIGGUI_EXPORT void restoreWindowPosition(QWindow *window, const KConfigGroup &config)
Restores the window's screen position from the configuration and calls restoreWindowScreenPosition.
bool isValid() const const
QString number(int n, int base)
QList< QScreen * > virtualSiblings() const const
QList< QScreen * > screens()
int width() const const
QScreen * screen() const const
int width() const const
Qt::WindowStates windowState() const const
void reserve(int alloc)
int height() const const
KCONFIGGUI_EXPORT void restoreWindowScreenPosition(QWindow *window, const QScreen *screen, const KConfigGroup &config)
Restores the window's position on provided screen from the configuration.
void setWindowState(Qt::WindowStates windowState)
WindowMaximized
KCONFIGGUI_EXPORT void saveWindowPosition(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options=KConfigGroup::Normal)
Saves the window's position either to the global or application config file.
QString join(const QString &separator) const const
KSharedConfigPtr config()
bool setProperty(const char *name, const QVariant &value)
void resize(int w, int h)
int height() const const
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
KCONFIGGUI_EXPORT void restoreWindowSize(QWindow *window, const KConfigGroup &config)
Restores the dialog's size from the configuration according to the screen size.
QSize toSize() const const
KJOBWIDGETS_EXPORT QWidget * window(KJob *job)
void sort(Qt::CaseSensitivity cs)
QVariant property(const char *name) const const
KCONFIGGUI_EXPORT void saveWindowSize(const QWindow *window, KConfigGroup &config, KConfigGroup::WriteConfigFlags options=KConfigGroup::Normal)
Saves the window's size dependent on the screen dimension either to the global or application config ...
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Thu Dec 7 2023 04:08:32 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.