// 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. pragma Singleton import Quickshell import Quickshell.Io import QtQuick import "SceneIcons.js" as Icons // The scene-icon map from ~/.config, watched so a settings edit or a hand edit // recolours the pill with no restart. The starter map covers a missing file. Singleton { id: root readonly property string path: (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/quickshell/obs-scene-icons.json" property var map: Icons.defaults // The settings page's draft, null when not editing. Every edit reassigns // draft so the page's bindings fire; Save normalizes and writes. property var draft: null property bool dirty: false readonly property bool valid: draft !== null && Icons.validMap(draft) // Emitted when the draft is dropped, so the page rebuilds its fields: // TextInput breaks its text binding on user edit, so a Discard would // otherwise leave the old glyph on screen. signal reset function iconFor(name) { return Icons.iconFor(root.map, name); } function validGlyph(s) { return Icons.validGlyph(s); } function begin() { if (!root.dirty) root.draft = Icons.parse(Icons.serialize(root.map)); } function editScene(name, text) { if (!root.draft) root.begin(); const scenes = Object.assign({}, root.draft.scenes); if (text === "") delete scenes[name]; else scenes[name] = text; root.draft = Object.assign({}, root.draft, { scenes: scenes }); root.dirty = true; } function editGeneric(text) { if (!root.draft) root.begin(); root.draft = Object.assign({}, root.draft, { generic: text }); root.dirty = true; } function discard() { root.draft = null; root.dirty = false; root.reset(); } function save(map) { if (!map) return; const out = Icons.normalize(map); root.map = out; fv.setText(Icons.serialize(out)); root.draft = null; root.dirty = false; root.reset(); } FileView { id: fv path: root.path atomicWrites: true watchChanges: true onFileChanged: reload() onLoaded: root.map = Icons.parse(text()) onLoadFailed: root.map = Icons.defaults } }