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
28enum class LayoutCategory {
29 Manual = 0,
30 Autotile = 1,
32};
33
41class PHOSPHORZONES_EXPORT Layout : public QObject
42{
43 Q_OBJECT
44
45 Q_PROPERTY(QUuid id READ id CONSTANT)
46 Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
47 Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged)
48 Q_PROPERTY(int zonePadding READ zonePadding WRITE setZonePadding NOTIFY zonePaddingChanged)
49 Q_PROPERTY(int outerGap READ outerGap WRITE setOuterGap NOTIFY outerGapChanged)
50 Q_PROPERTY(bool hasZonePaddingOverride READ hasZonePaddingOverride NOTIFY zonePaddingChanged)
51 Q_PROPERTY(bool hasOuterGapOverride READ hasOuterGapOverride NOTIFY outerGapChanged)
52 Q_PROPERTY(bool usePerSideOuterGap READ usePerSideOuterGap WRITE setUsePerSideOuterGap NOTIFY outerGapChanged)
53 Q_PROPERTY(int outerGapTop READ outerGapTop WRITE setOuterGapTop NOTIFY outerGapChanged)
54 Q_PROPERTY(int outerGapBottom READ outerGapBottom WRITE setOuterGapBottom NOTIFY outerGapChanged)
55 Q_PROPERTY(int outerGapLeft READ outerGapLeft WRITE setOuterGapLeft NOTIFY outerGapChanged)
56 Q_PROPERTY(int outerGapRight READ outerGapRight WRITE setOuterGapRight NOTIFY outerGapChanged)
57 Q_PROPERTY(bool hasPerSideOuterGapOverride READ hasPerSideOuterGapOverride NOTIFY outerGapChanged)
58 Q_PROPERTY(bool showZoneNumbers READ showZoneNumbers WRITE setShowZoneNumbers NOTIFY showZoneNumbersChanged)
59 Q_PROPERTY(
60 int overlayDisplayMode READ overlayDisplayMode WRITE setOverlayDisplayMode NOTIFY overlayDisplayModeChanged)
61 Q_PROPERTY(bool hasOverlayDisplayModeOverride READ hasOverlayDisplayModeOverride NOTIFY overlayDisplayModeChanged)
62 Q_PROPERTY(int zoneCount READ zoneCount NOTIFY zonesChanged)
63 Q_PROPERTY(QString sourcePath READ sourcePath WRITE setSourcePath NOTIFY sourcePathChanged)
64 Q_PROPERTY(bool isSystemLayout READ isSystemLayout NOTIFY sourcePathChanged)
65 Q_PROPERTY(QString shaderId READ shaderId WRITE setShaderId NOTIFY shaderIdChanged)
66 Q_PROPERTY(QVariantMap shaderParams READ shaderParams WRITE setShaderParams NOTIFY shaderParamsChanged)
67
68 // Auto-assign: new windows fill first empty zone
69 Q_PROPERTY(bool autoAssign READ autoAssign WRITE setAutoAssign NOTIFY autoAssignChanged)
70
71 // Geometry mode: use full screen or available (panel-excluded) geometry
72 Q_PROPERTY(bool useFullScreenGeometry READ useFullScreenGeometry WRITE setUseFullScreenGeometry NOTIFY
73 useFullScreenGeometryChanged)
74
75 // Aspect ratio classification
76 Q_PROPERTY(
77 int aspectRatioClass READ aspectRatioClassInt WRITE setAspectRatioClassInt NOTIFY aspectRatioClassChanged)
78 Q_PROPERTY(qreal minAspectRatio READ minAspectRatio WRITE setMinAspectRatio NOTIFY aspectRatioClassChanged)
79 Q_PROPERTY(qreal maxAspectRatio READ maxAspectRatio WRITE setMaxAspectRatio NOTIFY aspectRatioClassChanged)
80
81 // Visibility filtering
82 Q_PROPERTY(
83 bool hiddenFromSelector READ hiddenFromSelector WRITE setHiddenFromSelector NOTIFY hiddenFromSelectorChanged)
84 Q_PROPERTY(QStringList allowedScreens READ allowedScreens WRITE setAllowedScreens NOTIFY allowedScreensChanged)
85 Q_PROPERTY(QList<int> allowedDesktops READ allowedDesktops WRITE setAllowedDesktops NOTIFY allowedDesktopsChanged)
86 Q_PROPERTY(
87 QStringList allowedActivities READ allowedActivities WRITE setAllowedActivities NOTIFY allowedActivitiesChanged)
88
89public:
90 explicit Layout(QObject* parent = nullptr);
91 explicit Layout(const QString& name, QObject* parent = nullptr);
92 ~Layout() override;
93
94 // A Layout is a QObject, so it is never copied or assigned. Duplication
95 // goes through clone(), which returns a heap object with an explicit owner
96 // the way every other Layout in the codebase is created.
97 Layout(const Layout&) = delete;
98 Layout& operator=(const Layout&) = delete;
99
115 Layout* clone(QObject* parent = nullptr) const;
116
117 // Identification
118 QUuid id() const
119 {
120 return m_id;
121 }
122 QString name() const
123 {
124 return m_name;
125 }
126 void setName(const QString& name);
127
128 QString description() const
129 {
130 return m_description;
131 }
132 void setDescription(const QString& description);
133
134 // Layout settings (per-layout gap overrides, -1 = use global setting)
135 int zonePadding() const
136 {
137 return m_zonePadding;
138 }
139 void setZonePadding(int padding);
141 {
142 return m_zonePadding >= 0;
143 }
145
146 int outerGap() const
147 {
148 return m_outerGap;
149 }
150 void setOuterGap(int gap);
152 {
153 return m_outerGap >= 0;
154 }
156
157 // Per-side outer gap overrides (-1 = use global setting)
159 {
160 return m_usePerSideOuterGap;
161 }
162 void setUsePerSideOuterGap(bool enabled);
163 int outerGapTop() const
164 {
165 return m_outerGapTop;
166 }
167 void setOuterGapTop(int gap);
168 int outerGapBottom() const
169 {
170 return m_outerGapBottom;
171 }
172 void setOuterGapBottom(int gap);
173 int outerGapLeft() const
174 {
175 return m_outerGapLeft;
176 }
177 void setOuterGapLeft(int gap);
178 int outerGapRight() const
179 {
180 return m_outerGapRight;
181 }
182 void setOuterGapRight(int gap);
184 {
185 return m_usePerSideOuterGap
186 && (m_outerGapTop >= 0 || m_outerGapBottom >= 0 || m_outerGapLeft >= 0 || m_outerGapRight >= 0);
187 }
194 {
195 return {m_outerGapTop, m_outerGapBottom, m_outerGapLeft, m_outerGapRight};
196 }
197
198 bool showZoneNumbers() const
199 {
200 return m_showZoneNumbers;
201 }
202 void setShowZoneNumbers(bool show);
203
205 {
206 return m_overlayDisplayMode;
207 }
208 void setOverlayDisplayMode(int mode);
210 {
211 return m_overlayDisplayMode >= 0;
212 }
214
215 // Source path tracking - determines if layout is from system or user directory
216 QString sourcePath() const
217 {
218 return m_sourcePath;
219 }
220 void setSourcePath(const QString& path);
221
222 // Returns true if layout was loaded from a system directory (not user's .local)
223 // This determines whether the layout can be edited/deleted in place
224 bool isSystemLayout() const;
225
226 // Original system layout path — set when a user override replaces a system layout.
227 // Persisted in user JSON so deletion can restore the system original without scanning.
228 QString systemSourcePath() const
229 {
230 return m_systemSourcePath;
231 }
232 void setSystemSourcePath(const QString& path)
233 {
234 m_systemSourcePath = path;
235 }
236 bool hasSystemOrigin() const
237 {
238 return !m_systemSourcePath.isEmpty();
239 }
240
241 // Shader support
242 QString shaderId() const
243 {
244 return m_shaderId;
245 }
246 void setShaderId(const QString& id);
247 QVariantMap shaderParams() const
248 {
249 return m_shaderParams;
250 }
251 void setShaderParams(const QVariantMap& params);
252
253 // Aspect ratio classification
255 {
256 return m_aspectRatioClass;
257 }
260 {
261 return static_cast<int>(m_aspectRatioClass);
262 }
264 qreal minAspectRatio() const
265 {
266 return m_minAspectRatio;
267 }
268 void setMinAspectRatio(qreal ratio);
269 qreal maxAspectRatio() const
270 {
271 return m_maxAspectRatio;
272 }
273 void setMaxAspectRatio(qreal ratio);
274
280 bool matchesAspectRatio(qreal screenAspectRatio) const;
281
282 // Visibility filtering
284 {
285 return m_hiddenFromSelector;
286 }
287 void setHiddenFromSelector(bool hidden);
288 QStringList allowedScreens() const
289 {
290 return m_allowedScreens;
291 }
292 void setAllowedScreens(const QStringList& screens);
293 QList<int> allowedDesktops() const
294 {
295 return m_allowedDesktops;
296 }
297 void setAllowedDesktops(const QList<int>& desktops);
298 QStringList allowedActivities() const
299 {
300 return m_allowedActivities;
301 }
302 void setAllowedActivities(const QStringList& activities);
303
304 // Auto-assign: new windows fill first empty zone
305 bool autoAssign() const
306 {
307 return m_autoAssign;
308 }
309 void setAutoAssign(bool enabled);
310
311 // Geometry mode: when true, zones span the full screen including panel/taskbar areas
313 {
314 return m_useFullScreenGeometry;
315 }
316 void setUseFullScreenGeometry(bool enabled);
317
320
323 QRectF fixedZoneBoundingBox() const;
324
333
340 static constexpr int DefaultOrderUnset = 999;
341
342 // Optional load order for "default" layout when defaultLayoutId is not set (lower = first)
343 int defaultOrder() const
344 {
345 return m_defaultOrder;
346 }
347 void setDefaultOrder(int order)
348 {
349 m_defaultOrder = order;
350 }
351
352 // Zone management
353 int zoneCount() const
354 {
355 return m_zones.size();
356 }
357 QVector<Zone*> zones() const
358 {
359 return m_zones;
360 }
361 Zone* zone(int index) const;
362 Zone* zoneById(const QUuid& id) const;
363 Zone* zoneByNumber(int number) const;
368 Zone* zoneByName(const QString& name) const;
369
370 Q_INVOKABLE void addZone(Zone* zone);
371 Q_INVOKABLE void removeZone(Zone* zone);
372 Q_INVOKABLE void removeZoneAt(int index);
373 Q_INVOKABLE void clearZones();
374 Q_INVOKABLE void moveZone(int fromIndex, int toIndex);
375
376 // Zone detection
377 Q_INVOKABLE Zone* zoneAtPoint(const QPointF& point) const;
378 Q_INVOKABLE Zone* nearestZone(const QPointF& point, qreal maxDistance = -1) const;
379 Q_INVOKABLE QVector<Zone*> zonesInRect(const QRectF& rect) const;
380 Q_INVOKABLE QVector<Zone*> adjacentZones(const QPointF& point, qreal threshold = 20) const;
381
382 // Geometry calculations
383 Q_INVOKABLE void renumberZones();
384 QRectF lastRecalcGeometry() const
385 {
386 return m_lastRecalcGeometry;
387 }
388 void setLastRecalcGeometry(const QRectF& geom)
389 {
390 m_lastRecalcGeometry = geom;
391 }
398 bool needsRecalc(const QRectF& screenGeometry) const
399 {
400 return screenGeometry != m_lastRecalcGeometry || m_zoneGeometryDirty;
401 }
406 void markRecalculated(const QRectF& geom)
407 {
408 m_lastRecalcGeometry = geom;
409 m_zoneGeometryDirty = false;
410 }
411
412 // Serialization
413 QJsonObject toJson() const;
414 static Layout* fromJson(const QJsonObject& json, QObject* parent = nullptr);
415
428 using ScreenIdResolver = std::function<QString(const QString&)>;
436
437 // Predefined layouts (templates)
438 static Layout* createColumnsLayout(int columns, QObject* parent = nullptr);
439 static Layout* createRowsLayout(int rows, QObject* parent = nullptr);
440 static Layout* createGridLayout(int columns, int rows, QObject* parent = nullptr);
441 static Layout* createPriorityGridLayout(QObject* parent = nullptr);
442 static Layout* createFocusLayout(QObject* parent = nullptr);
443
444 // Dirty tracking for copy-on-write saving
445 bool isDirty() const
446 {
447 return m_dirty;
448 }
450 {
451 m_dirty = true;
452 }
454 {
455 m_dirty = false;
456 }
459
460Q_SIGNALS:
478 void zoneAdded(Zone* zone);
479 void zoneRemoved(Zone* zone);
481
482public:
491 void recalculateZoneGeometries(const QRectF& screenGeometry);
492
493private:
494 void emitModifiedIfNotBatched();
495
502 void initFromJson(const QJsonObject& json);
503
504 QUuid m_id;
505 QString m_name;
506 QString m_description;
507 int m_zonePadding = -1; // -1 = use global setting
508 int m_outerGap = -1; // -1 = use global setting
509 bool m_usePerSideOuterGap = false;
510 int m_outerGapTop = -1; // -1 = use global setting
511 int m_outerGapBottom = -1;
512 int m_outerGapLeft = -1;
513 int m_outerGapRight = -1;
514 bool m_showZoneNumbers = true;
515 int m_overlayDisplayMode = -1; // -1 = use global setting
516 QString m_sourcePath; // Path where layout was loaded from (empty for new layouts)
517 QString m_systemSourcePath; // Original system path if this is a user override of a system layout
518 int m_defaultOrder = DefaultOrderUnset; // Optional: lower values appear first when choosing default
519 QVector<Zone*> m_zones;
520
521 // Auto-assign: new windows fill first empty zone
522 bool m_autoAssign = false;
523
524 // Geometry mode: zones use full screen (true) or available area excluding panels (false)
525 bool m_useFullScreenGeometry = false;
526
527 // Shader support
528 QString m_shaderId; // Shader effect ID (empty = no shader)
529 QVariantMap m_shaderParams; // Shader-specific parameters
530
531 // Aspect ratio classification
533 qreal m_minAspectRatio = 0.0; // 0 = not set (use class matching)
534 qreal m_maxAspectRatio = 0.0; // 0 = not set (use class matching)
535
536 // Visibility filtering
537 bool m_hiddenFromSelector = false;
538 QStringList m_allowedScreens; // empty = all screens
539 QList<int> m_allowedDesktops; // empty = all desktops
540 QStringList m_allowedActivities; // empty = all activities
541
542 // Screen rect the zone geometries were last recalculated against. Empty
543 // means "no screen known yet". Doubles as the reference frame the derived
544 // relativeGeometry is defined over — see fixedZoneReferenceGeometry.
545 mutable QRectF m_lastRecalcGeometry;
546
547 // Set when the zone SET changes (add / remove / clear). Editing the zone
548 // list leaves the recalculated absolute geometries stale, but it does not
549 // move the layout to another screen, so the rect above stays valid as a
550 // reference and only the recompute is owed. Keeping these two facts apart
551 // is what lets a serialization that follows a zone swap still normalize
552 // against the screen instead of silently falling back to the fixed-zone
553 // bounding box.
554 bool m_zoneGeometryDirty = false;
555
556 // Cached classification derived from m_sourcePath. Recomputed in
557 // setSourcePath so isSystemLayout() is O(1) — QStandardPaths lookups add
558 // up on hot paths like QML preview rebuilds.
559 bool m_isSystemLayout = false;
560
561 // Dirty tracking for copy-on-write saving
562 bool m_dirty = false;
563 int m_batchModifyDepth = 0;
564};
565
566} // namespace PhosphorZones
Represents a collection of zones that form a layout.
Definition Layout.h:42
bool hasOuterGapOverride() const
Definition Layout.h:151
int overlayDisplayMode() const
Definition Layout.h:204
Zone * nearestZone(const QPointF &point, qreal maxDistance=-1) const
int zonePadding() const
Definition Layout.h:135
QList< int > allowedDesktops() const
Definition Layout.h:293
int outerGapLeft() const
Definition Layout.h:173
::PhosphorLayout::EdgeGaps rawOuterGaps() const
Raw per-side gap overrides.
Definition Layout.h:193
void addZone(Zone *zone)
bool usePerSideOuterGap() const
Definition Layout.h:158
bool isSystemLayout() const
bool isDirty() const
Definition Layout.h:445
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:228
int defaultOrder() const
Definition Layout.h:343
void setShaderId(const QString &id)
void zoneRemoved(Zone *zone)
static Layout * createPriorityGridLayout(QObject *parent=nullptr)
int outerGapTop() const
Definition Layout.h:163
void setDefaultOrder(int order)
Definition Layout.h:347
bool hasFixedGeometryZones() const
Returns true if any zone uses fixed (pixel) geometry mode.
void setAspectRatioClass(::PhosphorLayout::AspectRatioClass cls)
void setOuterGapTop(int gap)
void setLastRecalcGeometry(const QRectF &geom)
Definition Layout.h:388
void setUseFullScreenGeometry(bool enabled)
void setHiddenFromSelector(bool hidden)
qreal maxAspectRatio() const
Definition Layout.h:269
QRectF fixedZoneBoundingBox() const
Bounding box of all fixed-geometry zones in pixel coordinates, anchored at (0, 0).
void markDirty()
Definition Layout.h:449
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:140
Layout(const QString &name, QObject *parent=nullptr)
QVariantMap shaderParams() const
Definition Layout.h:247
void setZonePadding(int padding)
QString sourcePath() const
Definition Layout.h:216
void setAutoAssign(bool enabled)
Layout(const Layout &)=delete
QVector< Zone * > adjacentZones(const QPointF &point, qreal threshold=20) const
bool hasSystemOrigin() const
Definition Layout.h:236
static void setScreenIdResolver(ScreenIdResolver resolver)
int zoneCount() const
Definition Layout.h:353
void recalculateZoneGeometries(const QRectF &screenGeometry)
Recalculate every zone's absolute geometry against screenGeometry.
QJsonObject toJson() const
Zone * zoneByNumber(int number) const
void markRecalculated(const QRectF &geom)
Full-recalc bookkeeping for external appliers (the async worker path): stamps the rect AND clears the...
Definition Layout.h:406
void hiddenFromSelectorChanged()
bool hasPerSideOuterGapOverride() const
Definition Layout.h:183
bool hasOverlayDisplayModeOverride() const
Definition Layout.h:209
Zone * zone(int index) const
std::function< QString(const QString &)> ScreenIdResolver
Screen-id resolver.
Definition Layout.h:428
QStringList allowedActivities() const
Definition Layout.h:298
void setOverlayDisplayMode(int mode)
int outerGapRight() const
Definition Layout.h:178
int outerGap() const
Definition Layout.h:146
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:312
void setDescription(const QString &description)
bool hiddenFromSelector() const
Definition Layout.h:283
::PhosphorLayout::AspectRatioClass aspectRatioClass() const
Definition Layout.h:254
void setMinAspectRatio(qreal ratio)
Layout & operator=(const Layout &)=delete
QRectF lastRecalcGeometry() const
Definition Layout.h:384
void setSystemSourcePath(const QString &path)
Definition Layout.h:232
Zone * zoneAtPoint(const QPointF &point) const
QStringList allowedScreens() const
Definition Layout.h:288
bool showZoneNumbers() const
Definition Layout.h:198
void setAspectRatioClassInt(int cls)
void removeZone(Zone *zone)
void setAllowedScreens(const QStringList &screens)
Zone * zoneByName(const QString &name) const
First zone (in zone-number order) whose name equals name after trimming, compared case-insensitively.
bool needsRecalc(const QRectF &screenGeometry) const
The complete staleness predicate recalculateZoneGeometries() gates on: the screen rect changed OR the...
Definition Layout.h:398
QString shaderId() const
Definition Layout.h:242
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:122
QUuid id() const
Definition Layout.h:118
Layout * clone(QObject *parent=nullptr) const
Deep-copy this layout into a new, independently owned Layout.
void setMaxAspectRatio(qreal ratio)
QString description() const
Definition Layout.h:128
qreal minAspectRatio() const
Definition Layout.h:264
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:357
int outerGapBottom() const
Definition Layout.h:168
bool autoAssign() const
Definition Layout.h:305
void zoneAdded(Zone *zone)
void setShaderParams(const QVariantMap &params)
void setShowZoneNumbers(bool show)
int aspectRatioClassInt() const
Definition Layout.h:259
static Layout * createColumnsLayout(int columns, QObject *parent=nullptr)
void clearDirty()
Definition Layout.h:453
A scrolling screen's first-class sizing template.
Definition ScrollingTemplate.h:128
Represents a single zone within a layout.
Definition Zone.h:44
AspectRatioClass
Screen aspect-ratio classification.
Definition AspectRatioClass.h:22
@ Any
Suitable for all aspect ratios (default)
Definition IWindowTrackingService.h:23
LayoutCategory
Category for layout type.
Definition Layout.h:28
@ Autotile
Dynamic auto-tiling algorithm.
@ Manual
Traditional zone-based layout.
Per-side edge gap values (resolved, non-negative pixel values)
Definition EdgeGaps.h:27