diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 02554f3..771cbf9 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -1,4 +1,5 @@ pragma Singleton + import QtQuick import Quickshell import Quickshell.Io @@ -6,243 +7,249 @@ import qs.Commons import qs.Services Singleton { - id: root + id: root - // Define our app directories - // Default config directory: ~/.config/noctalia - // Default cache directory: ~/.cache/noctalia - property string shellName: "noctalia" - property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") || Quickshell.env("HOME") + "/.config") + "/" + shellName + "/" - property string cacheDir: Quickshell.env("NOCTALIA_CACHE_DIR") || (Quickshell.env("XDG_CACHE_HOME") || Quickshell.env("HOME") + "/.cache") + "/" + shellName + "/" - property string cacheDirImages: cacheDir + "images/" + // Define our app directories + // Default config directory: ~/.config/noctalia + // Default cache directory: ~/.cache/noctalia + property string shellName: "noctalia" + property string configDir: Quickshell.env("NOCTALIA_CONFIG_DIR") || (Quickshell.env("XDG_CONFIG_HOME") + || Quickshell.env( + "HOME") + "/.config") + "/" + shellName + "/" + property string cacheDir: Quickshell.env("NOCTALIA_CACHE_DIR") || (Quickshell.env("XDG_CACHE_HOME") || Quickshell.env( + "HOME") + "/.cache") + "/" + shellName + "/" + property string cacheDirImages: cacheDir + "images/" - property string settingsFile: Quickshell.env("NOCTALIA_SETTINGS_FILE") || (configDir + "settings.json") + property string settingsFile: Quickshell.env("NOCTALIA_SETTINGS_FILE") || (configDir + "settings.json") - property string defaultWallpaper: Qt.resolvedUrl("../Assets/Tests/wallpaper.png") - property string defaultAvatar: Quickshell.env("HOME") + "/.face" + property string defaultWallpaper: Qt.resolvedUrl("../Assets/Tests/wallpaper.png") + property string defaultAvatar: Quickshell.env("HOME") + "/.face" - // Used to access via Settings.data.xxx.yyy - property alias data: adapter + // Used to access via Settings.data.xxx.yyy + property alias data: adapter - // Flag to prevent unnecessary wallpaper calls during reloads - property bool isInitialLoad: true + // Flag to prevent unnecessary wallpaper calls during reloads + property bool isInitialLoad: true - // Function to validate monitor configurations - function validateMonitorConfigurations() { - var availableScreenNames = []; - for (var i = 0; i < Quickshell.screens.length; i++) { - availableScreenNames.push(Quickshell.screens[i].name); - } - - Logger.log("Settings", "Available monitors: [" + availableScreenNames.join(", ") + "]"); - Logger.log("Settings", "Configured bar monitors: [" + adapter.bar.monitors.join(", ") + "]"); - - // Check bar monitors - if (adapter.bar.monitors.length > 0) { - var hasValidBarMonitor = false; - for (var j = 0; j < adapter.bar.monitors.length; j++) { - if (availableScreenNames.includes(adapter.bar.monitors[j])) { - hasValidBarMonitor = true; - break; - } - } - if (!hasValidBarMonitor) { - Logger.log("Settings", "No configured bar monitors found on system, clearing bar monitor list to show on all screens"); - adapter.bar.monitors = []; - } else { - Logger.log("Settings", "Found valid bar monitors, keeping configuration"); - } - } else { - Logger.log("Settings", "Bar monitor list is empty, will show on all available screens"); - } - } - Item { - Component.onCompleted: { - - // ensure settings dir exists - Quickshell.execDetached(["mkdir", "-p", configDir]); - Quickshell.execDetached(["mkdir", "-p", cacheDir]); - Quickshell.execDetached(["mkdir", "-p", cacheDirImages]); - } + // Function to validate monitor configurations + function validateMonitorConfigurations() { + var availableScreenNames = [] + for (var i = 0; i < Quickshell.screens.length; i++) { + availableScreenNames.push(Quickshell.screens[i].name) } - // 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() + Logger.log("Settings", "Available monitors: [" + availableScreenNames.join(", ") + "]") + Logger.log("Settings", "Configured bar monitors: [" + adapter.bar.monitors.join(", ") + "]") + + // Check bar monitors + if (adapter.bar.monitors.length > 0) { + var hasValidBarMonitor = false + for (var j = 0; j < adapter.bar.monitors.length; j++) { + if (availableScreenNames.includes(adapter.bar.monitors[j])) { + hasValidBarMonitor = true + break + } + } + if (!hasValidBarMonitor) { + Logger.log("Settings", + "No configured bar monitors found on system, clearing bar monitor list to show on all screens") + adapter.bar.monitors = [] + } else { + Logger.log("Settings", "Found valid bar monitors, keeping configuration") + } + } else { + Logger.log("Settings", "Bar monitor list is empty, will show on all available screens") + } + } + Item { + Component.onCompleted: { + + // ensure settings dir exists + Quickshell.execDetached(["mkdir", "-p", configDir]) + Quickshell.execDetached(["mkdir", "-p", cacheDir]) + Quickshell.execDetached(["mkdir", "-p", cacheDirImages]) + } + } + + // 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: settingsFile + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: saveTimer.start() + Component.onCompleted: function () { + reload() + } + onLoaded: function () { + Qt.callLater(function () { + if (isInitialLoad) { + Logger.log("Settings", "OnLoaded") + // Only set wallpaper on initial load, not on reloads + if (adapter.wallpaper.current !== "") { + Logger.log("Settings", "Set current wallpaper", adapter.wallpaper.current) + WallpaperService.setCurrentWallpaper(adapter.wallpaper.current, true) + } + + // Validate monitor configurations, only once + // if none of the configured monitors exist, clear the lists + validateMonitorConfigurations() + } + + isInitialLoad = false + }) + } + onLoadFailed: function (error) { + if (error.toString().includes("No such file") || error === 2) + // File doesn't exist, create it with default values + writeAdapter() } - FileView { - id: settingsFileView - path: settingsFile - watchChanges: true - onFileChanged: reload() - onAdapterUpdated: saveTimer.start() - Component.onCompleted: function () { - reload(); + JsonAdapter { + id: adapter + + // bar + property JsonObject bar: JsonObject { + property string position: "top" // Possible values: "top", "bottom" + property bool showActiveWindowIcon: true + property bool alwaysShowBatteryPercentage: false + property real backgroundOpacity: 1.0 + property list monitors: [] + + // Widget configuration for modular bar system + property JsonObject widgets + widgets: JsonObject { + property list left: ["SystemMonitor", "ActiveWindow", "MediaMini"] + property list center: ["Workspace"] + property list right: ["ScreenRecorderIndicator", "Tray", "NotificationHistory", "WiFi", "Bluetooth", "Battery", "Volume", "Brightness", "Clock", "SidePanelToggle"] } - onLoaded: function () { - Qt.callLater(function () { - if (isInitialLoad) { - Logger.log("Settings", "OnLoaded"); - // Only set wallpaper on initial load, not on reloads - if (adapter.wallpaper.current !== "") { - Logger.log("Settings", "Set current wallpaper", adapter.wallpaper.current); - WallpaperService.setCurrentWallpaper(adapter.wallpaper.current, true); - } + } - // Validate monitor configurations, only once - // if none of the configured monitors exist, clear the lists - validateMonitorConfigurations(); - } + // general + property JsonObject general: JsonObject { + property string avatarImage: defaultAvatar + property bool dimDesktop: false + property bool showScreenCorners: false + property real radiusRatio: 1.0 + } - isInitialLoad = false; - }); - } - onLoadFailed: function (error) { - if (error.toString().includes("No such file") || error === 2) - // File doesn't exist, create it with default values - writeAdapter(); + // location + property JsonObject location: JsonObject { + property string name: "Tokyo" + property bool useFahrenheit: false + property bool reverseDayMonth: false + property bool use12HourClock: false + property bool showDateWithClock: false + } + + // screen recorder + property JsonObject screenRecorder: JsonObject { + property string directory: "~/Videos" + 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 string directory: "/usr/share/wallpapers" + property string current: "" + property bool isRandom: false + property int randomInterval: 300 + property JsonObject swww + + onDirectoryChanged: WallpaperService.listWallpapers() + onIsRandomChanged: WallpaperService.toggleRandomWallpaper() + onRandomIntervalChanged: WallpaperService.restartRandomWallpaperTimer() + + swww: JsonObject { + property bool enabled: false + property string resizeMethod: "crop" + property int transitionFps: 60 + property string transitionType: "random" + property real transitionDuration: 1.1 } + } - JsonAdapter { - id: adapter + // applauncher + property JsonObject appLauncher: JsonObject { + // When disabled, Launcher hides clipboard command and ignores cliphist + property bool enableClipboardHistory: true + // Position: center, top_left, top_right, bottom_left, bottom_right + property string position: "center" + property list pinnedExecs: [] + } - // bar - property JsonObject bar: JsonObject { - property string position: "top" // Possible values: "top", "bottom" - property bool showActiveWindowIcon: true - property bool alwaysShowBatteryPercentage: false - property real backgroundOpacity: 1.0 - property list monitors: [] + // dock + property JsonObject dock: JsonObject { + property bool autoHide: false + property bool exclusive: false + property list monitors: [] + } - // Widget configuration for modular bar system - property JsonObject widgets - widgets: JsonObject { - property list left: ["SystemMonitor", "ActiveWindow", "MediaMini"] - property list center: ["Workspace"] - property list right: ["ScreenRecorderIndicator", "Tray", "NotificationHistory", "WiFi", "Bluetooth", "Battery", "Volume", "Brightness", "Clock", "SidePanelToggle"] - } - } + // network + property JsonObject network: JsonObject { + property bool wifiEnabled: true + property bool bluetoothEnabled: true + } - // general - property JsonObject general: JsonObject { - property string avatarImage: defaultAvatar - property bool dimDesktop: false - property bool showScreenCorners: false - property real radiusRatio: 1.0 - } + // notifications + property JsonObject notifications: JsonObject { + property list monitors: [] + } - // location - property JsonObject location: JsonObject { - property string name: "Tokyo" - property bool useFahrenheit: false - property bool reverseDayMonth: false - property bool use12HourClock: false - property bool showDateWithClock: false - } + // audio + property JsonObject audio: JsonObject { + property bool showMiniplayerAlbumArt: false + property bool showMiniplayerCava: false + property string visualizerType: "linear" + property int volumeStep: 5 + property int cavaFrameRate: 60 + } - // screen recorder - property JsonObject screenRecorder: JsonObject { - property string directory: "~/Videos" - 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" - } + // ui + property JsonObject ui: JsonObject { + property string fontDefault: "Roboto" // Default font for all text + property string fontFixed: "DejaVu Sans Mono" // Fixed width font for terminal + property string fontBillboard: "Inter" // Large bold font for clocks and prominent displays - // wallpaper - property JsonObject wallpaper: JsonObject { - property string directory: "/usr/share/wallpapers" - property string current: "" - property bool isRandom: false - property int randomInterval: 300 - property JsonObject swww + // Legacy compatibility + property string fontFamily: fontDefault // Keep for backward compatibility - onDirectoryChanged: WallpaperService.listWallpapers() - onIsRandomChanged: WallpaperService.toggleRandomWallpaper() - onRandomIntervalChanged: WallpaperService.restartRandomWallpaperTimer() + // Idle inhibitor state + property bool idleInhibitorEnabled: false + } - swww: JsonObject { - property bool enabled: false - property string resizeMethod: "crop" - property int transitionFps: 60 - property string transitionType: "random" - property real transitionDuration: 1.1 - } - } + // Scaling (not stored inside JsonObject, or it crashes) + property var monitorsScaling: { - // applauncher - property JsonObject appLauncher: JsonObject { - // When disabled, Launcher hides clipboard command and ignores cliphist - property bool enableClipboardHistory: true - // Position: center, top_left, top_right, bottom_left, bottom_right - property string position: "center" - property list pinnedExecs: [] - } + } - // dock - property JsonObject dock: JsonObject { - property bool autoHide: false - property bool exclusive: false - property list monitors: [] - } + // brightness + property JsonObject brightness: JsonObject { + property int brightnessStep: 5 + } - // network - property JsonObject network: JsonObject { - property bool wifiEnabled: true - property bool bluetoothEnabled: true - } - - // notifications - property JsonObject notifications: JsonObject { - property list monitors: [] - } - - // audio - property JsonObject audio: JsonObject { - property bool showMiniplayerAlbumArt: false - property bool showMiniplayerCava: false - property string visualizerType: "linear" - property int volumeStep: 5 - property int cavaFrameRate: 60 - } - - // ui - property JsonObject ui: JsonObject { - property string fontDefault: "Roboto" // Default font for all text - property string fontFixed: "DejaVu Sans Mono" // Fixed width font for terminal - property string fontBillboard: "Inter" // Large bold font for clocks and prominent displays - - // Legacy compatibility - property string fontFamily: fontDefault // Keep for backward compatibility - - // Idle inhibitor state - property bool idleInhibitorEnabled: false - } - - // Scaling (not stored inside JsonObject, or it crashes) - property var monitorsScaling: {} - - // brightness - property JsonObject brightness: JsonObject { - property int brightnessStep: 5 - } - - property JsonObject colorSchemes: JsonObject { - property bool useWallpaperColors: false - property string predefinedScheme: "" - property bool darkMode: true - // External app theming (GTK & Qt) - property bool themeApps: false - } - } + property JsonObject colorSchemes: JsonObject { + property bool useWallpaperColors: false + property string predefinedScheme: "" + property bool darkMode: true + // External app theming (GTK & Qt) + property bool themeApps: false + } } + } } diff --git a/Modules/Bar/Bar.qml b/Modules/Bar/Bar.qml index 43b9a5a..48ed76e 100644 --- a/Modules/Bar/Bar.qml +++ b/Modules/Bar/Bar.qml @@ -65,9 +65,12 @@ Variants { sourceComponent: widgetLoader.getWidgetComponent(modelData) active: true visible: { - if (modelData === "WiFi" && !Settings.data.network.wifiEnabled) return false - if (modelData === "Bluetooth" && !Settings.data.network.bluetoothEnabled) return false - if (modelData === "Battery" && !shouldShowBattery()) return false + if (modelData === "WiFi" && !Settings.data.network.wifiEnabled) + return false + if (modelData === "Bluetooth" && !Settings.data.network.bluetoothEnabled) + return false + if (modelData === "Battery" && !shouldShowBattery()) + return false return true } anchors.verticalCenter: parent.verticalCenter @@ -98,9 +101,12 @@ Variants { sourceComponent: widgetLoader.getWidgetComponent(modelData) active: true visible: { - if (modelData === "WiFi" && !Settings.data.network.wifiEnabled) return false - if (modelData === "Bluetooth" && !Settings.data.network.bluetoothEnabled) return false - if (modelData === "Battery" && !shouldShowBattery()) return false + if (modelData === "WiFi" && !Settings.data.network.wifiEnabled) + return false + if (modelData === "Bluetooth" && !Settings.data.network.bluetoothEnabled) + return false + if (modelData === "Battery" && !shouldShowBattery()) + return false return true } anchors.verticalCenter: parent.verticalCenter @@ -132,8 +138,10 @@ Variants { sourceComponent: widgetLoader.getWidgetComponent(modelData) active: true visible: { - if (modelData === "WiFi" && !Settings.data.network.wifiEnabled) return false - if (modelData === "Bluetooth" && !Settings.data.network.bluetoothEnabled) return false + if (modelData === "WiFi" && !Settings.data.network.wifiEnabled) + return false + if (modelData === "Bluetooth" && !Settings.data.network.bluetoothEnabled) + return false return true } anchors.verticalCenter: parent.verticalCenter diff --git a/Modules/Bar/Widgets/ActiveWindow.qml b/Modules/Bar/Widgets/ActiveWindow.qml index 990f532..904b5b4 100644 --- a/Modules/Bar/Widgets/ActiveWindow.qml +++ b/Modules/Bar/Widgets/ActiveWindow.qml @@ -7,7 +7,6 @@ import qs.Commons import qs.Services import qs.Widgets - Row { id: root anchors.verticalCenter: parent.verticalCenter @@ -43,8 +42,7 @@ Row { function getTitle() { // Use the service's focusedWindowTitle property which is updated immediately // when WindowOpenedOrChanged events are received - return CompositorService.focusedWindowTitle !== "(No active window)" ? - CompositorService.focusedWindowTitle : "" + return CompositorService.focusedWindowTitle !== "(No active window)" ? CompositorService.focusedWindowTitle : "" } function getAppIcon() { diff --git a/Modules/Bar/Widgets/KeyboardLayout.qml b/Modules/Bar/Widgets/KeyboardLayout.qml index fd1e4fb..1ae3acb 100644 --- a/Modules/Bar/Widgets/KeyboardLayout.qml +++ b/Modules/Bar/Widgets/KeyboardLayout.qml @@ -25,10 +25,9 @@ Item { tooltipText: "Keyboard Layout: " + currentLayout onClicked: { + // You could open keyboard settings here if needed // For now, just show the current layout } } - - } diff --git a/Modules/IPC/IPCManager.qml b/Modules/IPC/IPCManager.qml index bcd1e3e..ad63934 100644 --- a/Modules/IPC/IPCManager.qml +++ b/Modules/IPC/IPCManager.qml @@ -5,31 +5,30 @@ import qs.Services Item { id: root - + IpcHandler { target: "settings" function toggle() { settingsPanel.toggle(Quickshell.screens[0]) } } - + IpcHandler { target: "notifications" function toggleHistory() { notificationHistoryPanel.toggle(Quickshell.screens[0]) } - function toggleDoNotDisturb() { - // TODO + function toggleDoNotDisturb() {// TODO } } - + IpcHandler { target: "idleInhibitor" function toggle() { return IdleInhibitorService.manualToggle() } } - + IpcHandler { target: "appLauncher" function toggle() { @@ -39,18 +38,18 @@ Item { launcherPanel.toggle(Quickshell.screens[0]) // Use the setSearchText function to set clipboard mode Qt.callLater(() => { - launcherPanel.setSearchText(">clip ") - }) + launcherPanel.setSearchText(">clip ") + }) } function calculator() { launcherPanel.toggle(Quickshell.screens[0]) // Use the setSearchText function to set calculator mode Qt.callLater(() => { - launcherPanel.setSearchText(">calc ") - }) + launcherPanel.setSearchText(">calc ") + }) } } - + IpcHandler { target: "launcher" function toggle() { @@ -60,18 +59,18 @@ Item { launcherPanel.toggle(Quickshell.screens[0]) // Use the setSearchText function to set clipboard mode Qt.callLater(() => { - launcherPanel.setSearchText(">clip ") - }) + launcherPanel.setSearchText(">clip ") + }) } function calculator() { launcherPanel.toggle(Quickshell.screens[0]) // Use the setSearchText function to set calculator mode Qt.callLater(() => { - launcherPanel.setSearchText(">calc ") - }) + launcherPanel.setSearchText(">calc ") + }) } } - + IpcHandler { target: "lockScreen" function toggle() { @@ -82,7 +81,7 @@ Item { } } } - + IpcHandler { target: "brightness" function increase() { @@ -92,11 +91,11 @@ Item { BrightnessService.decreaseBrightness() } } - + IpcHandler { target: "powerPanel" function toggle() { powerPanel.toggle(Quickshell.screens[0]) } } -} \ No newline at end of file +} diff --git a/Modules/Launcher/Calculator.qml b/Modules/Launcher/Calculator.qml index 8dae5bd..1082b89 100644 --- a/Modules/Launcher/Calculator.qml +++ b/Modules/Launcher/Calculator.qml @@ -33,7 +33,7 @@ QtObject { } } else { // Fallback to basic evaluation - console.log("AdvancedMath not available, using basic eval") + Logger.warn("Calculator", "AdvancedMath not available, using basic eval") // Basic preprocessing for common functions var processed = expression.trim( diff --git a/Modules/Launcher/Launcher.qml b/Modules/Launcher/Launcher.qml index f90963c..9bedecf 100644 --- a/Modules/Launcher/Launcher.qml +++ b/Modules/Launcher/Launcher.qml @@ -26,7 +26,7 @@ NPanel { // Properties property string searchText: "" - + // Add function to set search text programmatically function setSearchText(text) { searchText = text @@ -521,4 +521,4 @@ NPanel { } } } -} \ No newline at end of file +} diff --git a/Modules/LockScreen/LockScreen.qml b/Modules/LockScreen/LockScreen.qml index f5ded6e..db7686f 100644 --- a/Modules/LockScreen/LockScreen.qml +++ b/Modules/LockScreen/LockScreen.qml @@ -167,7 +167,8 @@ Loader { Item { id: keyboardLayout - property string currentLayout: (typeof KeyboardLayoutService !== 'undefined' && KeyboardLayoutService.currentLayout) ? KeyboardLayoutService.currentLayout : "Unknown" + property string currentLayout: (typeof KeyboardLayoutService !== 'undefined' + && KeyboardLayoutService.currentLayout) ? KeyboardLayoutService.currentLayout : "Unknown" } // Wallpaper image diff --git a/Modules/SettingsPanel/Tabs/BarTab.qml b/Modules/SettingsPanel/Tabs/BarTab.qml index 906d958..40d59da 100644 --- a/Modules/SettingsPanel/Tabs/BarTab.qml +++ b/Modules/SettingsPanel/Tabs/BarTab.qml @@ -202,13 +202,13 @@ ColumnLayout { // Helper functions function addWidgetToSection(widgetName, section) { - console.log("Adding widget", widgetName, "to section", section) + //Logger.log("BarTab", "Adding widget", widgetName, "to section", section) var sectionArray = Settings.data.bar.widgets[section] if (sectionArray) { // Create a new array to avoid modifying the original var newArray = sectionArray.slice() newArray.push(widgetName) - console.log("Widget added. New array:", JSON.stringify(newArray)) + //Logger.log("BarTab", "Widget added. New array:", JSON.stringify(newArray)) // Assign the new array Settings.data.bar.widgets[section] = newArray @@ -216,34 +216,35 @@ ColumnLayout { } function removeWidgetFromSection(section, index) { - console.log("Removing widget from section", section, "at index", index) + // Logger.log("BarTab", "Removing widget from section", section, "at index", index) var sectionArray = Settings.data.bar.widgets[section] - console.log("Current section array:", JSON.stringify(sectionArray)) - + //Logger.log("BarTab", "Current section array:", JSON.stringify(sectionArray)) + if (sectionArray && index >= 0 && index < sectionArray.length) { // Create a new array to avoid modifying the original var newArray = sectionArray.slice() newArray.splice(index, 1) - console.log("Widget removed. New array:", JSON.stringify(newArray)) + //Logger.log("BarTab", "Widget removed. New array:", JSON.stringify(newArray)) // Assign the new array Settings.data.bar.widgets[section] = newArray - + // Force a settings save - console.log("Settings updated, triggering save...") - + //Logger.log("BarTab", "Settings updated, triggering save...") + // Verify the change was applied - Qt.setTimeout(function() { + Qt.setTimeout(function () { var updatedArray = Settings.data.bar.widgets[section] - console.log("Verification - updated section array:", JSON.stringify(updatedArray)) + //Logger.log("BarTab", "Verification - updated section array:", JSON.stringify(updatedArray)) }, 100) } else { - console.log("Invalid section or index:", section, index, "array length:", sectionArray ? sectionArray.length : "null") + //Logger.log("BarTab", "Invalid section or index:", section, index, "array length:", + // sectionArray ? sectionArray.length : "null") } } function reorderWidgetInSection(section, fromIndex, toIndex) { - console.log("Reordering widget in section", section, "from", fromIndex, "to", toIndex) + //Logger.log("BarTab", "Reordering widget in section", section, "from", fromIndex, "to", toIndex) var sectionArray = Settings.data.bar.widgets[section] if (sectionArray && fromIndex >= 0 && fromIndex < sectionArray.length && toIndex >= 0 && toIndex < sectionArray.length) { @@ -253,7 +254,7 @@ ColumnLayout { var item = newArray[fromIndex] newArray.splice(fromIndex, 1) newArray.splice(toIndex, 0, item) - console.log("Widget reordered. New array:", JSON.stringify(newArray)) + Logger.log("BarTab", "Widget reordered. New array:", JSON.stringify(newArray)) // Assign the new array Settings.data.bar.widgets[section] = newArray diff --git a/Services/CompositorService.qml b/Services/CompositorService.qml index aaddde4..a1ec62b 100644 --- a/Services/CompositorService.qml +++ b/Services/CompositorService.qml @@ -298,8 +298,6 @@ Singleton { "isFocused": windowData.is_focused === true } - - if (existingIndex >= 0) { // Update existing window windows[existingIndex] = newWindow @@ -314,13 +312,13 @@ Singleton { const oldFocusedIndex = focusedWindowIndex focusedWindowIndex = windows.findIndex(w => w.id === windowData.id) updateFocusedWindowTitle() - + // Only emit activeWindowChanged if the focused window actually changed if (oldFocusedIndex !== focusedWindowIndex) { activeWindowChanged() } } else if (existingIndex >= 0 && existingIndex === focusedWindowIndex) { - // If this is the currently focused window (but not newly focused), + // If this is the currently focused window (but not newly focused), // still update the title in case it changed, but don't emit activeWindowChanged updateFocusedWindowTitle() } @@ -467,7 +465,7 @@ Singleton { } else { focusedWindowTitle = "(No active window)" } - + // Emit signal if title actually changed if (oldTitle !== focusedWindowTitle) { windowTitleChanged() diff --git a/Widgets/NWidgetCard.qml b/Widgets/NWidgetCard.qml index 22f4f5a..abf53c7 100644 --- a/Widgets/NWidgetCard.qml +++ b/Widgets/NWidgetCard.qml @@ -114,7 +114,7 @@ NBox { id: widgetItem required property int index required property string modelData - + width: widgetContent.implicitWidth + 16 * scaling height: 40 * scaling radius: Style.radiusL * scaling @@ -127,7 +127,7 @@ NBox { Drag.active: mouseArea.drag.active Drag.hotSpot.x: width / 2 Drag.hotSpot.y: height / 2 - + // Store the widget index for drag operations property int widgetIndex: index @@ -173,40 +173,40 @@ NBox { id: mouseArea anchors.fill: parent drag.target: parent - + onPressed: { // Check if the click is on the close button area const closeButtonX = widgetContent.x + widgetContent.width - 20 * scaling const closeButtonY = widgetContent.y const closeButtonWidth = 20 * scaling const closeButtonHeight = 20 * scaling - - if (mouseX >= closeButtonX && mouseX <= closeButtonX + closeButtonWidth && - mouseY >= closeButtonY && mouseY <= closeButtonY + closeButtonHeight) { + + if (mouseX >= closeButtonX && mouseX <= closeButtonX + closeButtonWidth && mouseY >= closeButtonY + && mouseY <= closeButtonY + closeButtonHeight) { // Click is on the close button, don't start drag mouse.accepted = false return } - + Logger.log("NWidgetCard", `Started dragging widget: ${modelData} at index ${index}`) // Bring to front when starting drag widgetItem.z = 1000 } - + onReleased: { Logger.log("NWidgetCard", `Released widget: ${modelData} at index ${index}`) // Reset z-index when drag ends widgetItem.z = 0 - + // Get the global mouse position const globalDropX = mouseArea.mouseX + widgetItem.x + widgetFlow.x const globalDropY = mouseArea.mouseY + widgetItem.y + widgetFlow.y - + // Find which widget the drop position is closest to let targetIndex = -1 let minDistance = Infinity - - for (let i = 0; i < widgetModel.length; i++) { + + for (var i = 0; i < widgetModel.length; i++) { if (i !== index) { // Get the position of other widgets const otherWidget = widgetFlow.children[i] @@ -214,13 +214,11 @@ NBox { // Calculate the center of the other widget const otherCenterX = otherWidget.x + otherWidget.width / 2 + widgetFlow.x const otherCenterY = otherWidget.y + otherWidget.height / 2 + widgetFlow.y - + // Calculate distance to the center of this widget - const distance = Math.sqrt( - Math.pow(globalDropX - otherCenterX, 2) + - Math.pow(globalDropY - otherCenterY, 2) - ) - + const distance = Math.sqrt(Math.pow(globalDropX - otherCenterX, + 2) + Math.pow(globalDropY - otherCenterY, 2)) + if (distance < minDistance) { minDistance = distance targetIndex = otherWidget.widgetIndex @@ -228,12 +226,15 @@ NBox { } } } - + // Only reorder if we found a valid target and it's different from current position if (targetIndex !== -1 && targetIndex !== index) { const fromIndex = index const toIndex = targetIndex - Logger.log("NWidgetCard", `Dropped widget from index ${fromIndex} to position ${toIndex} (distance: ${minDistance.toFixed(2)})`) + Logger.log( + "NWidgetCard", + `Dropped widget from index ${fromIndex} to position ${toIndex} (distance: ${minDistance.toFixed( + 2)})`) reorderWidget(sectionName.toLowerCase(), fromIndex, toIndex) } else { Logger.log("NWidgetCard", `No valid drop target found for widget at index ${index}`) @@ -244,29 +245,29 @@ NBox { } } - // Drop zone at the beginning (positioned absolutely) - DropArea { - id: startDropZone - width: 40 * scaling - height: 40 * scaling - x: widgetFlow.x - y: widgetFlow.y + (widgetFlow.height - height) / 2 - keys: ["widget"] - z: 1001 // Above the Flow - - Rectangle { - anchors.fill: parent - color: startDropZone.containsDrag ? Color.applyOpacity(Color.mPrimary, "20") : Color.transparent - border.color: startDropZone.containsDrag ? Color.mPrimary : Color.transparent - border.width: startDropZone.containsDrag ? 2 : 0 - radius: Style.radiusS * scaling - } - - onEntered: function(drag) { + // Drop zone at the beginning (positioned absolutely) + DropArea { + id: startDropZone + width: 40 * scaling + height: 40 * scaling + x: widgetFlow.x + y: widgetFlow.y + (widgetFlow.height - height) / 2 + keys: ["widget"] + z: 1001 // Above the Flow + + Rectangle { + anchors.fill: parent + color: startDropZone.containsDrag ? Color.applyOpacity(Color.mPrimary, "20") : Color.transparent + border.color: startDropZone.containsDrag ? Color.mPrimary : Color.transparent + border.width: startDropZone.containsDrag ? 2 : 0 + radius: Style.radiusS * scaling + } + + onEntered: function (drag) { Logger.log("NWidgetCard", "Entered start drop zone") } - - onDropped: function(drop) { + + onDropped: function (drop) { Logger.log("NWidgetCard", "Dropped on start zone") if (drop.source && drop.source.widgetIndex !== undefined) { const fromIndex = drop.source.widgetIndex @@ -279,29 +280,29 @@ NBox { } } - // Drop zone at the end (positioned absolutely) - DropArea { - id: endDropZone - width: 40 * scaling - height: 40 * scaling - x: widgetFlow.x + widgetFlow.width - width - y: widgetFlow.y + (widgetFlow.height - height) / 2 - keys: ["widget"] - z: 1001 // Above the Flow - - Rectangle { - anchors.fill: parent - color: endDropZone.containsDrag ? Color.applyOpacity(Color.mPrimary, "20") : Color.transparent - border.color: endDropZone.containsDrag ? Color.mPrimary : Color.transparent - border.width: endDropZone.containsDrag ? 2 : 0 - radius: Style.radiusS * scaling - } - - onEntered: function(drag) { + // Drop zone at the end (positioned absolutely) + DropArea { + id: endDropZone + width: 40 * scaling + height: 40 * scaling + x: widgetFlow.x + widgetFlow.width - width + y: widgetFlow.y + (widgetFlow.height - height) / 2 + keys: ["widget"] + z: 1001 // Above the Flow + + Rectangle { + anchors.fill: parent + color: endDropZone.containsDrag ? Color.applyOpacity(Color.mPrimary, "20") : Color.transparent + border.color: endDropZone.containsDrag ? Color.mPrimary : Color.transparent + border.width: endDropZone.containsDrag ? 2 : 0 + radius: Style.radiusS * scaling + } + + onEntered: function (drag) { Logger.log("NWidgetCard", "Entered end drop zone") } - - onDropped: function(drop) { + + onDropped: function (drop) { Logger.log("NWidgetCard", "Dropped on end zone") if (drop.source && drop.source.widgetIndex !== undefined) { const fromIndex = drop.source.widgetIndex