Phosphor
Qt6 / Wayland library suite for window-management tools
 
Loading...
Searching...
No Matches
Layout.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
9#include <phosphorzones_export.h>
10#include <QObject>
11#include <QVariantMap>
12#include <QVector>
13#include <QUuid>
14#include <QString>
15#include <QJsonArray>
16#include <QJsonObject>
17#include <functional>
18#include <memory>
19
20namespace PhosphorZones {
21
27enum class LayoutCategory {
28 Manual = 0,
29 Autotile = 1
30};
31
39class PHOSPHORZONES_EXPORT Layout : public QObject
40{
41 Q_OBJECT
42
43 Q_PROPERTY(QUuid id READ id CONSTANT)
44 Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
45 Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged)
46 Q_PROPERTY(int zonePadding READ zonePadding WRITE setZonePadding NOTIFY zonePaddingChanged)
47 Q_PROPERTY(int outerGap READ outerGap WRITE setOuterGap NOTIFY outerGapChanged)
48 Q_PROPERTY(bool hasZonePaddingOverride READ hasZonePaddingOverride NOTIFY zonePaddingChanged)
49 Q_PROPERTY(bool hasOuterGapOverride READ hasOuterGapOverride NOTIFY outerGapChanged)
50 Q_PROPERTY(bool usePerSideOuterGap READ usePerSideOuterGap WRITE setUsePerSideOuterGap NOTIFY outerGapChanged)
51 Q_PROPERTY(int outerGapTop READ outerGapTop WRITE setOuterGapTop NOTIFY outerGapChanged)
52 Q_PROPERTY(int outerGapBottom READ outerGapBottom WRITE setOuterGapBottom NOTIFY outerGapChanged)
53 Q_PROPERTY(int outerGapLeft READ outerGapLeft WRITE setOuterGapLeft NOTIFY outerGapChanged)
54 Q_PROPERTY(int outerGapRight READ outerGapRight WRITE setOuterGapRight NOTIFY outerGapChanged)
55 Q_PROPERTY(bool hasPerSideOuterGapOverride READ hasPerSideOuterGapOverride NOTIFY outerGapChanged)
56 Q_PROPERTY(bool showZoneNumbers READ showZoneNumbers WRITE setShowZoneNumbers NOTIFY showZoneNumbersChanged)
57 Q_PROPERTY(
58 int overlayDisplayMode READ overlayDisplayMode WRITE setOverlayDisplayMode NOTIFY overlayDisplayModeChanged)
59 Q_PROPERTY(bool hasOverlayDisplayModeOverride READ hasOverlayDisplayModeOverride NOTIFY overlayDisplayModeChanged)
60 Q_PROPERTY(int zoneCount READ zoneCount NOTIFY zonesChanged)
61 Q_PROPERTY(QString sourcePath READ sourcePath WRITE setSourcePath NOTIFY sourcePathChanged)
62 Q_PROPERTY(bool isSystemLayout READ isSystemLayout NOTIFY sourcePathChanged)
63 Q_PROPERTY(QString shaderId READ shaderId WRITE setShaderId NOTIFY shaderIdChanged)
64 Q_PROPERTY(QVariantMap shaderParams READ shaderParams WRITE setShaderParams NOTIFY shaderParamsChanged)
65
66 // Auto-assign: new windows fill first empty zone
67 Q_PROPERTY(bool autoAssign READ autoAssign WRITE setAutoAssign NOTIFY autoAssignChanged)
68
69 // Geometry mode: use full screen or available (panel-excluded) geometry
70 Q_PROPERTY(bool useFullScreenGeometry READ useFullScreenGeometry WRITE setUseFullScreenGeometry NOTIFY
71 useFullScreenGeometryChanged)
72
73 // Aspect ratio classification
74 Q_PROPERTY(
75 int aspectRatioClass READ aspectRatioClassInt WRITE setAspectRatioClassInt NOTIFY aspectRatioClassChanged)
76 Q_PROPERTY(qreal minAspectRatio READ minAspectRatio WRITE setMinAspectRatio NOTIFY aspectRatioClassChanged)
77 Q_PROPERTY(qreal maxAspectRatio READ maxAspectRatio WRITE setMaxAspectRatio NOTIFY aspectRatioClassChanged)
78
79 // Visibility filtering
80 Q_PROPERTY(
81 bool hiddenFromSelector READ hiddenFromSelector WRITE setHiddenFromSelector NOTIFY hiddenFromSelectorChanged)
82 Q_PROPERTY(QStringList allowedScreens READ allowedScreens WRITE setAllowedScreens NOTIFY allowedScreensChanged)
83 Q_PROPERTY(QList<int> allowedDesktops READ allowedDesktops WRITE setAllowedDesktops NOTIFY allowedDesktopsChanged)
84 Q_PROPERTY(
85 QStringList allowedActivities READ allowedActivities WRITE setAllowedActivities NOTIFY allowedActivitiesChanged)
86
87public:
88 explicit Layout(QObject* parent = nullptr);
89 explicit Layout(const QString& name, QObject* parent = nullptr);
90 Layout(const Layout& other);
91 ~Layout() override;
92
93 Layout& operator=(const Layout& other);
94
95 // Identification
96 QUuid id() const
97 {
98 return m_id;
99 }
100 QString name() const
101 {
102 return m_name;
103 }
104 void setName(const QString& name);
105
106 QString description() const
107 {
108 return m_description;
109 }
110 void setDescription(const QString& description);
111
112 // Layout settings (per-layout gap overrides, -1 = use global setting)
113 int zonePadding() const
114 {
115 return m_zonePadding;
116 }
117 void setZonePadding(int padding);
119 {
120 return m_zonePadding >= 0;
121 }
123
124 int outerGap() const
125 {
126 return m_outerGap;
127 }
128 void setOuterGap(int gap);
130 {
131 return m_outerGap >= 0;
132 }
134
135 // Per-side outer gap overrides (-1 = use global setting)
137 {
138 return m_usePerSideOuterGap;
139 }
140 void setUsePerSideOuterGap(bool enabled);
141 int outerGapTop() const
142 {
143 return m_outerGapTop;
144 }
145 void setOuterGapTop(int gap);
146 int outerGapBottom() const
147 {
148 return m_outerGapBottom;
149 }
150 void setOuterGapBottom(int gap);
151 int outerGapLeft() const
152 {
153 return m_outerGapLeft;
154 }
155 void setOuterGapLeft(int gap);
156 int outerGapRight() const
157 {
158 return m_outerGapRight;
159 }
160 void setOuterGapRight(int gap);
162 {
163 return m_usePerSideOuterGap
164 && (m_outerGapTop >= 0 || m_outerGapBottom >= 0 || m_outerGapLeft >= 0 || m_outerGapRight >= 0);
165 }
172 {
173 return {m_outerGapTop, m_outerGapBottom, m_outerGapLeft, m_outerGapRight};
174 }
175
176 bool showZoneNumbers() const
177 {
178 return m_showZoneNumbers;
179 }
180 void setShowZoneNumbers(bool show);
181
183 {
184 return m_overlayDisplayMode;
185 }
186 void setOverlayDisplayMode(int mode);
188 {
189 return m_overlayDisplayMode >= 0;
190 }
192
193 // Source path tracking - determines if layout is from system or user directory
194 QString sourcePath() const
195 {
196 return m_sourcePath;
197 }
198 void setSourcePath(const QString& path);
199
200 // Returns true if layout was loaded from a system directory (not user's .local)
201 // This determines whether the layout can be edited/deleted in place
202 bool isSystemLayout() const;
203
204 // Original system layout path — set when a user override replaces a system layout.
205 // Persisted in user JSON so deletion can restore the system original without scanning.
206 QString systemSourcePath() const
207 {
208 return m_systemSourcePath;
209 }
210 void setSystemSourcePath(const QString& path)
211 {
212 m_systemSourcePath = path;
213 }
214 bool hasSystemOrigin() const
215 {
216 return !m_systemSourcePath.isEmpty();
217 }
218
219 // Shader support
220 QString shaderId() const
221 {
222 return m_shaderId;
223 }
224 void setShaderId(const QString& id);
225 QVariantMap shaderParams() const
226 {
227 return m_shaderParams;
228 }
229 void setShaderParams(const QVariantMap& params);
230
231 // Aspect ratio classification
233 {
234 return m_aspectRatioClass;
235 }
238 {
239 return static_cast<int>(m_aspectRatioClass);
240 }
242 qreal minAspectRatio() const
243 {
244 return m_minAspectRatio;
245 }
246 void setMinAspectRatio(qreal ratio);
247 qreal maxAspectRatio() const
248 {
249 return m_maxAspectRatio;
250 }
251 void setMaxAspectRatio(qreal ratio);
252
258 bool matchesAspectRatio(qreal screenAspectRatio) const;
259
260 // Visibility filtering
262 {
263 return m_hiddenFromSelector;
264 }
265 void setHiddenFromSelector(bool hidden);
266 QStringList allowedScreens() const
267 {
268 return m_allowedScreens;
269 }
270 void setAllowedScreens(const QStringList& screens);
271 QList<int> allowedDesktops() const
272 {
273 return m_allowedDesktops;
274 }
275 void setAllowedDesktops(const QList<int>& desktops);
276 QStringList allowedActivities() const
277 {
278 return m_allowedActivities;
279 }
280 void setAllowedActivities(const QStringList& activities);
281
282 // Auto-assign: new windows fill first empty zone
283 bool autoAssign() const
284 {
285 return m_autoAssign;
286 }
287 void setAutoAssign(bool enabled);
288
289 // Geometry mode: when true, zones span the full screen including panel/taskbar areas
291 {
292 return m_useFullScreenGeometry;
293 }
294 void setUseFullScreenGeometry(bool enabled);
295
298
301 QRectF fixedZoneBoundingBox() const;
302
311
312 // Optional load order for "default" layout when defaultLayoutId is not set (lower = first)
313 int defaultOrder() const
314 {
315 return m_defaultOrder;
316 }
317 void setDefaultOrder(int order)
318 {
319 m_defaultOrder = order;
320 }
321
322 // Zone management
323 int zoneCount() const
324 {
325 return m_zones.size();
326 }
327 QVector<Zone*> zones() const
328 {
329 return m_zones;
330 }
331 Zone* zone(int index) const;
332 Zone* zoneById(const QUuid& id) const;
333 Zone* zoneByNumber(int number) const;
334
335 Q_INVOKABLE void addZone(Zone* zone);
336 Q_INVOKABLE void removeZone(Zone* zone);
337 Q_INVOKABLE void removeZoneAt(int index);
338 Q_INVOKABLE void clearZones();
339 Q_INVOKABLE void moveZone(int fromIndex, int toIndex);
340
341 // Zone detection
342 Q_INVOKABLE Zone* zoneAtPoint(const QPointF& point) const;
343 Q_INVOKABLE Zone* nearestZone(const QPointF& point, qreal maxDistance = -1) const;
344 Q_INVOKABLE QVector<Zone*> zonesInRect(const QRectF& rect) const;
345 Q_INVOKABLE QVector<Zone*> adjacentZones(const QPointF& point, qreal threshold = 20) const;
346
347 // Geometry calculations
348 Q_INVOKABLE void renumberZones();
349 QRectF lastRecalcGeometry() const
350 {
351 return m_lastRecalcGeometry;
352 }
353 void setLastRecalcGeometry(const QRectF& geom)
354 {
355 m_lastRecalcGeometry = geom;
356 }
357
358 // Serialization
359 QJsonObject toJson() const;
360 static Layout* fromJson(const QJsonObject& json, QObject* parent = nullptr);
361
374 using ScreenIdResolver = std::function<QString(const QString&)>;
382
383 // Predefined layouts (templates)
384 static Layout* createColumnsLayout(int columns, QObject* parent = nullptr);
385 static Layout* createRowsLayout(int rows, QObject* parent = nullptr);
386 static Layout* createGridLayout(int columns, int rows, QObject* parent = nullptr);
387 static Layout* createPriorityGridLayout(QObject* parent = nullptr);
388 static Layout* createFocusLayout(QObject* parent = nullptr);
389
390 // Dirty tracking for copy-on-write saving
391 bool isDirty() const
392 {
393 return m_dirty;
394 }
396 {
397 m_dirty = true;
398 }
400 {
401 m_dirty = false;
402 }
405
406Q_SIGNALS:
424 void zoneAdded(Zone* zone);
425 void zoneRemoved(Zone* zone);
427
428public:
437 void recalculateZoneGeometries(const QRectF& screenGeometry);
438
439private:
440 void emitModifiedIfNotBatched();
441
448 void initFromJson(const QJsonObject& json);
449
450 QUuid m_id;
451 QString m_name;
452 QString m_description;
453 int m_zonePadding = -1; // -1 = use global setting
454 int m_outerGap = -1; // -1 = use global setting
455 bool m_usePerSideOuterGap = false;
456 int m_outerGapTop = -1; // -1 = use global setting
457 int m_outerGapBottom = -1;
458 int m_outerGapLeft = -1;
459 int m_outerGapRight = -1;
460 bool m_showZoneNumbers = true;
461 int m_overlayDisplayMode = -1; // -1 = use global setting
462 QString m_sourcePath; // Path where layout was loaded from (empty for new layouts)
463 QString m_systemSourcePath; // Original system path if this is a user override of a system layout
464 int m_defaultOrder = 999; // Optional: lower values appear first when choosing default (999 = not set)
465 QVector<Zone*> m_zones;
466
467 // Auto-assign: new windows fill first empty zone
468 bool m_autoAssign = false;
469
470 // Geometry mode: zones use full screen (true) or available area excluding panels (false)
471 bool m_useFullScreenGeometry = false;
472
473 // Shader support
474 QString m_shaderId; // Shader effect ID (empty = no shader)
475 QVariantMap m_shaderParams; // Shader-specific parameters
476
477 // Aspect ratio classification
479 qreal m_minAspectRatio = 0.0; // 0 = not set (use class matching)
480 qreal m_maxAspectRatio = 0.0; // 0 = not set (use class matching)
481
482 // Visibility filtering
483 bool m_hiddenFromSelector = false;
484 QStringList m_allowedScreens; // empty = all screens
485 QList<int> m_allowedDesktops; // empty = all desktops
486 QStringList m_allowedActivities; // empty = all activities
487
488 // Cache last geometry used for recalculation to avoid redundant work
489 mutable QRectF m_lastRecalcGeometry;
490
491 // Cached classification derived from m_sourcePath. Recomputed in
492 // setSourcePath so isSystemLayout() is O(1) — QStandardPaths lookups add
493 // up on hot paths like QML preview rebuilds.
494 bool m_isSystemLayout = false;
495
496 // Dirty tracking for copy-on-write saving
497 bool m_dirty = false;
498 int m_batchModifyDepth = 0;
499};
500
501} // namespace PhosphorZones
Represents a collection of zones that form a layout.
Definition Layout.h:40
bool hasOuterGapOverride() const
Definition Layout.h:129
int overlayDisplayMode() const
Definition Layout.h:182
Zone * nearestZone(const QPointF &point, qreal maxDistance=-1) const
int zonePadding() const
Definition Layout.h:113
QList< int > allowedDesktops() const
Definition Layout.h:271
int outerGapLeft() const
Definition Layout.h:151
::PhosphorLayout::EdgeGaps rawOuterGaps() const
Raw per-side gap overrides.
Definition Layout.h:171
void addZone(Zone *zone)
bool usePerSideOuterGap() const
Definition Layout.h:136
bool isSystemLayout() const
bool isDirty() const
Definition Layout.h:391
void moveZone(int fromIndex, int toIndex)
Zone * zoneById(const QUuid &id) const
void setSourcePath(const QString &path)
void setAllowedActivities(const QStringList &activities)
QString systemSourcePath() const
Definition Layout.h:206
int defaultOrder() const
Definition Layout.h:313
void setShaderId(const QString &id)
void zoneRemoved(Zone *zone)
static Layout * createPriorityGridLayout(QObject *parent=nullptr)
int outerGapTop() const
Definition Layout.h:141
void setDefaultOrder(int order)
Definition Layout.h:317
bool hasFixedGeometryZones() const
Returns true if any zone uses fixed (pixel) geometry mode.
Layout(const Layout &other)
void setAspectRatioClass(::PhosphorLayout::AspectRatioClass cls)
void setOuterGapTop(int gap)
void setLastRecalcGeometry(const QRectF &geom)
Definition Layout.h:353
void setUseFullScreenGeometry(bool enabled)
void setHiddenFromSelector(bool hidden)
qreal maxAspectRatio() const
Definition Layout.h:247
QRectF fixedZoneBoundingBox() const
Bounding box of all fixed-geometry zones in pixel coordinates, anchored at (0, 0).
void markDirty()
Definition Layout.h:395
void overlayDisplayModeChanged()
QRectF fixedZoneReferenceGeometry() const
Reference geometry suitable for normalizing fixed-pixel zones to 0–1 relative coordinates.
void setOuterGap(int gap)
bool matchesAspectRatio(qreal screenAspectRatio) const
Check if this layout is suitable for a screen with the given aspect ratio.
void setUsePerSideOuterGap(bool enabled)
bool hasZonePaddingOverride() const
Definition Layout.h:118
Layout(const QString &name, QObject *parent=nullptr)
QVariantMap shaderParams() const
Definition Layout.h:225
void setZonePadding(int padding)
QString sourcePath() const
Definition Layout.h:194
void setAutoAssign(bool enabled)
QVector< Zone * > adjacentZones(const QPointF &point, qreal threshold=20) const
bool hasSystemOrigin() const
Definition Layout.h:214
static void setScreenIdResolver(ScreenIdResolver resolver)
int zoneCount() const
Definition Layout.h:323
void recalculateZoneGeometries(const QRectF &screenGeometry)
Recalculate every zone's absolute geometry against screenGeometry.
QJsonObject toJson() const
Zone * zoneByNumber(int number) const
void hiddenFromSelectorChanged()
bool hasPerSideOuterGapOverride() const
Definition Layout.h:161
bool hasOverlayDisplayModeOverride() const
Definition Layout.h:187
Zone * zone(int index) const
std::function< QString(const QString &)> ScreenIdResolver
Screen-id resolver.
Definition Layout.h:374
QStringList allowedActivities() const
Definition Layout.h:276
void setOverlayDisplayMode(int mode)
int outerGapRight() const
Definition Layout.h:156
int outerGap() const
Definition Layout.h:124
static Layout * createGridLayout(int columns, int rows, QObject *parent=nullptr)
QVector< Zone * > zonesInRect(const QRectF &rect) const
static Layout * createFocusLayout(QObject *parent=nullptr)
Layout(QObject *parent=nullptr)
bool useFullScreenGeometry() const
Definition Layout.h:290
void setDescription(const QString &description)
bool hiddenFromSelector() const
Definition Layout.h:261
::PhosphorLayout::AspectRatioClass aspectRatioClass() const
Definition Layout.h:232
void setMinAspectRatio(qreal ratio)
QRectF lastRecalcGeometry() const
Definition Layout.h:349
void setSystemSourcePath(const QString &path)
Definition Layout.h:210
Zone * zoneAtPoint(const QPointF &point) const
QStringList allowedScreens() const
Definition Layout.h:266
bool showZoneNumbers() const
Definition Layout.h:176
void setAspectRatioClassInt(int cls)
void removeZone(Zone *zone)
void setAllowedScreens(const QStringList &screens)
QString shaderId() const
Definition Layout.h:220
static ScreenIdResolver screenIdResolver()
Returns a copy of the currently-installed resolver (empty if none).
void setName(const QString &name)
void setOuterGapRight(int gap)
void clearOverlayDisplayModeOverride()
void setOuterGapBottom(int gap)
void setOuterGapLeft(int gap)
void removeZoneAt(int index)
QString name() const
Definition Layout.h:100
QUuid id() const
Definition Layout.h:96
void setMaxAspectRatio(qreal ratio)
QString description() const
Definition Layout.h:106
Layout & operator=(const Layout &other)
qreal minAspectRatio() const
Definition Layout.h:242
void useFullScreenGeometryChanged()
void setAllowedDesktops(const QList< int > &desktops)
static Layout * fromJson(const QJsonObject &json, QObject *parent=nullptr)
static Layout * createRowsLayout(int rows, QObject *parent=nullptr)
QVector< Zone * > zones() const
Definition Layout.h:327
int outerGapBottom() const
Definition Layout.h:146
bool autoAssign() const
Definition Layout.h:283
void zoneAdded(Zone *zone)
void setShaderParams(const QVariantMap &params)
void setShowZoneNumbers(bool show)
int aspectRatioClassInt() const
Definition Layout.h:237
static Layout * createColumnsLayout(int columns, QObject *parent=nullptr)
void clearDirty()
Definition Layout.h:399
Represents a single zone within a layout.
Definition Zone.h:44
AspectRatioClass
Screen aspect-ratio classification.
Definition AspectRatioClass.h:21
@ Any
Suitable for all aspect ratios (default)
Definition IWindowTrackingService.h:23
LayoutCategory
Category for layout type.
Definition Layout.h:27
@ Autotile
Dynamic auto-tiling algorithm.
@ Manual
Traditional zone-based layout.
Per-side edge gap values (resolved, non-negative pixel values)
Definition EdgeGaps.h:27