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>
9
10#include <QHash>
11#include <QObject>
12#include <QSet>
13#include <QString>
14#include <QStringList>
15
16#include <optional>
17
18namespace PhosphorSnapEngine {
19
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
51 {
52 m_windowRegistry = registry;
53 }
54
55 // ═══════════════════════════════════════════════════════════════════════════
56 // IPlacementState
57 // ═══════════════════════════════════════════════════════════════════════════
58
59 QString screenId() const override;
60 int windowCount() const override;
61 QStringList managedWindows() const override;
62 bool containsWindow(const QString& windowId) const override;
63 bool isFloating(const QString& windowId) const override;
64 QStringList floatingWindows() const override;
65 QString placementIdForWindow(const QString& windowId) const override;
66
67 // ═══════════════════════════════════════════════════════════════════════════
68 // Zone Assignment CRUD
69 // ═══════════════════════════════════════════════════════════════════════════
70
71 void assignWindowToZone(const QString& windowId, const QString& zoneId, const QString& screenId,
72 int virtualDesktop);
73 void assignWindowToZones(const QString& windowId, const QStringList& zoneIds, const QString& screenId,
74 int virtualDesktop);
76 {
77 bool wasAssigned = false;
78 bool lastUsedZoneCleared = false;
79 };
80 UnassignResult unassignWindow(const QString& windowId);
93 bool clearScreenAndDesktop(const QString& windowId);
94
95 QString zoneForWindow(const QString& windowId) const;
96 QStringList zonesForWindow(const QString& windowId) const;
97 QStringList windowsInZone(const QString& zoneId) const;
98 QStringList snappedWindows() const;
99 bool isWindowSnapped(const QString& windowId) const;
100
117 void noteFocused(const QString& windowId);
118 QString lastSnappedFocus() const
119 {
120 return m_lastSnappedFocus;
121 }
122 QString lastFloatingFocus() const
123 {
124 return m_lastFloatingFocus;
125 }
126
127 QString screenForWindow(const QString& windowId) const;
128
141 void recordResidence(const QString& windowId, const QString& screenId, int virtualDesktop);
142 int desktopForWindow(const QString& windowId) const;
143
152 bool reassignDesktop(const QString& windowId, int virtualDesktop);
153
160 QStringList windowsOnScreenAndDesktop(const QString& screenId, int virtualDesktop) const;
161
162 const QHash<QString, QString>& screenAssignments() const
163 {
164 return m_windowScreenAssignments;
165 }
166 const QHash<QString, int>& desktopAssignments() const
167 {
168 return m_windowDesktopAssignments;
169 }
170 void setScreenAssignments(const QHash<QString, QString>& s)
171 {
172 if (m_windowScreenAssignments == s) {
173 return;
174 }
175 m_windowScreenAssignments = s;
176 Q_EMIT stateChanged();
177 }
178
179 // ═══════════════════════════════════════════════════════════════════════════
180 // Floating State
181 // ═══════════════════════════════════════════════════════════════════════════
182
183 void setFloating(const QString& windowId, bool floating);
184
191 void setFloatingOnScreen(const QString& windowId, const QString& screenId, int virtualDesktop);
192
194 UnassignResult unsnapForFloat(const QString& windowId);
195 QString preFloatZone(const QString& windowId) const;
196 QStringList preFloatZones(const QString& windowId) const;
197 QString preFloatScreen(const QString& windowId) const;
200 bool clearPreFloatZone(const QString& windowId);
201 void addPreFloatZone(const QString& windowId, const QStringList& zoneIds);
202 void addPreFloatScreen(const QString& windowId, const QString& screenId);
203
204 const QHash<QString, QStringList>& preFloatZoneAssignments() const
205 {
206 return m_preFloatZoneAssignments;
207 }
208 const QHash<QString, QString>& preFloatScreenAssignments() const
209 {
210 return m_preFloatScreenAssignments;
211 }
212 void setPreFloatScreenAssignments(const QHash<QString, QString>& a)
213 {
214 if (m_preFloatScreenAssignments == a) {
215 return;
216 }
217 m_preFloatScreenAssignments = a;
218 Q_EMIT stateChanged();
219 }
220
221 // ═══════════════════════════════════════════════════════════════════════════
222 // Window Lifecycle
223 // ═══════════════════════════════════════════════════════════════════════════
224
225 void windowClosed(const QString& windowId);
226 bool isEmpty() const;
227 void clear();
228
239 void migrateWindowTo(SnapState* target, const QString& windowId, const QString& newScreenId);
240
250 bool removeWindowData(const QString& windowId);
251
252 // ═══════════════════════════════════════════════════════════════════════════
253 // Last-Used Zone Tracking
254 //
255 // Each per-(screen,desktop,activity) store tracks its OWN last-used zone, so a
256 // window opening on monitor A only ever restores to a zone A was last snapped to
257 // (never monitor B's). The on-disk `LastUsedZoneId` persists a single id; the
258 // facade picks the representative store by lastUsedSeq() (most-recently updated).
259 // ═══════════════════════════════════════════════════════════════════════════
260
262 void updateLastUsedZone(const QString& zoneId, const QString& screenId, const QString& windowClass,
263 int virtualDesktop);
264
266 void restoreLastUsedZone(const QString& zoneId, const QString& screenId, const QString& zoneClass, int desktop);
267
268 QString lastUsedZoneId() const
269 {
270 return m_lastUsedZoneId;
271 }
272 QString lastUsedScreenId() const
273 {
274 return m_lastUsedScreenId;
275 }
276 QString lastUsedZoneClass() const
277 {
278 return m_lastUsedZoneClass;
279 }
280 int lastUsedDesktop() const
281 {
282 return m_lastUsedDesktop;
283 }
288 quint64 lastUsedSeq() const
289 {
290 return m_lastUsedSeq;
291 }
292 void retagLastUsedZoneClass(const QString& newClass)
293 {
294 if (m_lastUsedZoneClass == newClass) {
295 return;
296 }
297 m_lastUsedZoneClass = newClass;
298 Q_EMIT stateChanged();
299 }
300
301 // ═══════════════════════════════════════════════════════════════════════════
302 // Auto-Snap Bookkeeping
303 // ═══════════════════════════════════════════════════════════════════════════
304
305 void recordSnapIntent(const QString& windowClass, bool wasUserInitiated);
306 const QSet<QString>& userSnappedClasses() const
307 {
308 return m_userSnappedClasses;
309 }
310 void setUserSnappedClasses(const QSet<QString>& classes)
311 {
312 if (m_userSnappedClasses == classes) {
313 return;
314 }
315 m_userSnappedClasses = classes;
316 Q_EMIT stateChanged();
317 }
318
319 void markAsAutoSnapped(const QString& windowId);
320 bool isAutoSnapped(const QString& windowId) const;
321 bool clearAutoSnapped(const QString& windowId);
322
323 // ═══════════════════════════════════════════════════════════════════════════
324 // Occupied Zone Queries
325 // ═══════════════════════════════════════════════════════════════════════════
326
330 QSet<QString> buildOccupiedZoneSet(const QString& screenFilter = {}, int desktopFilter = 0) const;
331
333 int pruneStaleAssignments(const QSet<QString>& aliveWindowIds);
334
335 // ═══════════════════════════════════════════════════════════════════════════
336 // State Access (for persistence layer)
337 // ═══════════════════════════════════════════════════════════════════════════
338
339 const QHash<QString, QStringList>& zoneAssignments() const
340 {
341 return m_windowZoneAssignments;
342 }
343
344Q_SIGNALS:
345 void windowAssigned(const QString& windowId, const QString& zoneId);
346 void windowUnassigned(const QString& windowId);
347 void floatingChanged(const QString& windowId, bool floating);
349
350private:
358 QString canonicalizeForLookup(const QString& rawWindowId) const;
359
367 UnassignResult clearZoneAssignment(const QString& windowId, bool preserveScreenAndDesktop);
368
369 QSet<QString> allManagedWindowIds() const
370 {
371 QSet<QString> all;
372 all.reserve(m_windowZoneAssignments.size() + m_floatingWindows.size() + m_autoSnappedWindows.size());
373 for (auto it = m_windowZoneAssignments.constBegin(); it != m_windowZoneAssignments.constEnd(); ++it) {
374 all.insert(it.key());
375 }
376 all.unite(m_floatingWindows);
377 all.unite(m_autoSnappedWindows);
378 return all;
379 }
380
381 QString m_screenId;
382
384 PhosphorEngine::IWindowRegistry* m_windowRegistry = nullptr;
385
386 QHash<QString, QStringList> m_windowZoneAssignments;
387 QHash<QString, QString> m_windowScreenAssignments;
388 QHash<QString, int> m_windowDesktopAssignments;
389 QSet<QString> m_floatingWindows;
390 QHash<QString, QStringList> m_preFloatZoneAssignments;
391 QHash<QString, QString> m_preFloatScreenAssignments;
392
393 QString m_lastSnappedFocus;
394 QString m_lastFloatingFocus;
395
396 QString m_lastUsedZoneId;
397 QString m_lastUsedScreenId;
398 QString m_lastUsedZoneClass;
399 int m_lastUsedDesktop = 0;
401 quint64 m_lastUsedSeq = 0;
402
403 QSet<QString> m_userSnappedClasses;
404 QSet<QString> m_autoSnappedWindows;
405};
406
407} // namespace PhosphorSnapEngine
Per-screen placement state contract.
Definition IPlacementState.h:28
Definition IWindowRegistry.h:14
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:310
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 lastSnappedFocus() const
Definition SnapState.h:118
QString lastUsedZoneClass() const
Definition SnapState.h:276
bool clearPreFloatZone(const QString &windowId)
Returns true when an entry was actually removed, so callers can gate their dirty-marking on a real mu...
SnapState(const SnapState &)=delete
UnassignResult unassignWindow(const QString &windowId)
QStringList managedWindows() const override
All windows managed by this state (tiled + floating).
bool removeWindowData(const QString &windowId)
Remove ALL of windowId's per-window data from this store (zone/screen/ desktop assignment,...
void setScreenAssignments(const QHash< QString, QString > &s)
Definition SnapState.h:170
QString screenId() const override
Screen this state object manages.
bool containsWindow(const QString &windowId) const override
Whether the window is in this state's managed set.
bool clearScreenAndDesktop(const QString &windowId)
Drop ONLY the screen/desktop residence entries, leaving the zone assignment, the floating bit and the...
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:306
UnassignResult unsnapForFloat(const QString &windowId)
Save zone assignment before floating for later restore.
void setWindowRegistry(PhosphorEngine::IWindowRegistry *registry)
Attach the daemon's shared window registry so every windowId-keyed accessor can canonicalize the inco...
Definition SnapState.h:50
void noteFocused(const QString &windowId)
Layer-side focus memory for switch-focus-between-floating-and-tiling.
QStringList windowsOnScreenAndDesktop(const QString &screenId, int virtualDesktop) const
Windows with a recorded desktop assignment (snapped, or floated-on-screen via setFloatingOnScreen / u...
QString lastUsedScreenId() const
Definition SnapState.h:272
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:166
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:292
const QHash< QString, QStringList > & preFloatZoneAssignments() const
Definition SnapState.h:204
QStringList snappedWindows() const
quint64 lastUsedSeq() const
Monotonic stamp bumped every time this store's last-used zone is set to a non-empty value (via update...
Definition SnapState.h:288
void setPreFloatScreenAssignments(const QHash< QString, QString > &a)
Definition SnapState.h:212
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)
void migrateWindowTo(SnapState *target, const QString &windowId, const QString &newScreenId)
Move windowId's per-window placement entries (zone assignment, live screen, desktop,...
QString placementIdForWindow(const QString &windowId) const override
Opaque placement identifier for the window's current slot.
QString lastUsedZoneId() const
Definition SnapState.h:268
bool reassignDesktop(const QString &windowId, int virtualDesktop)
Re-stamp a snapped window's virtual-desktop membership to virtualDesktop, keeping its zone and screen...
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:162
void assignWindowToZone(const QString &windowId, const QString &zoneId, const QString &screenId, int virtualDesktop)
void windowAssigned(const QString &windowId, const QString &zoneId)
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).
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:208
void windowUnassigned(const QString &windowId)
int lastUsedDesktop() const
Definition SnapState.h:280
void recordResidence(const QString &windowId, const QString &screenId, int virtualDesktop)
Record screen + desktop residence WITHOUT a zone assignment and without touching the floating set.
const QHash< QString, QStringList > & zoneAssignments() const
Definition SnapState.h:339
QString lastFloatingFocus() const
Definition SnapState.h:122
bool isAutoSnapped(const QString &windowId) const
Definition SnapStateResolver.h:11