diff options
Diffstat (limited to 'desktop/modules')
| -rw-r--r-- | desktop/modules/appearance/Hypridle.qml | 8 | ||||
| -rw-r--r-- | desktop/modules/appearance/Hyprsunset.qml | 12 | ||||
| -rw-r--r-- | desktop/modules/appearance/Icons.qml | 3 | ||||
| -rw-r--r-- | desktop/modules/appearance/IconsModule.qml | 7 | ||||
| -rw-r--r-- | desktop/modules/appearance/IconsTab.qml | 32 | ||||
| -rw-r--r-- | desktop/modules/appearance/IdleModule.qml | 8 | ||||
| -rw-r--r-- | desktop/modules/appearance/IdleTab.qml | 6 | ||||
| -rw-r--r-- | desktop/modules/appearance/README.md | 3 | ||||
| -rw-r--r-- | desktop/modules/appearance/SunsetModule.qml | 8 | ||||
| -rw-r--r-- | desktop/modules/appearance/SunsetTab.qml | 6 | ||||
| -rw-r--r-- | desktop/modules/appearance/WallpaperModule.qml | 5 | ||||
| -rw-r--r-- | desktop/modules/appearance/WallpaperTab.qml | 15 | ||||
| -rw-r--r-- | desktop/modules/user/UserModule.qml | 8 | ||||
| -rw-r--r-- | desktop/modules/user/UserPage.qml | 13 |
14 files changed, 75 insertions, 59 deletions
diff --git a/desktop/modules/appearance/Hypridle.qml b/desktop/modules/appearance/Hypridle.qml index a1d3ecd..5f03c35 100644 --- a/desktop/modules/appearance/Hypridle.qml +++ b/desktop/modules/appearance/Hypridle.qml @@ -30,6 +30,10 @@ Singleton { property string tail: "" property string notice: "" property bool loaded: false + // The file as last read or written: dirty is what save() would write + // differing from it. Discard is refresh(). + property string saved: "" + readonly property bool dirty: root.loaded && root.serialize(root.prefix, root.listeners, root.tail) !== root.saved function getField(body, key) { const m = body.match(new RegExp(`^\\s*${key}\\s*=\\s*(.+?)\\s*$`, "m")); @@ -146,6 +150,7 @@ listener { root.prefix = p.prefix; root.listeners = p.listeners; root.tail = p.tail; + root.saved = root.serialize(p.prefix, p.listeners, p.tail); root.loaded = true; } } @@ -155,7 +160,8 @@ listener { function save() { if (!root.loaded) return; // not loaded yet: writing would truncate the file - confFile.setText(root.serialize(root.prefix, root.listeners, root.tail)); + root.saved = root.serialize(root.prefix, root.listeners, root.tail); + confFile.setText(root.saved); Quickshell.execDetached(["sh", "-c", "pkill -x hypridle; setsid -f hypridle"]); root.notice = "saved; idle timers reset"; } diff --git a/desktop/modules/appearance/Hyprsunset.qml b/desktop/modules/appearance/Hyprsunset.qml index 5886735..4befcb5 100644 --- a/desktop/modules/appearance/Hyprsunset.qml +++ b/desktop/modules/appearance/Hyprsunset.qml @@ -39,6 +39,12 @@ Singleton { property bool busy: false property bool confLoaded: false property bool appLoaded: false + // The two files as last read or written, to tell an unsaved edit: dirty + // is what save() would write differing from them. Discard is refresh(). + property string savedConf: "" + property string savedApp: "" + readonly property bool dirty: root.confLoaded && root.appLoaded + && (root.serializeProfiles(root.profiles) !== root.savedConf || root.appConfText() !== root.savedApp) readonly property string header: "# Managed by hyprsunset-qt. Edits here are overwritten on save.\n" @@ -166,6 +172,7 @@ profile { stdout: StdioCollector { onStreamFinished: { root.profiles = root.parseProfiles(text); + root.savedConf = root.serializeProfiles(root.profiles); root.confLoaded = true; } } @@ -202,6 +209,7 @@ profile { root.cachePathRaw = c["cache.path"] ?? "~/.config/hyprsunset-qt/sun.json"; root.cachePath = root.expandTilde(root.cachePathRaw); root.daemonCommand = c["daemon.command"] ?? "hyprsunset"; + root.savedApp = root.appConfText(); root.appLoaded = true; } } @@ -242,7 +250,9 @@ profile { root.notice = `invalid gamma: ${p.gamma}`; return; } } - confFile.setText(root.serializeProfiles(root.profiles)); + root.savedConf = root.serializeProfiles(root.profiles); + root.savedApp = root.appConfText(); + confFile.setText(root.savedConf); root.saveSettings(); root.restart(); root.notice = "saved + restarted"; diff --git a/desktop/modules/appearance/Icons.qml b/desktop/modules/appearance/Icons.qml index a6eaec5..fa2753b 100644 --- a/desktop/modules/appearance/Icons.qml +++ b/desktop/modules/appearance/Icons.qml @@ -110,6 +110,9 @@ for name in sys.argv[2:]: property var cursorPreview: ({}) property bool scanning: true property string notice: "" + // A cursor theme clicked but not applied, "" for none. Kept here rather + // than in the page, so the header's Apply and the sidebar dot can see it. + property string pendingCursor: "" // Pure: classify works on entries shaped { name, index, cursors, manifest } // so it can be tested without touching the filesystem. diff --git a/desktop/modules/appearance/IconsModule.qml b/desktop/modules/appearance/IconsModule.qml index bf3722b..94132db 100644 --- a/desktop/modules/appearance/IconsModule.qml +++ b/desktop/modules/appearance/IconsModule.qml @@ -19,6 +19,7 @@ Module { // nf-fa-mouse_pointer, present in Inconsolata Nerd Font. icon: "\uf245" settings: true + dirty: Icons.pendingCursor !== "" group: "Appearance" keywords: ["cursor", "pointer", "size", "gtk", "qt", "theme"] searchItems: Icons.iconThemes.map(n => ({ text: "icons: " + n, key: "icon:" + n })) @@ -29,6 +30,12 @@ Module { page: Component { Page { title: "Icons & cursor" + // The staged cursor theme; an icon theme applies on click. + actions: Component { + SaveBar { saveText: "Apply"; discardText: "Reset"; dirty: Icons.pendingCursor !== "" + onSave: { Icons.applyCursor(Icons.pendingCursor); Icons.pendingCursor = ""; } + onDiscard: Icons.pendingCursor = "" } + } IconsTab { width: parent.width } } } diff --git a/desktop/modules/appearance/IconsTab.qml b/desktop/modules/appearance/IconsTab.qml index 777ea51..1df9cb0 100644 --- a/desktop/modules/appearance/IconsTab.qml +++ b/desktop/modules/appearance/IconsTab.qml @@ -22,7 +22,6 @@ Item { // persists the theme to org.gnome.desktop.interface cursor-theme, so // hovering wrote the setting and a close left the last hovered theme set. // The four rendered shapes on each card are the preview. - property string pendingCursor: "" Column { id: col @@ -139,7 +138,7 @@ Item { required property string modelData objectName: "cursor:" + modelData readonly property bool current: Icons.currentCursor === modelData - readonly property bool staged: pendingCursor === modelData + readonly property bool staged: Icons.pendingCursor === modelData width: 230; height: 150; radius: 10 color: current || staged ? Qt.alpha(Theme.accent, 0.2) : Qt.alpha(Theme.surface, 0.35) border.width: current || staged ? 2 : 1 @@ -150,7 +149,7 @@ Item { MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor - onClicked: pendingCursor = modelData + onClicked: Icons.pendingCursor = modelData } Column { @@ -191,28 +190,11 @@ Item { } } - Row { - spacing: 8 - Text { - anchors.verticalCenter: parent.verticalCenter - text: pendingCursor !== "" ? "click Apply to set" : "click a cursor theme to stage it" - color: Theme.overlay - font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } - } - Tab { - text: "Apply" - selected: pendingCursor !== "" - enabled: pendingCursor !== "" - onClicked: { - Icons.applyCursor(pendingCursor); - pendingCursor = ""; - } - } - Tab { - text: "Reset" - enabled: pendingCursor !== "" - onClicked: pendingCursor = "" - } + // Apply and Reset are in the page header, with every settings page's. + Text { + text: Icons.pendingCursor !== "" ? "Apply, in the header, sets it" : "click a cursor theme to stage it" + color: Theme.overlay + font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } } Text { diff --git a/desktop/modules/appearance/IdleModule.qml b/desktop/modules/appearance/IdleModule.qml index 4b27cd3..2e4f32a 100644 --- a/desktop/modules/appearance/IdleModule.qml +++ b/desktop/modules/appearance/IdleModule.qml @@ -19,14 +19,20 @@ Module { // nf-fa-hourglass, present in Inconsolata Nerd Font. icon: "\uf254" settings: true + dirty: Hypridle.dirty group: "Desktop" keywords: ["hypridle", "lock", "timeout", "screen", "dpms", "suspend", "sleep"] - function refresh() { Hypridle.refresh(); } + // Not over unsaved edits: the reload would drop them. + function refresh() { if (!Hypridle.dirty) Hypridle.refresh(); } page: Component { Page { title: "HyprIdle" + actions: Component { + SaveBar { saveText: "Save + Restart"; dirty: Hypridle.dirty + onSave: Hypridle.save(); onDiscard: Hypridle.refresh() } + } IdleTab { width: parent.width } } } diff --git a/desktop/modules/appearance/IdleTab.qml b/desktop/modules/appearance/IdleTab.qml index 1199543..fdb39ec 100644 --- a/desktop/modules/appearance/IdleTab.qml +++ b/desktop/modules/appearance/IdleTab.qml @@ -82,11 +82,7 @@ Item { } } - Tab { - text: "Save + Restart hypridle" - selected: true - onClicked: Hypridle.save() - } + // Save + Restart is in the page header, with every settings page's. Text { width: parent.width diff --git a/desktop/modules/appearance/README.md b/desktop/modules/appearance/README.md index 51e5f09..b80b564 100644 --- a/desktop/modules/appearance/README.md +++ b/desktop/modules/appearance/README.md @@ -65,7 +65,8 @@ waybar-theme-udt's `install.sh` or signals waybar. ## Wallpapers Clicking a thumbnail **stages** it rather than setting it, so both screens can -be composed before anything changes, and Apply then makes a single +be composed before anything changes, and Apply, in the page header like +every settings page's commit, then makes a single `wallp --set H=… V=…` call. The mock screens above the list show what the desk would look like: the staged pick where there is one, what is currently set where there is not, and the hovered thumbnail on the targeted screen while the diff --git a/desktop/modules/appearance/SunsetModule.qml b/desktop/modules/appearance/SunsetModule.qml index 557a2a4..d55d874 100644 --- a/desktop/modules/appearance/SunsetModule.qml +++ b/desktop/modules/appearance/SunsetModule.qml @@ -19,14 +19,20 @@ Module { // nf-fa-moon_o, present in Inconsolata Nerd Font. icon: "\uf186" settings: true + dirty: Hyprsunset.dirty group: "Desktop" keywords: ["hyprsunset", "night", "light", "temperature", "gamma", "location", "profile"] - function refresh() { Hyprsunset.refresh(); } + // Not over unsaved edits: the reload would drop them. + function refresh() { if (!Hyprsunset.dirty) Hyprsunset.refresh(); } page: Component { Page { title: "HyprSunset" + actions: Component { + SaveBar { saveText: "Save + Restart"; dirty: Hyprsunset.dirty + onSave: Hyprsunset.save(); onDiscard: Hyprsunset.refresh() } + } SunsetTab { width: parent.width } } } diff --git a/desktop/modules/appearance/SunsetTab.qml b/desktop/modules/appearance/SunsetTab.qml index 657a920..26ec264 100644 --- a/desktop/modules/appearance/SunsetTab.qml +++ b/desktop/modules/appearance/SunsetTab.qml @@ -150,11 +150,7 @@ Item { temperature: null, gamma: null }]) } Tab { text: "Live preview"; onClicked: Hyprsunset.preview() } - Tab { - text: "Save + Restart" - selected: true - onClicked: Hyprsunset.save() - } + // Save + Restart is in the page header, with every settings page's. } Text { diff --git a/desktop/modules/appearance/WallpaperModule.qml b/desktop/modules/appearance/WallpaperModule.qml index c21d94d..cbc7158 100644 --- a/desktop/modules/appearance/WallpaperModule.qml +++ b/desktop/modules/appearance/WallpaperModule.qml @@ -20,6 +20,7 @@ Module { // nf-fa-picture_o, present in Inconsolata Nerd Font. icon: "\uf03e" settings: true + dirty: Wallpapers.dirty group: "Appearance" keywords: ["background", "screen", "monitor", "wallp", "wallhaven", "horizontal", "vertical"] @@ -29,6 +30,10 @@ Module { Page { id: pg title: "Wallpaper" + actions: Component { + SaveBar { saveText: "Apply"; discardText: "Reset"; dirty: Wallpapers.dirty; busy: Wallpapers.applying + onSave: Wallpapers.apply(); onDiscard: Wallpapers.clearPending() } + } WallpaperTab { width: parent.width; height: pg.viewHeight; onDismiss: pg.dismiss() } } } diff --git a/desktop/modules/appearance/WallpaperTab.qml b/desktop/modules/appearance/WallpaperTab.qml index 9788ba2..af3c23b 100644 --- a/desktop/modules/appearance/WallpaperTab.qml +++ b/desktop/modules/appearance/WallpaperTab.qml @@ -85,7 +85,7 @@ Column { width: parent.width wrapMode: Text.Wrap text: tab.hovering !== "" ? Wallpapers.label(tab.hovering) - : Wallpapers.dirty ? "click Apply to set" + : Wallpapers.dirty ? "Apply, in the header, sets it" : "click a wallpaper to stage it" font { family: Theme.fontFamily; pixelSize: Theme.fontSize - 4 } color: Theme.overlay @@ -111,17 +111,8 @@ Column { width: parent.width spacing: 8 - Tab { - text: Wallpapers.applying ? "applying…" : "Apply" - selected: Wallpapers.dirty && !Wallpapers.applying - enabled: Wallpapers.dirty && !Wallpapers.applying - onClicked: Wallpapers.apply() - } - Tab { - text: "Reset" - enabled: Wallpapers.dirty && !Wallpapers.applying - onClicked: Wallpapers.clearPending() - } + // Apply and Reset are in the page header, with every settings + // page's. // wallhaven-dl searches wallhaven.cc and finds larger // copies of low-res wallpapers. Launched through its // desktop entry, which needs no ~/bin on quickshell's PATH diff --git a/desktop/modules/user/UserModule.qml b/desktop/modules/user/UserModule.qml index 79f1be5..31ef0d8 100644 --- a/desktop/modules/user/UserModule.qml +++ b/desktop/modules/user/UserModule.qml @@ -29,7 +29,13 @@ Module { Page { id: pg title: "User" - UserPage { width: parent.width; owner: um; onMoreGit: pg.go("git") } + // The draft lives in the page, so it is not marked in the + // sidebar: leaving the page drops it, as it always has. + actions: Component { + SaveBar { dirty: up.changed.length > 0; valid: up.loadError === ""; busy: up.busy + onSave: up.save(); onDiscard: up.discard() } + } + UserPage { id: up; width: parent.width; owner: um; onMoreGit: pg.go("git") } } } } diff --git a/desktop/modules/user/UserPage.qml b/desktop/modules/user/UserPage.qml index f7a2289..1cc0c1e 100644 --- a/desktop/modules/user/UserPage.qml +++ b/desktop/modules/user/UserPage.qml @@ -62,6 +62,12 @@ Column { property string loadError: "" readonly property var changed: Object.keys(draft).filter(k => draft[k] !== saved[k] || !!errors[k]) + // Back to what is saved, for the header's Discard. + function discard() { + form.draft = Object.assign({}, form.saved); + form.errors = ({}); + } + function set(key, value) { const d = Object.assign({}, form.draft); d[key] = value; @@ -410,10 +416,5 @@ Column { onClicked: form.moreGit() } - Button { - anchors.right: parent.right - text: form.busy ? "Saving…" : "Save" - enabled: !form.busy && form.loadError === "" && form.changed.length > 0 - onClicked: form.save() - } + // Save and Discard are in the page header, with every settings page's. } |
