// Tiling. // // How windows share the space. The window-layout card keeps its handoff row // because the adjustable parts of window *appearance* -- gaps, corners, // borders, effects -- live on the Appearance page next to the preview that // explains them. Splitting them would put two halves of one idea on two pages; // pointing at the other half keeps one. import QtQuick import qs.config import qs.services SettingsPage { id: root title: "Tiling" lede: "How windows share the space, and where their edges are." // The rule being edited, or null while the editor is closed. Held here // rather than in the editor so that opening one row's Edit closes another's. property var editingRule: null property bool addingRule: false readonly property bool ruleEditorOpen: root.addingRule || root.editingRule !== null function closeRuleEditor(): void { root.addingRule = false; root.editingRule = null; } // Per-application behavior, above the general tiling settings: a rule for // one application is what somebody came here to write, and the gaps and // borders below are set once and left alone. SettingsCard { title: "App rules" subtitle: "How specific applications behave when they open. Behaviors, not regexes: pick an application, tick what it should do. Panama's own surfaces cannot be matched." Repeater { id: ruleRows model: WindowRules.rules WindowRuleRow { id: ruleRow required property var modelData required property int index rule: ruleRow.modelData divider: ruleRow.index < ruleRows.count - 1 onEditRequested: { root.addingRule = false; root.editingRule = ruleRow.modelData; ruleEditor.load(ruleRow.modelData); } onRemoveRequested: { if (root.editingRule === ruleRow.modelData) root.closeRuleEditor(); WindowRules.removeRule(String(ruleRow.modelData.class ?? "")); } } } Text { width: parent.width visible: WindowRules.rules.length === 0 && !root.ruleEditorOpen text: "No rules yet. Every application opens the way the compositor decides." color: Theme.fgMuted font.family: Theme.fontFamily font.pixelSize: Theme.fontSize topPadding: 10 bottomPadding: 10 } WindowRuleEditor { id: ruleEditor width: parent.width visible: root.ruleEditorOpen onCommitted: rule => { const saved = root.editingRule === null ? WindowRules.addRule(rule) : WindowRules.updateRule(String(root.editingRule.class ?? ""), rule); if (saved) root.closeRuleEditor(); } onCanceled: root.closeRuleEditor() } SettingRow { visible: !root.ruleEditorOpen label: "Add a rule" detail: WindowRules.rules.length === 0 ? "Applies on save, with a compositor reload" : (WindowRules.applied ? "Applied on the last reload — Hyprland publishes no rule listing, so this is the reload's word, not a read-back of the rules themselves" : "Saved, waiting on the reload that puts it in effect") controlWidth: 130 divider: false SettingsButton { anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter tone: "accent" text: "+ Add a rule" enabled: !WindowRules.reloading onClicked: { root.editingRule = null; ruleEditor.reset(); root.addingRule = true; } } } ErrorRow { message: WindowRules.lastError } } SettingsCard { title: "Window layout" subtitle: "Follows the Forge mental model, with native Hyprland tiling." // These were two read-only rows reporting "Tiling" and "Dynamic", which // described settings rather than facts -- both are ordinary Hyprland // options that simply had no controls. TextRow's own documentation says // a setting the user could reasonably change does not belong in it. ChoiceRow { setting: "windowLayout" } ToggleRow { setting: "preserveSplit" } ChoiceRow { setting: "forceSplit" } ToggleRow { setting: "windowSnapping" } ActionRow { label: "Gaps, corners, and effects" detail: "Adjusted on the Appearance page, beside a live preview" action: "Open Appearance" divider: false onTriggered: ShellState.openSettingsSection("appearance", "windows") } } // GNOME's Multitasking panel, in Hyprland's terms. // Only meaningful when the layout above is Master and stack. Hidden // otherwise, because a card of settings that do nothing under the layout // you are actually running is worse than not offering the layout at all. SettingsCard { visible: DesktopPreferences.get("windowLayout") === "master" title: "Master and stack" subtitle: "How the master area behaves. These apply only while the tiling layout above is Master and stack." SliderRow { setting: "masterFactor" } ChoiceRow { setting: "masterOrientation" } ChoiceRow { setting: "masterNewStatus" } ToggleRow { setting: "masterNewOnTop"; divider: false } } SettingsCard { title: "Window edges" subtitle: "How the pointer grabs a window's border, and how floating windows behave near each other and the screen edge." ToggleRow { setting: "resizeOnBorder" } SliderRow { setting: "borderGrabArea"; zeroLabel: "Border only" } ToggleRow { setting: "hoverIconOnBorder" } SliderRow { setting: "snapWindowGap"; zeroLabel: "Touching" } SliderRow { setting: "snapMonitorGap"; zeroLabel: "Touching" } ToggleRow { setting: "snapRespectGaps"; divider: false } } // Hyprland's own interruptions. Panama turns all four off, which is a // defensible default and was not previously a decision anyone could // reverse without editing looks.lua. SettingsCard { title: "Hyprland notices" subtitle: "Panama hides all of these by default. They are the compositor's own, not Panama's." ToggleRow { setting: "hyprlandLogo" } ToggleRow { setting: "hyprlandSplash" } ToggleRow { setting: "hyprlandUpdateNews" } ToggleRow { setting: "hyprlandDonationNag"; divider: false } } }