From 3f6bc3414d577e7206528cbe5f9299ff37820918 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Wed, 6 Aug 2025 16:02:33 +0200 Subject: [PATCH] Possible ram improvement, move settings into sidebar --- Bar/Bar.qml | 9 +- Bar/Modules/Bluetooth.qml | 330 ++++---- Bar/Modules/SettingsButton.qml | 80 -- Bar/Modules/Wifi.qml | 488 ++++++------ Components/Corners.qml | 45 +- Helpers/IPCHandlers.qml | 1 - Widgets/Notification/NotificationIcon.qml | 46 +- Widgets/SettingsWindow/SettingsWindow.qml | 71 +- .../Sidebar/Config/CollapsibleCategory.qml | 56 -- Widgets/Sidebar/Config/ProfileSettings.qml | 643 ---------------- Widgets/Sidebar/Config/SettingsModal.qml | 190 +---- Widgets/Sidebar/Config/WallpaperSettings.qml | 722 ------------------ Widgets/Sidebar/Config/WeatherSettings.qml | 275 ------- Widgets/Sidebar/Panel/PanelPopup.qml | 120 ++- Widgets/Sidebar/Panel/SettingsIcon.qml | 80 ++ Widgets/Sidebar/Panel/Weather.qml | 43 +- shell.qml | 50 +- 17 files changed, 795 insertions(+), 2454 deletions(-) delete mode 100644 Bar/Modules/SettingsButton.qml delete mode 100644 Widgets/Sidebar/Config/CollapsibleCategory.qml delete mode 100644 Widgets/Sidebar/Config/ProfileSettings.qml delete mode 100644 Widgets/Sidebar/Config/WallpaperSettings.qml delete mode 100644 Widgets/Sidebar/Config/WeatherSettings.qml create mode 100644 Widgets/Sidebar/Panel/SettingsIcon.qml diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 8d355ba..730c61b 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -102,6 +102,7 @@ Scope { } NotificationIcon { + shell: rootScope.shell anchors.verticalCenter: parent.verticalCenter } @@ -135,12 +136,9 @@ Scope { anchors.verticalCenter: parent.verticalCenter } - SettingsButton { - anchors.verticalCenter: parent.verticalCenter - } - PanelPopup { id: sidebarPopup + shell: rootScope.shell } Button { @@ -151,8 +149,7 @@ Scope { } } - Background {} - Overview {} + } PanelWindow { diff --git a/Bar/Modules/Bluetooth.qml b/Bar/Modules/Bluetooth.qml index 80beb61..e8ae996 100644 --- a/Bar/Modules/Bluetooth.qml +++ b/Bar/Modules/Bluetooth.qml @@ -55,14 +55,19 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - bluetoothMenu.visible = !bluetoothMenu.visible; - // Enable adapter and start discovery when menu opens - if (bluetoothMenu.visible && Bluetooth.defaultAdapter) { - if (!Bluetooth.defaultAdapter.enabled) { - Bluetooth.defaultAdapter.enabled = true; - } - if (!Bluetooth.defaultAdapter.discovering) { - Bluetooth.defaultAdapter.discovering = true; + if (!bluetoothMenuLoader.active) { + bluetoothMenuLoader.loading = true; + } + if (bluetoothMenuLoader.item) { + bluetoothMenuLoader.item.visible = !bluetoothMenuLoader.item.visible; + // Enable adapter and start discovery when menu opens + if (bluetoothMenuLoader.item.visible && Bluetooth.defaultAdapter) { + if (!Bluetooth.defaultAdapter.enabled) { + Bluetooth.defaultAdapter.enabled = true; + } + if (!Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = true; + } } } } @@ -80,190 +85,195 @@ Item { delay: 200 } - PanelWindow { - id: bluetoothMenu - implicitWidth: 320 - implicitHeight: 480 - visible: false - color: "transparent" - anchors.top: true - anchors.right: true - margins.right: 0 - margins.top: 0 - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // LazyLoader for Bluetooth menu + LazyLoader { + id: bluetoothMenuLoader + loading: false + component: PanelWindow { + id: bluetoothMenu + implicitWidth: 320 + implicitHeight: 480 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - onVisibleChanged: { - // Stop discovery when menu closes to save battery - if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { - Bluetooth.defaultAdapter.discovering = false; + onVisibleChanged: { + // Stop discovery when menu closes to save battery + if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = false; + } } - } - Rectangle { - anchors.fill: parent - color: Theme.backgroundPrimary - radius: 12 - - ColumnLayout { + Rectangle { anchors.fill: parent - anchors.margins: 16 - spacing: 16 + color: Theme.backgroundPrimary + radius: 12 - RowLayout { - Layout.fillWidth: true - spacing: 12 + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 16 - Text { - text: "bluetooth" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.accentPrimary - } - - Text { - text: "Bluetooth Devices" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary + RowLayout { Layout.fillWidth: true - } + spacing: 12 - IconButton { - icon: "close" - onClicked: { - bluetoothMenu.visible = false; - if (Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { - Bluetooth.defaultAdapter.discovering = false; + Text { + text: "bluetooth" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.accentPrimary + } + + Text { + text: "Bluetooth Devices" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + + IconButton { + icon: "close" + onClicked: { + bluetoothMenu.visible = false; + if (Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = false; + } } } } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.outline - opacity: 0.12 - } + Rectangle { + Layout.fillWidth: true + height: 1 + color: Theme.outline + opacity: 0.12 + } - ListView { - id: deviceList - Layout.fillWidth: true - Layout.fillHeight: true - model: Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.devices : [] - spacing: 8 - clip: true + ListView { + id: deviceList + Layout.fillWidth: true + Layout.fillHeight: true + model: Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.devices : [] + spacing: 8 + clip: true - delegate: Item { - width: parent.width - height: 48 + delegate: Item { + width: parent.width + height: 48 - Rectangle { - anchors.fill: parent - radius: 8 - color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (deviceMouseArea.containsMouse ? Theme.highlight : "transparent") - - RowLayout { + Rectangle { anchors.fill: parent - anchors.margins: 8 - spacing: 8 + radius: 8 + color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (deviceMouseArea.containsMouse ? Theme.highlight : "transparent") - Text { - text: modelData.connected ? "bluetooth" : "bluetooth_disabled" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 Text { - text: { - let deviceName = modelData.name || modelData.deviceName || "Unknown Device"; - // Hide MAC addresses and show "Unknown Device" instead - let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; - if (macPattern.test(deviceName)) { - return "Unknown Device"; - } - return deviceName; - } - color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 - elide: Text.ElideRight - Layout.fillWidth: true - } - - Text { - text: { - let deviceName = modelData.name || modelData.deviceName || ""; - let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; - if (macPattern.test(deviceName)) { - // Show MAC address in subtitle for unnamed devices - return modelData.address + " • " + (modelData.paired ? "Paired" : "Available"); - } else { - // Show only status for named devices - return modelData.paired ? "Paired" : "Available"; - } - } + text: modelData.connected ? "bluetooth" : "bluetooth_disabled" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 - elide: Text.ElideRight + } + + ColumnLayout { Layout.fillWidth: true + spacing: 2 + + Text { + text: { + let deviceName = modelData.name || modelData.deviceName || "Unknown Device"; + // Hide MAC addresses and show "Unknown Device" instead + let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; + if (macPattern.test(deviceName)) { + return "Unknown Device"; + } + return deviceName; + } + color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) + font.pixelSize: 14 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: { + let deviceName = modelData.name || modelData.deviceName || ""; + let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; + if (macPattern.test(deviceName)) { + // Show MAC address in subtitle for unnamed devices + return modelData.address + " • " + (modelData.paired ? "Paired" : "Available"); + } else { + // Show only status for named devices + return modelData.paired ? "Paired" : "Available"; + } + } + color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + Item { + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + visible: modelData.pairing || modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting + + Spinner { + visible: parent.visible + running: parent.visible + color: Theme.accentPrimary + anchors.centerIn: parent + size: 22 + } } } - Item { - Layout.preferredWidth: 22 - Layout.preferredHeight: 22 - visible: modelData.pairing || modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting - - Spinner { - visible: parent.visible - running: parent.visible - color: Theme.accentPrimary - anchors.centerIn: parent - size: 22 - } - } - } - - MouseArea { - id: deviceMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - // Handle device actions: disconnect, pair, or connect - if (modelData.connected) { - modelData.disconnect(); - } else if (!modelData.paired) { - modelData.pair(); - } else { - modelData.connect(); + MouseArea { + id: deviceMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + // Handle device actions: disconnect, pair, or connect + if (modelData.connected) { + modelData.disconnect(); + } else if (!modelData.paired) { + modelData.pair(); + } else { + modelData.connect(); + } } } } } } - } - // Discovering indicator - RowLayout { - Layout.fillWidth: true - spacing: 8 - visible: Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering + // Discovering indicator + RowLayout { + Layout.fillWidth: true + spacing: 8 + visible: Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering - Text { - text: "Scanning for devices..." - font.pixelSize: 12 - color: Theme.textSecondary - } + Text { + text: "Scanning for devices..." + font.pixelSize: 12 + color: Theme.textSecondary + } - Spinner { - running: true - color: Theme.accentPrimary - size: 16 + Spinner { + running: true + color: Theme.accentPrimary + size: 16 + } } } } diff --git a/Bar/Modules/SettingsButton.qml b/Bar/Modules/SettingsButton.qml deleted file mode 100644 index 7a9bede..0000000 --- a/Bar/Modules/SettingsButton.qml +++ /dev/null @@ -1,80 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import qs.Settings -import qs.Components -import qs.Widgets.SettingsWindow - -Item { - id: root - width: 22 - height: 22 - - property var settingsWindow: null - - Rectangle { - id: button - anchors.fill: parent - color: "transparent" - radius: width / 2 - - Text { - anchors.centerIn: parent - text: "settings" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 - color: mouseArea.containsMouse ? Theme.accentPrimary : Theme.textPrimary - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - - onClicked: { - if (!settingsWindow) { - // Create new window - settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues - if (settingsWindow) { - settingsWindow.visible = true; - // Handle window closure - settingsWindow.visibleChanged.connect(function() { - if (settingsWindow && !settingsWindow.visible) { - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.destroy(); - } - }); - } - } else if (settingsWindow.visible) { - // Close and destroy window - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.visible = false; - windowToDestroy.destroy(); - } - } - } - - StyledTooltip { - text: "Settings" - targetItem: mouseArea - tooltipVisible: mouseArea.containsMouse - } - } - - Component { - id: settingsComponent - SettingsWindow {} - } - - // Clean up on destruction - Component.onDestruction: { - if (settingsWindow) { - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.destroy(); - } - } -} \ No newline at end of file diff --git a/Bar/Modules/Wifi.qml b/Bar/Modules/Wifi.qml index 5c75ba1..13918f5 100644 --- a/Bar/Modules/Wifi.qml +++ b/Bar/Modules/Wifi.qml @@ -61,11 +61,16 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - wifiMenu.visible = !wifiMenu.visible; - if (wifiMenu.visible) { - network.onMenuOpened(); - } else { - network.onMenuClosed(); + if (!wifiMenuLoader.active) { + wifiMenuLoader.loading = true; + } + if (wifiMenuLoader.item) { + wifiMenuLoader.item.visible = !wifiMenuLoader.item.visible; + if (wifiMenuLoader.item.visible) { + network.onMenuOpened(); + } else { + network.onMenuClosed(); + } } } onEntered: wifiTooltip.tooltipVisible = true @@ -82,282 +87,287 @@ Item { delay: 200 } - PanelWindow { - id: wifiMenu - implicitWidth: 320 - implicitHeight: 480 - visible: false - color: "transparent" - anchors.top: true - anchors.right: true - margins.right: 0 - margins.top: 0 - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // LazyLoader for WiFi menu + LazyLoader { + id: wifiMenuLoader + loading: false + component: PanelWindow { + id: wifiMenu + implicitWidth: 320 + implicitHeight: 480 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - Rectangle { - anchors.fill: parent - color: Theme.backgroundPrimary - radius: 12 - - ColumnLayout { + Rectangle { anchors.fill: parent - anchors.margins: 16 - spacing: 16 + color: Theme.backgroundPrimary + radius: 12 - RowLayout { - Layout.fillWidth: true - spacing: 12 + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 16 - Text { - text: "wifi" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.accentPrimary - } - - Text { - text: "WiFi Networks" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary + RowLayout { Layout.fillWidth: true - } + spacing: 12 - IconButton { - icon: "refresh" - onClicked: network.refreshNetworks() - } + Text { + text: "wifi" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.accentPrimary + } - IconButton { - icon: "close" - onClicked: { - wifiMenu.visible = false; - network.onMenuClosed(); + Text { + text: "WiFi Networks" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + + IconButton { + icon: "refresh" + onClicked: network.refreshNetworks() + } + + IconButton { + icon: "close" + onClicked: { + wifiMenu.visible = false; + network.onMenuClosed(); + } } } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.outline - opacity: 0.12 - } + Rectangle { + Layout.fillWidth: true + height: 1 + color: Theme.outline + opacity: 0.12 + } - ListView { - id: networkList - Layout.fillWidth: true - Layout.fillHeight: true - model: Object.values(network.networks) - spacing: 8 - clip: true + ListView { + id: networkList + Layout.fillWidth: true + Layout.fillHeight: true + model: Object.values(network.networks) + spacing: 8 + clip: true - delegate: Item { - width: parent.width - height: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 108 : 48 // 48 for network + 60 for password prompt + delegate: Item { + width: parent.width + height: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 108 : 48 // 48 for network + 60 for password prompt - ColumnLayout { - anchors.fill: parent - spacing: 0 + ColumnLayout { + anchors.fill: parent + spacing: 0 - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 48 - radius: 8 - color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (networkMouseArea.containsMouse ? Theme.highlight : "transparent") + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 48 + radius: 8 + color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (networkMouseArea.containsMouse ? Theme.highlight : "transparent") - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 8 - - Text { - text: network.signalIcon(modelData.signal) - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 Text { - text: modelData.ssid || "Unknown Network" - color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 - elide: Text.ElideRight - Layout.fillWidth: true - } - - Text { - text: modelData.security && modelData.security !== "--" ? modelData.security : "Open" + text: network.signalIcon(modelData.signal) + font.family: "Material Symbols Outlined" + font.pixelSize: 18 color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 - elide: Text.ElideRight + } + + ColumnLayout { Layout.fillWidth: true + spacing: 2 + + Text { + text: modelData.ssid || "Unknown Network" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) + font.pixelSize: 14 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: modelData.security && modelData.security !== "--" ? modelData.security : "Open" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + visible: network.connectStatusSsid === modelData.ssid && network.connectStatus === "error" && network.connectError.length > 0 + text: network.connectError + color: Theme.error + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + Item { + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + visible: network.connectStatusSsid === modelData.ssid && (network.connectStatus !== "" || network.connectingSsid === modelData.ssid) + + Spinner { + visible: network.connectingSsid === modelData.ssid + running: network.connectingSsid === modelData.ssid + color: Theme.accentPrimary + anchors.centerIn: parent + size: 22 + } + + Text { + visible: network.connectStatus === "success" && !network.connectingSsid + text: "check_circle" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: "#43a047" + anchors.centerIn: parent + } + + Text { + visible: network.connectStatus === "error" && !network.connectingSsid + text: "error" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: Theme.error + anchors.centerIn: parent + } } Text { - visible: network.connectStatusSsid === modelData.ssid && network.connectStatus === "error" && network.connectError.length > 0 - text: network.connectError - color: Theme.error + visible: modelData.connected + text: "connected" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary font.pixelSize: 11 - elide: Text.ElideRight - Layout.fillWidth: true } } - Item { - Layout.preferredWidth: 22 - Layout.preferredHeight: 22 - visible: network.connectStatusSsid === modelData.ssid && (network.connectStatus !== "" || network.connectingSsid === modelData.ssid) - - Spinner { - visible: network.connectingSsid === modelData.ssid - running: network.connectingSsid === modelData.ssid - color: Theme.accentPrimary - anchors.centerIn: parent - size: 22 + MouseArea { + id: networkMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (modelData.connected) { + network.disconnectNetwork(modelData.ssid); + } else if (network.isSecured(modelData.security) && !modelData.existing) { + passwordPromptSsid = modelData.ssid; + showPasswordPrompt = true; + passwordInput = ""; // Clear previous input + Qt.callLater(function() { + passwordInputField.forceActiveFocus(); + }); + } else { + network.connectNetwork(modelData.ssid, modelData.security); + } } - - Text { - visible: network.connectStatus === "success" && !network.connectingSsid - text: "check_circle" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: "#43a047" - anchors.centerIn: parent - } - - Text { - visible: network.connectStatus === "error" && !network.connectingSsid - text: "error" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: Theme.error - anchors.centerIn: parent - } - } - - Text { - visible: modelData.connected - text: "connected" - color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary - font.pixelSize: 11 } } - MouseArea { - id: networkMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - if (modelData.connected) { - network.disconnectNetwork(modelData.ssid); - } else if (network.isSecured(modelData.security) && !modelData.existing) { - passwordPromptSsid = modelData.ssid; - showPasswordPrompt = true; - passwordInput = ""; // Clear previous input - Qt.callLater(function() { - passwordInputField.forceActiveFocus(); - }); - } else { - network.connectNetwork(modelData.ssid, modelData.security); + // Password prompt section + Rectangle { + id: passwordPromptSection + Layout.fillWidth: true + Layout.preferredHeight: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 60 : 0 + Layout.margins: 8 + visible: modelData.ssid === passwordPromptSsid && showPasswordPrompt + color: Theme.surfaceVariant + radius: 8 + + RowLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 10 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 36 + + Rectangle { + anchors.fill: parent + radius: 8 + color: "transparent" + border.color: passwordInputField.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: passwordInputField + anchors.fill: parent + anchors.margins: 12 + text: passwordInput + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + focus: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhNone + echoMode: TextInput.Password + onTextChanged: passwordInput = text + onAccepted: { + network.submitPassword(passwordPromptSsid, passwordInput); + showPasswordPrompt = false; + } + + MouseArea { + id: passwordInputMouseArea + anchors.fill: parent + onClicked: passwordInputField.forceActiveFocus() + } + } + } } - } - } - } - - // Password prompt section - Rectangle { - id: passwordPromptSection - Layout.fillWidth: true - Layout.preferredHeight: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 60 : 0 - Layout.margins: 8 - visible: modelData.ssid === passwordPromptSsid && showPasswordPrompt - color: Theme.surfaceVariant - radius: 8 - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 10 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 36 Rectangle { - anchors.fill: parent - radius: 8 - color: "transparent" - border.color: passwordInputField.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 + Layout.preferredWidth: 80 + Layout.preferredHeight: 36 + radius: 18 + color: Theme.accentPrimary + border.color: Theme.accentPrimary + border.width: 0 + opacity: 1.0 - TextInput { - id: passwordInputField + Behavior on color { + ColorAnimation { + duration: 100 + } + } + + MouseArea { anchors.fill: parent - anchors.margins: 12 - text: passwordInput - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - focus: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhNone - echoMode: TextInput.Password - onTextChanged: passwordInput = text - onAccepted: { + onClicked: { network.submitPassword(passwordPromptSsid, passwordInput); showPasswordPrompt = false; } - - MouseArea { - id: passwordInputMouseArea - anchors.fill: parent - onClicked: passwordInputField.forceActiveFocus() - } + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: parent.color = Qt.darker(Theme.accentPrimary, 1.1) + onExited: parent.color = Theme.accentPrimary } - } - } - Rectangle { - Layout.preferredWidth: 80 - Layout.preferredHeight: 36 - radius: 18 - color: Theme.accentPrimary - border.color: Theme.accentPrimary - border.width: 0 - opacity: 1.0 - - Behavior on color { - ColorAnimation { - duration: 100 + Text { + anchors.centerIn: parent + text: "Connect" + color: Theme.backgroundPrimary + font.pixelSize: 14 + font.bold: true } } - - MouseArea { - anchors.fill: parent - onClicked: { - network.submitPassword(passwordPromptSsid, passwordInput); - showPasswordPrompt = false; - } - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: parent.color = Qt.darker(Theme.accentPrimary, 1.1) - onExited: parent.color = Theme.accentPrimary - } - - Text { - anchors.centerIn: parent - text: "Connect" - color: Theme.backgroundPrimary - font.pixelSize: 14 - font.bold: true - } } } } diff --git a/Components/Corners.qml b/Components/Corners.qml index 0ec37ef..f41d14c 100644 --- a/Components/Corners.qml +++ b/Components/Corners.qml @@ -16,33 +16,33 @@ Shape { property var modelData: null - // Position flags derived from position string - property bool _isTop: position.includes("top") - property bool _isLeft: position.includes("left") - property bool _isRight: position.includes("right") - property bool _isBottom: position.includes("bottom") + // Position flags derived from position string - calculated once + readonly property bool _isTop: position.includes("top") + readonly property bool _isLeft: position.includes("left") + readonly property bool _isRight: position.includes("right") + readonly property bool _isBottom: position.includes("bottom") // Shift the path vertically if offsetY is negative to pull shape up - property real pathOffsetY: Math.min(offsetY, 0) + readonly property real pathOffsetY: Math.min(offsetY, 0) // Base coordinates for left corner shape, shifted by pathOffsetY vertically - property real _baseStartX: 30 * size - property real _baseStartY: (_isTop ? 20 * size : 0) + pathOffsetY - property real _baseLineX: 30 * size - property real _baseLineY: (_isTop ? 0 : 20 * size) + pathOffsetY - property real _baseArcX: 50 * size - property real _baseArcY: (_isTop ? 20 * size : 0) + pathOffsetY + readonly property real _baseStartX: 30 * size + readonly property real _baseStartY: (_isTop ? 20 * size : 0) + pathOffsetY + readonly property real _baseLineX: 30 * size + readonly property real _baseLineY: (_isTop ? 0 : 20 * size) + pathOffsetY + readonly property real _baseArcX: 50 * size + readonly property real _baseArcY: (_isTop ? 20 * size : 0) + pathOffsetY // Mirror coordinates for right corners - property real _startX: _isRight ? (concaveWidth - _baseStartX) : _baseStartX - property real _startY: _baseStartY - property real _lineX: _isRight ? (concaveWidth - _baseLineX) : _baseLineX - property real _lineY: _baseLineY - property real _arcX: _isRight ? (concaveWidth - _baseArcX) : _baseArcX - property real _arcY: _baseArcY + readonly property real _startX: _isRight ? (concaveWidth - _baseStartX) : _baseStartX + readonly property real _startY: _baseStartY + readonly property real _lineX: _isRight ? (concaveWidth - _baseLineX) : _baseLineX + readonly property real _lineY: _baseLineY + readonly property real _arcX: _isRight ? (concaveWidth - _baseArcX) : _baseArcX + readonly property real _arcY: _baseArcY // Arc direction varies by corner to maintain proper concave shape - property int _arcDirection: { + readonly property int _arcDirection: { if (_isTop && _isLeft) return PathArc.Counterclockwise if (_isTop && _isRight) return PathArc.Clockwise if (_isBottom && _isLeft) return PathArc.Clockwise @@ -57,9 +57,10 @@ Shape { x: _isLeft ? offsetX : (parent ? parent.width - width + offsetX : 0) y: _isTop ? offsetY : (parent ? parent.height - height + offsetY : 0) - preferredRendererType: Shape.CurveRenderer - layer.enabled: true - layer.samples: 4 + // Optimized rendering settings - reduced quality for better performance + preferredRendererType: Shape.GeometryRenderer // Use simpler renderer + layer.enabled: false // Disable layer rendering to save memory + antialiasing: true // Use standard antialiasing instead of MSAA ShapePath { strokeWidth: 0 diff --git a/Helpers/IPCHandlers.qml b/Helpers/IPCHandlers.qml index aa8ef69..90ed5c7 100644 --- a/Helpers/IPCHandlers.qml +++ b/Helpers/IPCHandlers.qml @@ -14,7 +14,6 @@ IpcHandler { idleInhibitor.toggle() } - function toggleNotificationPopup(): void { console.log("[IPC] NotificationPopup toggle() called") // Use the global toggle function from the notification manager diff --git a/Widgets/Notification/NotificationIcon.qml b/Widgets/Notification/NotificationIcon.qml index cfb7b6f..0a9556c 100644 --- a/Widgets/Notification/NotificationIcon.qml +++ b/Widgets/Notification/NotificationIcon.qml @@ -8,13 +8,25 @@ Item { id: root width: 22; height: 22 property bool isSilence: false - + property var shell: null Process { id: rightClickProcess command: ["qs","ipc", "call", "globalIPC", "toggleNotificationPopup"] } + // Timer to check when NotificationHistory is loaded + Timer { + id: checkHistoryTimer + interval: 50 + repeat: true + onTriggered: { + if (shell && shell.notificationHistoryWin) { + shell.notificationHistoryWin.visible = true; + checkHistoryTimer.stop(); + } + } + } Item { id: bell @@ -22,11 +34,23 @@ Item { Text { id: bellText anchors.centerIn: parent - text: notificationHistoryWin.hasUnread ? "notifications_unread" : "notifications" + text: { + if (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread) { + return "notifications_unread"; + } else { + return "notifications"; + } + } font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" font.pixelSize: 16 - font.weight: notificationHistoryWin.hasUnread ? Font.Bold : Font.Normal - color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (notificationHistoryWin.hasUnread ? Theme.accentPrimary : Theme.textDisabled) + font.weight: { + if (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread) { + return Font.Bold; + } else { + return Font.Normal; + } + } + color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread ? Theme.accentPrimary : Theme.textDisabled) } MouseArea { id: mouseAreaBell @@ -42,10 +66,18 @@ Item { } if (mouse.button === Qt.LeftButton){ - notificationHistoryWin.visible = !notificationHistoryWin.visible - return; + if (shell) { + if (!shell.notificationHistoryWin) { + // Use the shell function to load notification history + shell.loadNotificationHistory(); + checkHistoryTimer.start(); + } else { + // Already loaded, just toggle visibility + shell.notificationHistoryWin.visible = !shell.notificationHistoryWin.visible; + } + } + return; } - } onEntered: notificationTooltip.tooltipVisible = true onExited: notificationTooltip.tooltipVisible = false diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 8f4f93b..016379c 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -10,14 +10,32 @@ import qs.Widgets.SettingsWindow.Tabs.Components PanelWindow { id: panelMain - implicitHeight: screen.height / 2 - implicitWidth: screen.width / 2 + implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400 + implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600 color: "transparent" property int activeTabIndex: 0 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // Add safety checks for component loading + Component.onCompleted: { + // Ensure we start with a valid tab + if (activeTabIndex < 0 || activeTabIndex > 8) { + activeTabIndex = 0; + } + } + + // Cleanup when window is hidden + onVisibleChanged: { + if (!visible) { + // Reset to default tab when hiding to prevent state issues + activeTabIndex = 0; + if (tabName) { + tabName.text = "General"; + } + } + } Component { id: generalSettings @@ -84,7 +102,6 @@ PanelWindow { } } - Rectangle { id: settings color: Theme.backgroundPrimary @@ -98,7 +115,6 @@ PanelWindow { topRightRadius: 20 bottomRightRadius: 20 - Rectangle { id: headerArea anchors { @@ -114,7 +130,6 @@ PanelWindow { anchors.fill: parent spacing: 12 - Text { id: tabName text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : @@ -189,7 +204,6 @@ PanelWindow { } } - Rectangle { anchors { top: headerArea.bottom @@ -213,35 +227,11 @@ PanelWindow { topMargin: 32 } - + // Simplified single loader approach Loader { id: settingsLoader anchors.fill: parent sourceComponent: generalSettings - opacity: 1 - visible: opacity > 0 - - Behavior on opacity { - NumberAnimation { - duration: 150 - easing.type: Easing.InOutQuad - } - } - } - - - Loader { - id: settingsLoader2 - anchors.fill: parent - opacity: 0 - visible: opacity > 0 - - Behavior on opacity { - NumberAnimation { - duration: 150 - easing.type: Easing.InOutQuad - } - } } // Wallpaper Selector Component @@ -252,11 +242,10 @@ PanelWindow { } } - Rectangle { id: tabs color: Theme.surface - width: screen.width / 9 + width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100 height: panelMain.height topLeftRadius: 20 bottomLeftRadius: 20 @@ -356,7 +345,6 @@ PanelWindow { 8: aboutSettings }[index]; - const tabNames = [ "General", "Bar", @@ -370,21 +358,10 @@ PanelWindow { ]; tabName.text = tabNames[index]; - - if (settingsLoader.opacity === 1) { - - settingsLoader2.sourceComponent = newComponent; - settingsLoader.opacity = 0; - settingsLoader2.opacity = 1; - } else { - + // Simple component switching + if (newComponent) { settingsLoader.sourceComponent = newComponent; - settingsLoader2.opacity = 0; - settingsLoader.opacity = 1; } - - - // Tab colors and indicators are now handled by property bindings } } diff --git a/Widgets/Sidebar/Config/CollapsibleCategory.qml b/Widgets/Sidebar/Config/CollapsibleCategory.qml deleted file mode 100644 index d0c2eb7..0000000 --- a/Widgets/Sidebar/Config/CollapsibleCategory.qml +++ /dev/null @@ -1,56 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import qs.Settings - -ColumnLayout { - property alias title: headerText.text - property bool expanded: false // Hidden by default - default property alias content: contentItem.children - - Rectangle { - Layout.fillWidth: true - height: 44 - radius: 12 - color: Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 8 - Item { width: 2 } - Text { - id: headerText - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody - font.bold: true - color: Theme.textPrimary - } - Item { Layout.fillWidth: true } - Rectangle { - width: 32; height: 32 - color: "transparent" - Text { - anchors.centerIn: parent - text: expanded ? "expand_less" : "expand_more" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody - color: Theme.accentPrimary - } - } - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: expanded = !expanded - } - } - Item { height: 8 } - ColumnLayout { - id: contentItem - Layout.fillWidth: true - visible: expanded - spacing: 0 - } -} \ No newline at end of file diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml deleted file mode 100644 index 5cedebe..0000000 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ /dev/null @@ -1,643 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Effects -import QtQuick.Controls -import Quickshell.Widgets -import qs.Components -import qs.Settings - -Rectangle { - id: profileSettingsCard - Layout.fillWidth: true - Layout.preferredHeight: 690 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - // Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - Text { - text: "settings" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - Text { - text: "Profile Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - // Profile Image Input Section - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Profile Image" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - // Profile image - Rectangle { - width: 48 - height: 48 - radius: 24 - - // Border - Rectangle { - anchors.fill: parent - color: "transparent" - radius: 24 - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 - z: 2 - } - - Avatar {} - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: profileImageInput - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.profileImage !== undefined ? Settings.settings.profileImage : "" - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.profileImage = text; - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: profileImageInput.forceActiveFocus() - } - } - } - } - } - - // Show Active Window Icon Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Active Window Icon" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: activeWindowIconSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: activeWindowIconThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; - } - } - } - } - - // Show System Info In Bar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show System Info In Bar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: systemInfoSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: systemInfoThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; - } - } - } - } - - // Show Corners Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Corners" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: cornersSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: cornersThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showCorners = !Settings.settings.showCorners; - } - } - } - } - - // Show Taskbar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Taskbar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: taskbarSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: taskbarThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showTaskbar = !Settings.settings.showTaskbar; - } - } - } - } - - // Show Dock Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Dock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: dockSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: dockThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showDock ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showDock = !Settings.settings.showDock; - } - } - } - } - - // Show Media In Bar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Media In Bar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: mediaSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: mediaThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; - } - } - } - } - - // Dim Windows Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Dim Desktop" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: dimSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: dimThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.dimPanels = !Settings.settings.dimPanels; - } - } - } - } - - // Visualizer Type Selection - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 16 - - Text { - text: "Visualizer Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: visualizerTypeComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["radial", "fire", "diamond"] - currentIndex: model.indexOf(Settings.settings.visualizerType) - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing - text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1) - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: visualizerTypeComboBox.width - width - 12 - y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: visualizerTypeComboBox.height - width: visualizerTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null - currentIndex: visualizerTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: visualizerTypeComboBox.width - contentItem: Text { - text: modelData.charAt(0).toUpperCase() + modelData.slice(1) - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: visualizerTypeComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.visualizerType = model[index]; - } - } - } - - // Video Path Input Section - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 16 - - Text { - text: "Video Path" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: videoPathInput - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.videoPath = text; - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: videoPathInput.forceActiveFocus() - } - } - } - } - } -} diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index 056f6f5..98f88c0 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -5,6 +5,7 @@ import Quickshell import Quickshell.Wayland import qs.Settings import qs.Services +import qs.Widgets.SettingsWindow import qs.Components PanelWindow { @@ -19,164 +20,55 @@ PanelWindow { margins.top: 0 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - Rectangle { - anchors.fill: parent - color: Theme.backgroundPrimary - radius: 20 - z: 0 - - ColumnLayout { - id: content - anchors.fill: parent - anchors.leftMargin: 32 - anchors.rightMargin: 32 - anchors.topMargin: 32 - spacing: 24 - - // Header - ColumnLayout { - id: header - Layout.fillWidth: true - spacing: 4 - RowLayout { - Layout.fillWidth: true - spacing: 20 - Text { - text: "settings" - font.family: "Material Symbols Outlined" - font.pixelSize: 32 - color: Theme.accentPrimary - } - Text { - text: "Settings" - font.pixelSize: 26 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - Rectangle { - width: 36 - height: 36 - radius: 18 - color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - Text { - anchors.centerIn: parent - text: "close" - font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 20 - color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - } - MouseArea { - id: closeButtonArea - anchors.fill: parent - hoverEnabled: true - onClicked: settingsModal.closeSettings() - } - } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.outline - opacity: 0.12 - } - } - - // Tabs bar (reordered) - Tabs { - id: settingsTabs - Layout.fillWidth: true - tabsModel: [ - { icon: "settings", label: "System" }, - { icon: "wallpaper", label: "Wallpaper" }, - { icon: "cloud", label: "Weather" } - ] - } - - // Scrollable settings area - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: content.height - settingsTabs.height - header.height - 128 - color: "transparent" - border.width: 0 - radius: 20 - - Flickable { - id: flick - anchors.fill: parent - contentWidth: width - contentHeight: tabContentLoader.item ? tabContentLoader.item.implicitHeight : 0 - clip: true - - Loader { - id: tabContentLoader - anchors.top: parent.top - width: parent.width - sourceComponent: settingsTabs.currentIndex === 0 ? systemTab : settingsTabs.currentIndex === 1 ? wallpaperTab : weatherTab - } - } - - Component { - id: systemTab - ColumnLayout { - anchors.fill: parent - ProfileSettings { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } - - Component { - id: wallpaperTab - ColumnLayout { - anchors.fill: parent - WallpaperSettings { - id: wallpaperSettings - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } - - Component { - id: weatherTab - ColumnLayout { - anchors.fill: parent - WeatherSettings { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } - } - } - } + // Property to track the settings window instance + property var settingsWindow: null // Function to open the modal and initialize temp values function openSettings() { - visible = true; - focusTimer.start(); + if (!settingsWindow) { + // Create new window + settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues + if (settingsWindow) { + settingsWindow.visible = true; + // Handle window closure + settingsWindow.visibleChanged.connect(function() { + if (settingsWindow && !settingsWindow.visible) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + }); + } + } else if (settingsWindow.visible) { + // Close and destroy window + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } } // Function to close the modal and release focus function closeSettings() { - visible = false; + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } } - Timer { - id: focusTimer - interval: 100 - repeat: false - onTriggered: { - if (visible) { - // Focus logic can go here if needed - } + Component { + id: settingsComponent + SettingsWindow {} + } + + // Clean up on destruction + Component.onDestruction: { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); } } diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml deleted file mode 100644 index 730a935..0000000 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ /dev/null @@ -1,722 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import qs.Settings - -Rectangle { - id: wallpaperSettingsCard - - Layout.fillWidth: true - Layout.preferredHeight: Settings.settings.useSWWW ? 720 : 360 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - - RowLayout { - Layout.fillWidth: true - spacing: 12 - - Text { - text: "image" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - - Text { - text: "Wallpaper Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Wallpaper Path" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: folderInput - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.wallpaperFolder - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.wallpaperFolder = text; - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: folderInput.forceActiveFocus() - } - - } - - } - - } - - - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Random Wallpaper" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: randomWallpaperSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; - } - } - - } - - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use Wallpaper Theme" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: wallpaperThemeSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wallpaperThemeThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; - } - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Wallpaper Interval (seconds)" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.wallpaperInterval - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: intervalSlider - - Layout.fillWidth: true - from: 10 - to: 900 - stepSize: 10 - value: Settings.settings.wallpaperInterval - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); - } - - background: Rectangle { - x: intervalSlider.leftPadding - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: intervalSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: intervalSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: swwwSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: swwwThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - Text { - text: "Resize Mode" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: resizeComboBox - - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["no", "crop", "fit", "stretch"] - currentIndex: model.indexOf(Settings.settings.wallpaperResize) - onActivated: { - Settings.settings.wallpaperResize = model[index]; - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: resizeComboBox.indicator.width + resizeComboBox.spacing - text: resizeComboBox.displayText - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: resizeComboBox.width - width - 12 - y: resizeComboBox.topPadding + (resizeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: resizeComboBox.height - width: resizeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null - currentIndex: resizeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - - } - - delegate: ItemDelegate { - width: resizeComboBox.width - highlighted: resizeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - Text { - text: "Transition Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: transitionTypeComboBox - - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] - currentIndex: model.indexOf(Settings.settings.transitionType) - onActivated: { - Settings.settings.transitionType = model[index]; - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: transitionTypeComboBox.indicator.width + transitionTypeComboBox.spacing - text: transitionTypeComboBox.displayText - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: transitionTypeComboBox.width - width - 12 - y: transitionTypeComboBox.topPadding + (transitionTypeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: transitionTypeComboBox.height - width: transitionTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null - currentIndex: transitionTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - - } - - delegate: ItemDelegate { - width: transitionTypeComboBox.width - highlighted: transitionTypeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Transition FPS" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.transitionFps - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: fpsSlider - - Layout.fillWidth: true - from: 30 - to: 500 - stepSize: 5 - value: Settings.settings.transitionFps - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionFps = Math.round(value); - } - - background: Rectangle { - x: fpsSlider.leftPadding - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: fpsSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: fpsSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Transition Duration (seconds)" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.transitionDuration.toFixed(3) - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: durationSlider - - Layout.fillWidth: true - from: 0.25 - to: 10 - stepSize: 0.05 - value: Settings.settings.transitionDuration - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionDuration = value; - } - - background: Rectangle { - x: durationSlider.leftPadding - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: durationSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: durationSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - } - -} diff --git a/Widgets/Sidebar/Config/WeatherSettings.qml b/Widgets/Sidebar/Config/WeatherSettings.qml deleted file mode 100644 index 511e79a..0000000 --- a/Widgets/Sidebar/Config/WeatherSettings.qml +++ /dev/null @@ -1,275 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Settings - -Rectangle { - id: weatherSettingsCard - Layout.fillWidth: true - Layout.preferredHeight: 320 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - - RowLayout { - Layout.fillWidth: true - spacing: 12 - - Text { - text: "wb_sunny" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - - Text { - text: "Weather Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "City" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: cityInput - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.weatherCity - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - focus: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhNone - - onTextChanged: { - Settings.settings.weatherCity = text; - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: { - cityInput.forceActiveFocus(); - } - } - } - } - } - - - RowLayout { - spacing: 12 - Layout.fillWidth: true - - Text { - text: "Temperature Unit" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: customSwitch - width: 52 - height: 32 - radius: 16 - color: Theme.accentPrimary - border.color: Theme.accentPrimary - border.width: 2 - - Rectangle { - id: thumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useFahrenheit ? customSwitch.width - width - 2 : 2 - - Text { - anchors.centerIn: parent - text: Settings.settings.useFahrenheit ? "\u00b0F" : "\u00b0C" - font.family: Theme.fontFamily - font.pixelSize: 12 - font.bold: true - color: Theme.textPrimary - } - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useFahrenheit = !Settings.settings.useFahrenheit; - } - } - } - - - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use 12 Hour Clock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: use12HourClockSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.use12HourClock = !Settings.settings.use12HourClock; - } - } - } - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "US Style Date" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: reverseDayMonthSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: reverseDayMonthThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; - } - } - } - } - } -} diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index 0ddc9c0..d970789 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -4,11 +4,22 @@ import Quickshell import Quickshell.Io import Quickshell.Wayland import qs.Settings -import qs.Widgets.Sidebar.Config +import qs.Widgets.SettingsWindow import qs.Components PanelWithOverlay { id: sidebarPopup + property var shell: null + + // Trigger initial weather loading when component is completed + Component.onCompleted: { + // Load initial weather data after a short delay to ensure all components are ready + Qt.callLater(function() { + if (weather && weather.fetchCityWeather) { + weather.fetchCityWeather(); + } + }); + } function showAt() { sidebarPopupRect.showAt(); @@ -56,17 +67,17 @@ PanelWithOverlay { } function hidePopup() { - if (sidebarPopupRect.settingsModal && sidebarPopupRect.settingsModal.visible) { - sidebarPopupRect.settingsModal.visible = false; + if (shell && shell.settingsWindow && shell.settingsWindow.visible) { + shell.settingsWindow.visible = false; } - if (wallpaperPanel && wallpaperPanel.visible) { - wallpaperPanel.visible = false; + if (wallpaperPanelLoader.active && wallpaperPanelLoader.item && wallpaperPanelLoader.item.visible) { + wallpaperPanelLoader.item.visible = false; } - if (sidebarPopupRect.wifiPanelModal && sidebarPopupRect.wifiPanelModal.visible) { - sidebarPopupRect.wifiPanelModal.visible = false; + if (wifiPanelLoader.active && wifiPanelLoader.item && wifiPanelLoader.item.visible) { + wifiPanelLoader.item.visible = false; } - if (sidebarPopupRect.bluetoothPanelModal && sidebarPopupRect.bluetoothPanelModal.visible) { - sidebarPopupRect.bluetoothPanelModal.visible = false; + if (bluetoothPanelLoader.active && bluetoothPanelLoader.item && bluetoothPanelLoader.item.visible) { + bluetoothPanelLoader.item.visible = false; } if (sidebarPopup.visible) { slideAnim.from = 0; @@ -124,11 +135,44 @@ PanelWithOverlay { } } - property alias settingsModal: settingsModal - property alias wifiPanelModal: wifiPanel.panel - property alias bluetoothPanelModal: bluetoothPanel.panel - SettingsModal { + // Access the shell's SettingsWindow instead of creating a new one + + // LazyLoader for WifiPanel + LazyLoader { + id: wifiPanelLoader + loading: false + component: WifiPanel {} + } + + // LazyLoader for BluetoothPanel + LazyLoader { + id: bluetoothPanelLoader + loading: false + component: BluetoothPanel {} + } + + // LazyLoader for WallpaperPanel + LazyLoader { + id: wallpaperPanelLoader + loading: false + component: WallpaperPanel { + Component.onCompleted: { + if (parent) { + anchors.top = parent.top; + anchors.right = parent.right; + } + } + } + } + + // SettingsIcon component + SettingsIcon { id: settingsModal + onWeatherRefreshRequested: { + if (weather && weather.fetchCityWeather) { + weather.fetchCityWeather(); + } + } } Item { @@ -226,7 +270,14 @@ PanelWithOverlay { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: wifiPanel.showAt() + onClicked: { + if (!wifiPanelLoader.active) { + wifiPanelLoader.loading = true; + } + if (wifiPanelLoader.item) { + wifiPanelLoader.item.showAt(); + } + } } StyledTooltip { @@ -261,7 +312,14 @@ PanelWithOverlay { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: bluetoothPanel.showAt() + onClicked: { + if (!bluetoothPanelLoader.active) { + bluetoothPanelLoader.loading = true; + } + if (bluetoothPanelLoader.item) { + bluetoothPanelLoader.item.showAt(); + } + } } StyledTooltip { @@ -274,16 +332,6 @@ PanelWithOverlay { } } - // Hidden panel components for modal functionality - WifiPanel { - id: wifiPanel - visible: false - } - BluetoothPanel { - id: bluetoothPanel - visible: false - } - Item { Layout.fillHeight: true } @@ -310,10 +358,18 @@ PanelWithOverlay { } onSettingsRequested: { - settingsModal.visible = true; + // Use the SettingsModal's openSettings function + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) { + settingsModal.openSettings(); + } } onWallpaperRequested: { - wallpaperPanel.visible = true; + if (!wallpaperPanelLoader.active) { + wallpaperPanelLoader.loading = true; + } + if (wallpaperPanelLoader.item) { + wallpaperPanelLoader.item.visible = true; + } } } } @@ -408,15 +464,5 @@ PanelWithOverlay { } } } - - WallpaperPanel { - id: wallpaperPanel - Component.onCompleted: { - if (parent) { - anchors.top = parent.top; - anchors.right = parent.right; - } - } - } } } diff --git a/Widgets/Sidebar/Panel/SettingsIcon.qml b/Widgets/Sidebar/Panel/SettingsIcon.qml new file mode 100644 index 0000000..02fdb1d --- /dev/null +++ b/Widgets/Sidebar/Panel/SettingsIcon.qml @@ -0,0 +1,80 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import qs.Settings +import qs.Services +import qs.Widgets.SettingsWindow +import qs.Components + +PanelWindow { + id: settingsModal + implicitWidth: 480 + implicitHeight: 780 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + + // Signal to request weather refresh + signal weatherRefreshRequested() + + // Property to track the settings window instance + property var settingsWindow: null + + // Function to open the modal and initialize temp values + function openSettings() { + if (!settingsWindow) { + // Create new window + settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues + if (settingsWindow) { + settingsWindow.visible = true; + // Handle window closure + settingsWindow.visibleChanged.connect(function() { + if (settingsWindow && !settingsWindow.visible) { + // Trigger weather refresh when settings close + weatherRefreshRequested(); + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + }); + } + } else if (settingsWindow.visible) { + // Close and destroy window + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } + } + + // Function to close the modal and release focus + function closeSettings() { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } + } + + Component { + id: settingsComponent + SettingsWindow {} + } + + // Clean up on destruction + Component.onDestruction: { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + } + +} diff --git a/Widgets/Sidebar/Panel/Weather.qml b/Widgets/Sidebar/Panel/Weather.qml index 664f3fa..2317e9c 100644 --- a/Widgets/Sidebar/Panel/Weather.qml +++ b/Widgets/Sidebar/Panel/Weather.qml @@ -15,13 +15,17 @@ Rectangle { property var weatherData: null property string errorString: "" property bool isVisible: false + property int lastFetchTime: 0 + property bool isLoading: false // Auto-refetch weather when city changes Connections { target: Settings.settings function onWeatherCityChanged() { if (isVisible && city !== "") { - fetchCityWeather() + // Force refresh when city changes + lastFetchTime = 0; + fetchCityWeather(); } } } @@ -33,20 +37,42 @@ Rectangle { } function fetchCityWeather() { + if (!city || city.trim() === "") { + errorString = "No city configured"; + return; + } + + // Check if we should fetch new data (avoid fetching too frequently) + var currentTime = Date.now(); + var timeSinceLastFetch = currentTime - lastFetchTime; + + // Only skip if we have recent data AND lastFetchTime is not 0 (initial state) + if (lastFetchTime > 0 && timeSinceLastFetch < 60000) { // 1 minute + return; // Skip if last fetch was less than 1 minute ago + } + + isLoading = true; + errorString = ""; + WeatherHelper.fetchCityWeather(city, function(result) { weatherData = result.weather; + lastFetchTime = currentTime; errorString = ""; + isLoading = false; }, function(err) { errorString = err; + isLoading = false; } ); } function startWeatherFetch() { isVisible = true - fetchCityWeather() + // Force refresh when panel opens, regardless of time check + lastFetchTime = 0; + fetchCityWeather(); } function stopWeatherFetch() { @@ -77,12 +103,21 @@ Rectangle { Text { id: weatherIcon - text: weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud" + text: isLoading ? "sync" : (weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud") font.family: "Material Symbols Outlined" font.pixelSize: 28 verticalAlignment: Text.AlignVCenter - color: Theme.accentPrimary + color: isLoading ? Theme.accentPrimary : Theme.accentPrimary Layout.alignment: Qt.AlignVCenter + + // Add rotation animation for loading state + RotationAnimation on rotation { + running: isLoading + from: 0 + to: 360 + duration: 1000 + loops: Animation.Infinite + } } ColumnLayout { diff --git a/shell.qml b/shell.qml index 844f8ab..b8b7cfa 100644 --- a/shell.qml +++ b/shell.qml @@ -9,6 +9,7 @@ import qs.Bar.Modules import qs.Widgets import qs.Widgets.LockScreen import qs.Widgets.Notification +import qs.Widgets.SettingsWindow import qs.Settings import qs.Helpers @@ -19,8 +20,17 @@ Scope { id: root property alias appLauncherPanel: appLauncherPanel - property var notificationHistoryWin: notificationHistoryWin + property var notificationHistoryWin: notificationHistoryLoader.active ? notificationHistoryLoader.item : null + property var settingsWindow: null property bool pendingReload: false + + // Function to load notification history + function loadNotificationHistory() { + if (!notificationHistoryLoader.active) { + notificationHistoryLoader.loading = true; + } + return notificationHistoryLoader; + } // Helper function to round value to nearest step function roundToStep(value, step) { @@ -50,7 +60,7 @@ Scope { Bar { id: bar shell: root - property var notificationHistoryWin: notificationHistoryWin + property var notificationHistoryWin: notificationHistoryLoader.active ? notificationHistoryLoader.item : null } Variants { @@ -62,6 +72,9 @@ Scope { } } + Background {} + Overview {} + Applauncher { id: appLauncherPanel visible: false @@ -94,8 +107,8 @@ Scope { } } } - if (notificationHistoryWin) { - notificationHistoryWin.addToHistory({ + if (notificationHistoryLoader.active && notificationHistoryLoader.item) { + notificationHistoryLoader.item.addToHistory({ id: notification.id, appName: notification.appName || "Notification", summary: notification.summary || "", @@ -111,8 +124,33 @@ Scope { id: notificationPopup } - NotificationHistory { - id: notificationHistoryWin + // LazyLoader for NotificationHistory - only load when needed + LazyLoader { + id: notificationHistoryLoader + loading: false + component: NotificationHistory {} + } + + // Centralized LazyLoader for SettingsWindow - prevents crashes on multiple opens + LazyLoader { + id: settingsWindowLoader + loading: false + component: SettingsWindow { + Component.onCompleted: { + root.settingsWindow = this; + } + } + } + + // Function to safely show/hide settings window + function toggleSettingsWindow() { + if (!settingsWindowLoader.active) { + settingsWindowLoader.loading = true; + } + + if (settingsWindowLoader.item) { + settingsWindowLoader.item.visible = !settingsWindowLoader.item.visible; + } } // Reference to the default audio sink from Pipewire