183 lines
6.8 KiB
QML
183 lines
6.8 KiB
QML
// 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: "Application window 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 }
|
||
}
|
||
}
|