From 84702465d7c0368705c7d18318b11ebe2edb369f Mon Sep 17 00:00:00 2001 From: LemmyCook Date: Sun, 21 Sep 2025 15:40:41 -0400 Subject: [PATCH] wip: default settings --- Commons/Settings.qml | 559 ++++++++++++++++++++++--------------------- 1 file changed, 291 insertions(+), 268 deletions(-) diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 5e7d8192..43663086 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -36,6 +36,297 @@ Singleton { // Signal emitted when settings are loaded after startupcale changes signal settingsLoaded + // ----------------------------------------------------- + // ----------------------------------------------------- + // Ensure directories exist before FileView tries to read files + Component.onCompleted: { + // ensure settings dir exists + Quickshell.execDetached(["mkdir", "-p", configDir]) + Quickshell.execDetached(["mkdir", "-p", cacheDir]) + + Quickshell.execDetached(["mkdir", "-p", cacheDirImagesWallpapers]) + Quickshell.execDetached(["mkdir", "-p", cacheDirImagesNotifications]) + + // Mark directories as created and trigger file loading + directoriesCreated = true + + generateDefaultSettings(); + + settingsFileView.adapter = adapter; + + + } + + // Don't write settings to disk immediately + // This avoid excessive IO when a variable changes rapidly (ex: sliders) + Timer { + id: saveTimer + running: false + interval: 1000 + onTriggered: settingsFileView.writeAdapter() + } + + FileView { + id: settingsFileView + path: directoriesCreated ? settingsFile : undefined + printErrors: false + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: saveTimer.start() + + // Trigger initial load when path changes from empty to actual path + onPathChanged: { + if (path !== undefined) { + reload() + } + } + onLoaded: function () { + if (!isLoaded) { + Logger.log("Settings", "----------------------------") + Logger.log("Settings", "Settings loaded successfully") + + upgradeSettingsData() + + validateMonitorConfigurations() + + kickOffServices() + + isLoaded = true + + // Emit the signal + root.settingsLoaded() + } + } + onLoadFailed: function (error) { + if (error.toString().includes("No such file") || error === 2) + // File doesn't exist, create it with default values + writeAdapter() + } + } + JsonAdapter { + id: adapter + + property int settingsVersion: 3 + + // bar + property JsonObject bar: JsonObject { + property string position: "top" // "top", "bottom", "left", or "right" + property real backgroundOpacity: 1.0 + property list monitors: [] + property string density: "default" // "compact", "default", "comfortable" + property bool showCapsule: true + + // Floating bar settings + property bool floating: false + property real marginVertical: 0.25 + property real marginHorizontal: 0.25 + + // Widget configuration for modular bar system + property JsonObject widgets + widgets: JsonObject { + property list left: [{ + "id": "SystemMonitor" + }, { + "id": "ActiveWindow" + }, { + "id": "MediaMini" + }] + property list center: [{ + "id": "Workspace" + }] + property list right: [{ + "id": "ScreenRecorderIndicator" + }, { + "id": "Tray" + }, { + "id": "NotificationHistory" + }, { + "id": "WiFi" + }, { + "id": "Bluetooth" + }, { + "id": "Battery" + }, { + "id": "Volume" + }, { + "id": "Brightness" + }, { + "id": "NightLight" + }, { + "id": "Clock" + }, { + "id": "SidePanelToggle" + }] + } + } + + // general + property JsonObject general: JsonObject { + property string avatarImage: defaultAvatar + property bool dimDesktop: true + property bool showScreenCorners: false + property bool forceBlackScreenCorners: false + property real radiusRatio: 1.0 + property real screenRadiusRatio: 1.0 + property real animationSpeed: 1.0 + } + + // location + property JsonObject location: JsonObject { + property string name: defaultLocation + property bool useFahrenheit: false + property bool use12hourFormat: false + property bool showWeekNumberInCalendar: false + } + + // screen recorder + property JsonObject screenRecorder: JsonObject { + property string directory: defaultVideosDirectory + property int frameRate: 60 + property string audioCodec: "opus" + property string videoCodec: "h264" + property string quality: "very_high" + property string colorRange: "limited" + property bool showCursor: true + property string audioSource: "default_output" + property string videoSource: "portal" + } + + // wallpaper + property JsonObject wallpaper: JsonObject { + property bool enabled: true + property string directory: defaultWallpapersDirectory + property bool enableMultiMonitorDirectories: false + property bool setWallpaperOnAllMonitors: true + property string fillMode: "crop" + property color fillColor: "#000000" + property bool randomEnabled: false + property int randomIntervalSec: 300 // 5 min + property int transitionDuration: 1500 // 1500 ms + property string transitionType: "random" + property real transitionEdgeSmoothness: 0.05 + property list monitors: [] + } + + // applauncher + property JsonObject appLauncher: JsonObject { + property bool enableClipboardHistory: false + // Position: center, top_left, top_right, bottom_left, bottom_right, bottom_center, top_center + property string position: "center" + property real backgroundOpacity: 1.0 + property list pinnedExecs: [] + property bool useApp2Unit: false + property bool sortByMostUsed: true + } + + // dock + property JsonObject dock: JsonObject { + property bool autoHide: false + property bool exclusive: false + property real backgroundOpacity: 1.0 + property real floatingRatio: 1.0 + property list monitors: [] + } + + // network + property JsonObject network: JsonObject { + property bool wifiEnabled: true + property bool bluetoothEnabled: true + } + + // notifications + property JsonObject notifications: JsonObject { + property bool doNotDisturb: false + property list monitors: [] + // Last time the user opened the notification history (ms since e899999999999998poch) + property real lastSeenTs: 0 + // Duration settings for different urgency levels (in seconds) + property int lowUrgencyDuration: 3 + property int normalUrgencyDuration: 8 + property int criticalUrgencyDuration: 15 + } + + // audio + property JsonObject audio: JsonObject { + property int volumeStep: 5 + property int cavaFrameRate: 60 + property string visualizerType: "linear" + property list mprisBlacklist: [] + property string preferredPlayer: "" + } + + // ui + property JsonObject ui: JsonObject { + property string fontDefault: "Roboto" + property string fontFixed: "DejaVu Sans Mono" + property string fontBillboard: "Inter" + property list monitorsScaling: [] + property bool idleInhibitorEnabled: false + } + + // brightness + property JsonObject brightness: JsonObject { + property int brightnessStep: 5 + } + + property JsonObject colorSchemes: JsonObject { + property bool useWallpaperColors: false + property string predefinedScheme: "" + property bool darkMode: true + } + + // matugen templates toggles + property JsonObject matugen: JsonObject { + // Per-template flags to control dynamic config generation + property bool gtk4: false + property bool gtk3: false + property bool qt6: false + property bool qt5: false + property bool kitty: false + property bool ghostty: false + property bool foot: false + property bool fuzzel: false + property bool vesktop: false + property bool pywalfox: false + property bool enableUserTemplates: false + } + + // night light + property JsonObject nightLight: JsonObject { + property bool enabled: false + property bool forced: false + property bool autoSchedule: true + property string nightTemp: "4000" + property string dayTemp: "6500" + property string manualSunrise: "06:30" + property string manualSunset: "18:30" + } + + // hooks + property JsonObject hooks: JsonObject { + property bool enabled: false + property string wallpaperChange: "" + property string darkModeChange: "" + } + } + + function generateDefaultSettings() { + try { + Logger.log("Settings", "Generating default settings file...") + + var defaultPath = configDir + "settings-default.json" + var jsonData = JSON.stringify(adapter, null, 2) + var tempFile = "/tmp/noctalia-default-settings.json" + + // Write to temp file first, then copy to final location + Quickshell.execDetached(["sh", "-c", `echo '${jsonData}' > "${tempFile}" && cp "${tempFile}" "${defaultPath}" && rm "${tempFile}"`]) + + Logger.log("Settings", "Generated settings-default.json with default values") + } catch (error) { + Logger.error("Settings", "Failed to generate default settings file: " + error) + } + } + // ----------------------------------------------------- // Function to validate monitor configurations function validateMonitorConfigurations() { @@ -197,272 +488,4 @@ Singleton { BluetoothService.init() } - - // ----------------------------------------------------- - // Ensure directories exist before FileView tries to read files - Component.onCompleted: { - // ensure settings dir exists - Quickshell.execDetached(["mkdir", "-p", configDir]) - Quickshell.execDetached(["mkdir", "-p", cacheDir]) - - Quickshell.execDetached(["mkdir", "-p", cacheDirImagesWallpapers]) - Quickshell.execDetached(["mkdir", "-p", cacheDirImagesNotifications]) - - // Mark directories as created and trigger file loading - directoriesCreated = true - } - - // Don't write settings to disk immediately - // This avoid excessive IO when a variable changes rapidly (ex: sliders) - Timer { - id: saveTimer - running: false - interval: 1000 - onTriggered: settingsFileView.writeAdapter() - } - - FileView { - id: settingsFileView - path: directoriesCreated ? settingsFile : undefined - printErrors: false - watchChanges: true - onFileChanged: reload() - onAdapterUpdated: saveTimer.start() - - // Trigger initial load when path changes from empty to actual path - onPathChanged: { - if (path !== undefined) { - reload() - } - } - onLoaded: function () { - if (!isLoaded) { - Logger.log("Settings", "----------------------------") - Logger.log("Settings", "Settings loaded successfully") - - upgradeSettingsData() - - validateMonitorConfigurations() - - kickOffServices() - - isLoaded = true - - // Emit the signal - root.settingsLoaded() - } - } - onLoadFailed: function (error) { - if (error.toString().includes("No such file") || error === 2) - // File doesn't exist, create it with default values - writeAdapter() - } - - JsonAdapter { - id: adapter - - property int settingsVersion: 3 - - // bar - property JsonObject bar: JsonObject { - property string position: "top" // "top", "bottom", "left", or "right" - property real backgroundOpacity: 1.0 - property list monitors: [] - property string density: "default" // "compact", "default", "comfortable" - property bool showCapsule: true - - // Floating bar settings - property bool floating: false - property real marginVertical: 0.25 - property real marginHorizontal: 0.25 - - // Widget configuration for modular bar system - property JsonObject widgets - widgets: JsonObject { - property list left: [{ - "id": "SystemMonitor" - }, { - "id": "ActiveWindow" - }, { - "id": "MediaMini" - }] - property list center: [{ - "id": "Workspace" - }] - property list right: [{ - "id": "ScreenRecorderIndicator" - }, { - "id": "Tray" - }, { - "id": "NotificationHistory" - }, { - "id": "WiFi" - }, { - "id": "Bluetooth" - }, { - "id": "Battery" - }, { - "id": "Volume" - }, { - "id": "Brightness" - }, { - "id": "NightLight" - }, { - "id": "Clock" - }, { - "id": "SidePanelToggle" - }] - } - } - - // general - property JsonObject general: JsonObject { - property string avatarImage: defaultAvatar - property bool dimDesktop: true - property bool showScreenCorners: false - property bool forceBlackScreenCorners: false - property real radiusRatio: 1.0 - property real screenRadiusRatio: 1.0 - property real animationSpeed: 1.0 - } - - // location - property JsonObject location: JsonObject { - property string name: defaultLocation - property bool useFahrenheit: false - property bool use12hourFormat: false - property bool showWeekNumberInCalendar: false - } - - // screen recorder - property JsonObject screenRecorder: JsonObject { - property string directory: defaultVideosDirectory - property int frameRate: 60 - property string audioCodec: "opus" - property string videoCodec: "h264" - property string quality: "very_high" - property string colorRange: "limited" - property bool showCursor: true - property string audioSource: "default_output" - property string videoSource: "portal" - } - - // wallpaper - property JsonObject wallpaper: JsonObject { - property bool enabled: true - property string directory: defaultWallpapersDirectory - property bool enableMultiMonitorDirectories: false - property bool setWallpaperOnAllMonitors: true - property string fillMode: "crop" - property color fillColor: "#000000" - property bool randomEnabled: false - property int randomIntervalSec: 300 // 5 min - property int transitionDuration: 1500 // 1500 ms - property string transitionType: "random" - property real transitionEdgeSmoothness: 0.05 - property list monitors: [] - } - - // applauncher - property JsonObject appLauncher: JsonObject { - property bool enableClipboardHistory: false - // Position: center, top_left, top_right, bottom_left, bottom_right, bottom_center, top_center - property string position: "center" - property real backgroundOpacity: 1.0 - property list pinnedExecs: [] - property bool useApp2Unit: false - property bool sortByMostUsed: true - } - - // dock - property JsonObject dock: JsonObject { - property bool autoHide: false - property bool exclusive: false - property real backgroundOpacity: 1.0 - property real floatingRatio: 1.0 - property list monitors: [] - } - - // network - property JsonObject network: JsonObject { - property bool wifiEnabled: true - property bool bluetoothEnabled: true - } - - // notifications - property JsonObject notifications: JsonObject { - property bool doNotDisturb: false - property list monitors: [] - // Last time the user opened the notification history (ms since e899999999999998poch) - property real lastSeenTs: 0 - // Duration settings for different urgency levels (in seconds) - property int lowUrgencyDuration: 3 - property int normalUrgencyDuration: 8 - property int criticalUrgencyDuration: 15 - } - - // audio - property JsonObject audio: JsonObject { - property int volumeStep: 5 - property int cavaFrameRate: 60 - property string visualizerType: "linear" - property list mprisBlacklist: [] - property string preferredPlayer: "" - } - - // ui - property JsonObject ui: JsonObject { - property string fontDefault: "Roboto" - property string fontFixed: "DejaVu Sans Mono" - property string fontBillboard: "Inter" - property list monitorsScaling: [] - property bool idleInhibitorEnabled: false - } - - // brightness - property JsonObject brightness: JsonObject { - property int brightnessStep: 5 - } - - property JsonObject colorSchemes: JsonObject { - property bool useWallpaperColors: false - property string predefinedScheme: "" - property bool darkMode: true - } - - // matugen templates toggles - property JsonObject matugen: JsonObject { - // Per-template flags to control dynamic config generation - property bool gtk4: false - property bool gtk3: false - property bool qt6: false - property bool qt5: false - property bool kitty: false - property bool ghostty: false - property bool foot: false - property bool fuzzel: false - property bool vesktop: false - property bool pywalfox: false - property bool enableUserTemplates: false - } - - // night light - property JsonObject nightLight: JsonObject { - property bool enabled: false - property bool forced: false - property bool autoSchedule: true - property string nightTemp: "4000" - property string dayTemp: "6500" - property string manualSunrise: "06:30" - property string manualSunset: "18:30" - } - - // hooks - property JsonObject hooks: JsonObject { - property bool enabled: false - property string wallpaperChange: "" - property string darkModeChange: "" - } - } - } }