// Copyright (C) 2026 Danilo M. // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as // published by the Free Software Foundation. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. import Quickshell import QtQuick // The settings window: a normal toplevel, not a layer surface, so Hyprland // tiles or floats it like any app. The drawer is the quick area; things set // once and left alone live here. It hosts the pages of the modules that // declare `settings: true`, from the same module list as the drawer, with a // list of them down the left. Scope { id: root property list modules property bool open: false property string page: "" readonly property var pages: { const out = []; for (let i = 0; i < root.modules.length; i++) if (root.modules[i].settings) out.push(root.modules[i]); return out; } // Null is the landing page. readonly property QtObject current: root.pages.find(m => m.name === root.page) ?? null // An unknown or empty name opens the landing page. function show(name) { root.page = name; root.open = true; } // One step out: a page to the landing page, the landing page closes. function back() { if (root.current) root.page = ""; else root.close(); } function close() { root.open = false; root.page = ""; } LazyLoader { active: root.open FloatingWindow { title: "Settings" implicitWidth: 900 implicitHeight: 640 color: Theme.base onClosed: root.close() Item { anchors.fill: parent focus: true Keys.onEscapePressed: root.back() ListView { id: side anchors { top: parent.top; left: parent.left; bottom: parent.bottom; margins: 12 } width: 200 spacing: 4 model: root.pages delegate: Rectangle { id: entry required property QtObject modelData readonly property bool selected: root.current === modelData width: side.width height: 36 radius: 8 color: selected ? Qt.alpha(Theme.accent, 0.22) : area.containsMouse ? Qt.alpha(Theme.text, 0.06) : "transparent" Row { anchors { left: parent.left; leftMargin: 10; verticalCenter: parent.verticalCenter } spacing: 10 Text { text: entry.modelData.icon font { family: Theme.iconFamily; pixelSize: Theme.fontSize } color: Theme.text } Text { text: entry.modelData.label font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 2 } color: Theme.text } } MouseArea { id: area anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: root.page = entry.modelData.name } } } Rectangle { id: rule anchors { top: parent.top; bottom: parent.bottom; left: side.right; leftMargin: 12 } width: 1 color: Qt.alpha(Theme.text, 0.12) } // The landing page: every settings page as a tile, as the // drawer's grid shows its modules. Where back returns to. Item { anchors { top: parent.top; right: parent.right; bottom: parent.bottom; left: rule.right; margins: 16 } visible: root.current === null Text { id: landingTitle height: 44 verticalAlignment: Text.AlignVCenter text: "Settings" font { family: Theme.fontFamily; pixelSize: Theme.fontSize + 2; bold: true } color: Theme.text } Flow { id: landing anchors { top: landingTitle.bottom; topMargin: 12; left: parent.left; right: parent.right } spacing: 10 Repeater { model: root.pages Tile { required property QtObject modelData width: (landing.width - 2 * landing.spacing) / 3 icon: modelData.icon label: modelData.label onClicked: root.page = modelData.name } } } } Loader { id: pageLoader anchors { top: parent.top; right: parent.right; bottom: parent.bottom; left: rule.right; margins: 16 } sourceComponent: root.current?.page ?? null // The Page's back arrow steps out to the landing page, // dismiss closes the window, and go() moves to another // settings page by name. onLoaded: { if (item && item.back) item.back.connect(root.back); if (item && item.dismiss) item.dismiss.connect(root.close); if (item && item.go) item.go.connect(name => root.page = name); } Binding { target: pageLoader.item property: "icon" value: root.current ? root.current.icon : "" when: pageLoader.item !== null } } } } } }