diff --git a/.config/quickshell/modules/common/functions/object_utils.js b/.config/quickshell/modules/common/functions/object_utils.js index 51eed7f9..96c632fd 100644 --- a/.config/quickshell/modules/common/functions/object_utils.js +++ b/.config/quickshell/modules/common/functions/object_utils.js @@ -1,7 +1,3 @@ -function trimFileProtocol(str) { - return str.startsWith("file://") ? str.slice(7) : str; -} - function toPlainObject(qtObj) { if (qtObj === null || typeof qtObj !== "object") return qtObj; @@ -26,4 +22,24 @@ function toPlainObject(qtObj) { } } return result; -} \ No newline at end of file +} + +function applyToQtObject(qtObj, jsonObj) { + if (!qtObj || typeof jsonObj !== "object" || jsonObj === null) return; + + for (let key in jsonObj) { + if (!qtObj.hasOwnProperty(key)) continue; + + // Check if the property is a QtObject (not a value) + const value = qtObj[key]; + const jsonValue = jsonObj[key]; + + // If it's an object and not an array, recurse + if (value && typeof value === "object" && !Array.isArray(value)) { + applyToQtObject(value, jsonValue); + } else { + // Otherwise, assign the value + qtObj[key] = jsonValue; + } + } +} diff --git a/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml b/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml index c96bbd73..882d3a03 100644 --- a/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml +++ b/.config/quickshell/modules/sidebarRight/BottomWidgetGroup.qml @@ -29,13 +29,8 @@ Rectangle { } } - Component.onCompleted: { - bottomWidgetGroupRow.opacity = !collapsed - collapsedBottomWidgetGroupRow.opacity = collapsed - } - function setCollapsed(state) { - PersistentStates.sidebar.bottomGroup.collapsed = state + PersistentStateManager.setState("sidebar.bottomGroup.collapsed", state) if (collapsed) { bottomWidgetGroupRow.opacity = 0 } @@ -70,6 +65,7 @@ Rectangle { // The thing when collapsed RowLayout { id: collapsedBottomWidgetGroupRow + opacity: collapsed ? 1 : 0 visible: opacity > 0 Behavior on opacity { NumberAnimation { @@ -111,6 +107,7 @@ Rectangle { RowLayout { id: bottomWidgetGroupRow + opacity: collapsed ? 0 : 1 visible: opacity > 0 Behavior on opacity { NumberAnimation { diff --git a/.config/quickshell/services/ConfigLoader.qml b/.config/quickshell/services/ConfigLoader.qml index 176dfd0c..1bc940d9 100644 --- a/.config/quickshell/services/ConfigLoader.qml +++ b/.config/quickshell/services/ConfigLoader.qml @@ -25,27 +25,7 @@ Singleton { try { const json = JSON.parse(fileContent); - function applyToQtObject(qtObj, jsonObj) { - if (!qtObj || typeof jsonObj !== "object" || jsonObj === null) return; - - for (let key in jsonObj) { - if (!qtObj.hasOwnProperty(key)) continue; - - // Check if the property is a QtObject (not a value) - const value = qtObj[key]; - const jsonValue = jsonObj[key]; - - // If it's an object and not an array, recurse - if (value && typeof value === "object" && !Array.isArray(value)) { - applyToQtObject(value, jsonValue); - } else { - // Otherwise, assign the value - qtObj[key] = jsonValue; - } - } - } - - applyToQtObject(ConfigOptions, json); + ObjectUtils.applyToQtObject(ConfigOptions, json); if (root.firstLoad) { root.firstLoad = false; } else { diff --git a/.config/quickshell/services/PersistentStateManager.qml b/.config/quickshell/services/PersistentStateManager.qml new file mode 100644 index 00000000..a69702a7 --- /dev/null +++ b/.config/quickshell/services/PersistentStateManager.qml @@ -0,0 +1,100 @@ +pragma Singleton +pragma ComponentBehavior: Bound + +import "root:/modules/common" +import "root:/modules/common/functions/object_utils.js" as ObjectUtils +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Hyprland +import Qt.labs.platform + +Singleton { + id: root + property string fileDir: `${StandardPaths.standardLocations(StandardPaths.StateLocation)[0]}` + property string fileName: "states.json" + property string filePath: `${root.fileDir}/${root.fileName}` + + function getState(nestedKey) { + let keys = nestedKey.split("."); + let obj = PersistentStates; + for (let i = 0; i < keys.length; ++i) { + if (obj[keys[i]] === undefined) { + console.error(`[PersistentStateManager] Key "${keys[i]}" not found in PersistentStates`); + return null; + } + obj = obj[keys[i]]; + } + return obj; + } + + function setState(nestedKey, value) { + let keys = nestedKey.split("."); + let obj = PersistentStates; + let parents = [obj]; + + // Traverse and collect parent objects + for (let i = 0; i < keys.length - 1; ++i) { + if (!obj[keys[i]] || typeof obj[keys[i]] !== "object") { + obj[keys[i]] = {}; + } + obj = obj[keys[i]]; + parents.push(obj); + } + + // Set the value at the innermost key + obj[keys[keys.length - 1]] = value; + + saveStates() + } + + function loadStates() { + stateFileView.reload() + } + + function saveStates() { + console.log("[PersistentStateManager] Saving states to file:", root.filePath) + const plainStates = ObjectUtils.toPlainObject(PersistentStates) + stateFileView.setText(JSON.stringify(plainStates, null, 2)) + } + + function applyStates(fileContent) { + try { + const json = JSON.parse(fileContent); + + ObjectUtils.applyToQtObject(PersistentStates, json); + } catch (e) { + console.error("[PersistentStateManager] Error reading file:", e); + return; + } + } + + Timer { + id: delayedFileRead + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + running: false + onTriggered: { + root.applyStates(stateFileView.text()) + } + } + + FileView { + id: stateFileView + path: root.filePath + watchChanges: true + // onFileChanged: { + // console.log("[PersistentStateManager] File changed, reloading...") + // this.reload() + // delayedFileRead.start() + // } + onLoadedChanged: { + const fileContent = stateFileView.text() + root.applyStates(fileContent) + } + onLoadFailed: (error) => { + console.log("[PersistentStateManager] File not found, creating new file") + root.saveStates() + } + } +} diff --git a/.config/quickshell/shell.qml b/.config/quickshell/shell.qml index 7bc831b8..f6b60478 100644 --- a/.config/quickshell/shell.qml +++ b/.config/quickshell/shell.qml @@ -19,6 +19,7 @@ ShellRoot { Component.onCompleted: { MaterialThemeLoader.reapplyTheme() ConfigLoader.loadConfig() + PersistentStateManager.loadStates() } Bar {}