Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
SnapState.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 fuddlesworth
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#pragma once
5
6#include <phosphorsnapengine_export.h>
8
9#include <QHash>
10#include <QJsonObject>
11#include <QObject>
12#include <QRect>
13#include <QSet>
14#include <QString>
15#include <QStringList>
16
17#include <optional>
18
19namespace PhosphorSnapEngine {
20
31class PHOSPHORSNAPENGINE_EXPORT SnapState : public QObject, public PhosphorEngine::IPlacementState
32{
33 Q_OBJECT
34
35public:
36 explicit SnapState(const QString& screenId, QObject* parent = nullptr);
37 ~SnapState() override;
38
39 SnapState(const SnapState&) = delete;
40 SnapState& operator=(const SnapState&) = delete;
41
42 // ═══════════════════════════════════════════════════════════════════════════
43 // IPlacementState
44 // ═══════════════════════════════════════════════════════════════════════════
45
46 QString screenId() const override;
47 int windowCount() const override;
48 QStringList managedWindows() const override;
49 bool containsWindow(const QString& windowId) const override;
50 bool isFloating(const QString& windowId) const override;
51 QStringList floatingWindows() const override;
52 QString placementIdForWindow(const QString& windowId) const override;
53 QJsonObject toJson() const override;
54
55 // ═══════════════════════════════════════════════════════════════════════════
56 // Zone Assignment CRUD
57 // ═══════════════════════════════════════════════════════════════════════════
58
59 void assignWindowToZone(const QString& windowId, const QString& zoneId, const QString& screenId,
60 int virtualDesktop);
61 void assignWindowToZones(const QString& windowId, const QStringList& zoneIds, const QString& screenId,
62 int virtualDesktop);
64 {
65 bool wasAssigned = false;
66 bool lastUsedZoneCleared = false;
67 };
68 UnassignResult unassignWindow(const QString& windowId);
69
70 QString zoneForWindow(const QString& windowId) const;
71 QStringList zonesForWindow(const QString& windowId) const;
72 QStringList windowsInZone(const QString& zoneId) const;
73 QStringList snappedWindows() const;
74 bool isWindowSnapped(const QString& windowId) const;
75
76 QString screenForWindow(const QString& windowId) const;
77 int desktopForWindow(const QString& windowId) const;
78
79 const QHash<QString, QString>& screenAssignments() const
80 {
81 return m_windowScreenAssignments;
82 }
83 const QHash<QString, int>& desktopAssignments() const
84 {
85 return m_windowDesktopAssignments;
86 }
87 void setScreenAssignments(const QHash<QString, QString>& s)
88 {
89 if (m_windowScreenAssignments == s) {
90 return;
91 }
92 m_windowScreenAssignments = s;
93 Q_EMIT stateChanged();
94 }
95 void setDesktopAssignments(const QHash<QString, int>& d)
96 {
97 if (m_windowDesktopAssignments == d) {
98 return;
99 }
100 m_windowDesktopAssignments = d;
101 Q_EMIT stateChanged();
102 }
103
104 // ═══════════════════════════════════════════════════════════════════════════
105 // Floating State
106 // ═══════════════════════════════════════════════════════════════════════════
107
108 void setFloating(const QString& windowId, bool floating);
109
116 void setFloatingOnScreen(const QString& windowId, const QString& screenId, int virtualDesktop);
117
119 UnassignResult unsnapForFloat(const QString& windowId);
120 QString preFloatZone(const QString& windowId) const;
121 QStringList preFloatZones(const QString& windowId) const;
122 QString preFloatScreen(const QString& windowId) const;
123 void clearPreFloatZone(const QString& windowId);
124 void addPreFloatZone(const QString& windowId, const QStringList& zoneIds);
125 void addPreFloatScreen(const QString& windowId, const QString& screenId);
126
127 const QHash<QString, QStringList>& preFloatZoneAssignments() const
128 {
129 return m_preFloatZoneAssignments;
130 }
131 const QHash<QString, QString>& preFloatScreenAssignments() const
132 {
133 return m_preFloatScreenAssignments;
134 }
135 void setPreFloatScreenAssignments(const QHash<QString, QString>& a)
136 {
137 if (m_preFloatScreenAssignments == a) {
138 return;
139 }
140 m_preFloatScreenAssignments = a;
141 Q_EMIT stateChanged();
142 }
143
144 // ═══════════════════════════════════════════════════════════════════════════
145 // Window Lifecycle
146 // ═══════════════════════════════════════════════════════════════════════════
147
148 void windowClosed(const QString& windowId);
149 bool isEmpty() const;
150 void clear();
151
152 // ═══════════════════════════════════════════════════════════════════════════
153 // Rotation
154 // ═══════════════════════════════════════════════════════════════════════════
155
158 QStringList rotateAssignments(bool clockwise);
159
160 // ═══════════════════════════════════════════════════════════════════════════
161 // Last-Used Zone Tracking
162 // ═══════════════════════════════════════════════════════════════════════════
163
165 void updateLastUsedZone(const QString& zoneId, const QString& screenId, const QString& windowClass,
166 int virtualDesktop);
167
169 void restoreLastUsedZone(const QString& zoneId, const QString& screenId, const QString& zoneClass, int desktop);
170
171 QString lastUsedZoneId() const
172 {
173 return m_lastUsedZoneId;
174 }
175 QString lastUsedScreenId() const
176 {
177 return m_lastUsedScreenId;
178 }
179 QString lastUsedZoneClass() const
180 {
181 return m_lastUsedZoneClass;
182 }
183 int lastUsedDesktop() const
184 {
185 return m_lastUsedDesktop;
186 }
187 void retagLastUsedZoneClass(const QString& newClass)
188 {
189 if (m_lastUsedZoneClass == newClass) {
190 return;
191 }
192 m_lastUsedZoneClass = newClass;
193 Q_EMIT stateChanged();
194 }
195
196 // ═══════════════════════════════════════════════════════════════════════════
197 // Auto-Snap Bookkeeping
198 // ═══════════════════════════════════════════════════════════════════════════
199
200 void recordSnapIntent(const QString& windowClass, bool wasUserInitiated);
201 const QSet<QString>& userSnappedClasses() const
202 {
203 return m_userSnappedClasses;
204 }
205 void setUserSnappedClasses(const QSet<QString>& classes)
206 {
207 if (m_userSnappedClasses == classes) {
208 return;
209 }
210 m_userSnappedClasses = classes;
211 Q_EMIT stateChanged();
212 }
213
214 void markAsAutoSnapped(const QString& windowId);
215 bool isAutoSnapped(const QString& windowId) const;
216 bool clearAutoSnapped(const QString& windowId);
217
218 // ═══════════════════════════════════════════════════════════════════════════
219 // Occupied Zone Queries
220 // ═══════════════════════════════════════════════════════════════════════════
221
225 QSet<QString> buildOccupiedZoneSet(const QString& screenFilter = {}, int desktopFilter = 0) const;
226
228 int pruneStaleAssignments(const QSet<QString>& aliveWindowIds);
229
230 // ═══════════════════════════════════════════════════════════════════════════
231 // Deserialization
232 // ═══════════════════════════════════════════════════════════════════════════
233
234 static SnapState* fromJson(const QJsonObject& json, QObject* parent = nullptr);
235
236 // ═══════════════════════════════════════════════════════════════════════════
237 // State Access (for persistence layer)
238 // ═══════════════════════════════════════════════════════════════════════════
239
240 const QHash<QString, QStringList>& zoneAssignments() const
241 {
242 return m_windowZoneAssignments;
243 }
244
245 void setZoneAssignments(const QHash<QString, QStringList>& zones)
246 {
247 if (m_windowZoneAssignments == zones) {
248 return;
249 }
250 m_windowZoneAssignments = zones;
251 Q_EMIT stateChanged();
252 }
253 void setFloatingWindows(const QSet<QString>& windows)
254 {
255 if (m_floatingWindows == windows) {
256 return;
257 }
258 m_floatingWindows = windows;
259 Q_EMIT stateChanged();
260 }
261 void setPreFloatZoneAssignments(const QHash<QString, QStringList>& a)
262 {
263 if (m_preFloatZoneAssignments == a) {
264 return;
265 }
266 m_preFloatZoneAssignments = a;
267 Q_EMIT stateChanged();
268 }
269
270Q_SIGNALS:
271 void windowAssigned(const QString& windowId, const QString& zoneId);
272 void windowUnassigned(const QString& windowId);
273 void floatingChanged(const QString& windowId, bool floating);
275
276private:
277 bool removeWindowData(const QString& windowId);
278
286 UnassignResult clearZoneAssignment(const QString& windowId, bool preserveScreenAndDesktop);
287
288 QSet<QString> allManagedWindowIds() const
289 {
290 QSet<QString> all;
291 all.reserve(m_windowZoneAssignments.size() + m_floatingWindows.size() + m_autoSnappedWindows.size());
292 for (auto it = m_windowZoneAssignments.constBegin(); it != m_windowZoneAssignments.constEnd(); ++it) {
293 all.insert(it.key());
294 }
295 all.unite(m_floatingWindows);
296 all.unite(m_autoSnappedWindows);
297 return all;
298 }
299
300 QString m_screenId;
301
302 QHash<QString, QStringList> m_windowZoneAssignments;
303 QHash<QString, QString> m_windowScreenAssignments;
304 QHash<QString, int> m_windowDesktopAssignments;
305 QSet<QString> m_floatingWindows;
306 QHash<QString, QStringList> m_preFloatZoneAssignments;
307 QHash<QString, QString> m_preFloatScreenAssignments;
308
309 QString m_lastUsedZoneId;
310 QString m_lastUsedScreenId;
311 QString m_lastUsedZoneClass;
312 int m_lastUsedDesktop = 0;
313
314 QSet<QString> m_userSnappedClasses;
315 QSet<QString> m_autoSnappedWindows;
316};
317
318} // namespace PhosphorSnapEngine
Per-screen placement state contract.
Definition IPlacementState.h:26
Per-screen snap placement state.
Definition SnapState.h:32
void setFloatingOnScreen(const QString &windowId, const QString &screenId, int virtualDesktop)
Mark a window floating AND record its screen+desktop without a zone.
void setUserSnappedClasses(const QSet< QString > &classes)
Definition SnapState.h:205
SnapState(const QString &screenId, QObject *parent=nullptr)
void windowClosed(const QString &windowId)
QString preFloatScreen(const QString &windowId) const
bool isFloating(const QString &windowId) const override
Whether the window is floating (excluded from placement).
void markAsAutoSnapped(const QString &windowId)
void assignWindowToZones(const QString &windowId, const QStringList &zoneIds, const QString &screenId, int virtualDesktop)
QStringList zonesForWindow(const QString &windowId) const
QString lastUsedZoneClass() const
Definition SnapState.h:179
SnapState(const SnapState &)=delete
UnassignResult unassignWindow(const QString &windowId)
void setFloatingWindows(const QSet< QString > &windows)
Definition SnapState.h:253
QStringList managedWindows() const override
All windows managed by this state (tiled + floating).
void setScreenAssignments(const QHash< QString, QString > &s)
Definition SnapState.h:87
QString screenId() const override
Screen this state object manages.
QStringList rotateAssignments(bool clockwise)
Rotate zone assignments: each window moves to the next/previous zone in zone-number order.
bool containsWindow(const QString &windowId) const override
Whether the window is in this state's managed set.
QString preFloatZone(const QString &windowId) const
void restoreLastUsedZone(const QString &zoneId, const QString &screenId, const QString &zoneClass, int desktop)
Restore last-used zone fields from persistence without emitting stateChanged.
const QSet< QString > & userSnappedClasses() const
Definition SnapState.h:201
UnassignResult unsnapForFloat(const QString &windowId)
Save zone assignment before floating for later restore.
void clearPreFloatZone(const QString &windowId)
QString lastUsedScreenId() const
Definition SnapState.h:175
QSet< QString > buildOccupiedZoneSet(const QString &screenFilter={}, int desktopFilter=0) const
Build the set of zone IDs currently occupied by snapped windows.
void addPreFloatZone(const QString &windowId, const QStringList &zoneIds)
const QHash< QString, int > & desktopAssignments() const
Definition SnapState.h:83
int pruneStaleAssignments(const QSet< QString > &aliveWindowIds)
Remove zone/screen/desktop assignments for windows not in the alive set.
void retagLastUsedZoneClass(const QString &newClass)
Definition SnapState.h:187
const QHash< QString, QStringList > & preFloatZoneAssignments() const
Definition SnapState.h:127
QStringList snappedWindows() const
QJsonObject toJson() const override
Serialize to JSON for session persistence.
void setPreFloatScreenAssignments(const QHash< QString, QString > &a)
Definition SnapState.h:135
bool isWindowSnapped(const QString &windowId) const
QStringList windowsInZone(const QString &zoneId) const
QStringList preFloatZones(const QString &windowId) const
void addPreFloatScreen(const QString &windowId, const QString &screenId)
QString placementIdForWindow(const QString &windowId) const override
Opaque placement identifier for the window's current slot.
QString lastUsedZoneId() const
Definition SnapState.h:171
void recordSnapIntent(const QString &windowClass, bool wasUserInitiated)
void updateLastUsedZone(const QString &zoneId, const QString &screenId, const QString &windowClass, int virtualDesktop)
Update last-used zone and emit stateChanged.
QString zoneForWindow(const QString &windowId) const
const QHash< QString, QString > & screenAssignments() const
Definition SnapState.h:79
void assignWindowToZone(const QString &windowId, const QString &zoneId, const QString &screenId, int virtualDesktop)
void windowAssigned(const QString &windowId, const QString &zoneId)
void setZoneAssignments(const QHash< QString, QStringList > &zones)
Definition SnapState.h:245
bool clearAutoSnapped(const QString &windowId)
SnapState & operator=(const SnapState &)=delete
int desktopForWindow(const QString &windowId) const
QStringList floatingWindows() const override
All currently-floating windows.
int windowCount() const override
Total number of managed windows (tiled + floating).
static SnapState * fromJson(const QJsonObject &json, QObject *parent=nullptr)
void setFloating(const QString &windowId, bool floating)
void floatingChanged(const QString &windowId, bool floating)
QString screenForWindow(const QString &windowId) const
const QHash< QString, QString > & preFloatScreenAssignments() const
Definition SnapState.h:131
void windowUnassigned(const QString &windowId)
int lastUsedDesktop() const
Definition SnapState.h:183
const QHash< QString, QStringList > & zoneAssignments() const
Definition SnapState.h:240
void setPreFloatZoneAssignments(const QHash< QString, QStringList > &a)
Definition SnapState.h:261
void setDesktopAssignments(const QHash< QString, int > &d)
Definition SnapState.h:95
bool isAutoSnapped(const QString &windowId) const
Definition WindowTrackingService.h:37