From a498671ef137e2f84160d5cb97590dcd42ed3ea9 Mon Sep 17 00:00:00 2001 From: ferreo Date: Fri, 18 Jul 2025 15:43:22 +0100 Subject: [PATCH 1/2] feat: settings is now json, refactor panels to be able to dismiss by clicking outside --- .gitignore | 1 + Bar/Bar.qml | 46 +- Bar/Modules/ActiveWindow.qml | 11 +- Bar/Modules/Applauncher.qml | 710 ++++++++++-------- Bar/Modules/Media.qml | 8 +- Bar/Modules/SystemInfo.qml | 2 +- Components/CircularSpectrum.qml | 16 +- Components/PanelWithOverlay.qml | 44 ++ Components/StyledTooltip.qml | 32 +- Components/Tabs.qml | 88 +-- Helpers/IPCHandlers.qml | 1 - Services/WallpaperManager.qml | 31 +- Services/WorkspaceManager.qml | 2 +- Settings/Settings.qml | 140 ++-- Settings/Theme.qml | 10 +- Templates/wallust.toml | 2 +- Widgets/Background.qml | 2 +- Widgets/LockScreen.qml | 10 +- Widgets/Notification/NotificationHistory.qml | 202 +++-- Widgets/Notification/NotificationIcon.qml | 30 + Widgets/Notification/NotificationPopup.qml | 96 ++- Widgets/Overview.qml | 2 +- Widgets/Sidebar/Button.qml | 13 - .../Sidebar/Config/CollapsibleCategory.qml | 6 +- Widgets/Sidebar/Config/ProfileSettings.qml | 65 +- Widgets/Sidebar/Config/SettingsModal.qml | 153 +--- Widgets/Sidebar/Config/WallpaperSettings.qml | 80 +- Widgets/Sidebar/Config/WeatherSettings.qml | 23 +- Widgets/Sidebar/Panel/BluetoothPanel.qml | 6 +- Widgets/Sidebar/Panel/Music.qml | 6 +- Widgets/Sidebar/Panel/PanelPopup.qml | 704 +++++++++-------- Widgets/Sidebar/Panel/PowerProfile.qml | 6 +- Widgets/Sidebar/Panel/System.qml | 6 +- Widgets/Sidebar/Panel/SystemMonitor.qml | 6 +- Widgets/Sidebar/Panel/WallpaperPanel.qml | 10 +- Widgets/Sidebar/Panel/Weather.qml | 12 +- 36 files changed, 1282 insertions(+), 1300 deletions(-) create mode 100644 .gitignore create mode 100644 Components/PanelWithOverlay.qml create mode 100644 Widgets/Notification/NotificationIcon.qml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb3cef2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.qmlls.ini \ No newline at end of file diff --git a/Bar/Bar.qml b/Bar/Bar.qml index ebd120e..aa560f3 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -59,7 +59,7 @@ Scope { SystemInfo { anchors.verticalCenter: parent.verticalCenter } - + Media { anchors.verticalCenter: parent.verticalCenter } @@ -83,8 +83,7 @@ Scope { anchors.rightMargin: 18 spacing: 12 - NotificationHistory { - id: notificationHistoryWin + NotificationIcon { anchors.verticalCenter: parent.verticalCenter } @@ -132,20 +131,22 @@ Scope { } PanelWindow { - id: topCornerPanel + id: topLeftPanel anchors.top: true anchors.left: true - anchors.right: true + color: "transparent" screen: modelData margins.top: 36 WlrLayershell.exclusionMode: ExclusionMode.Ignore visible: true - + WlrLayershell.layer: WlrLayer.Background + aboveWindows: false + WlrLayershell.namespace: "swww-daemon" implicitHeight: 24 Corners { - id: topleftCorner + id: topLeftCorner position: "bottomleft" size: 1.3 fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" @@ -153,9 +154,25 @@ Scope { offsetY: 0 anchors.top: parent.top } + } + + PanelWindow { + id: topRightPanel + anchors.top: true + anchors.right: true + color: "transparent" + screen: modelData + margins.top: 36 + WlrLayershell.exclusionMode: ExclusionMode.Ignore + visible: true + WlrLayershell.layer: WlrLayer.Background + aboveWindows: false + WlrLayershell.namespace: "swww-daemon" + + implicitHeight: 24 Corners { - id: toprightCorner + id: topRightCorner position: "bottomright" size: 1.3 fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" @@ -173,6 +190,9 @@ Scope { screen: modelData WlrLayershell.exclusionMode: ExclusionMode.Ignore visible: true + WlrLayershell.layer: WlrLayer.Background + aboveWindows: false + WlrLayershell.namespace: "swww-daemon" implicitHeight: 24 @@ -188,13 +208,16 @@ Scope { } PanelWindow { - id: bottomRightCornerPanel + id: bottomRightPanel anchors.bottom: true anchors.right: true color: "transparent" screen: modelData WlrLayershell.exclusionMode: ExclusionMode.Ignore visible: true + WlrLayershell.layer: WlrLayer.Background + aboveWindows: false + WlrLayershell.namespace: "swww-daemon" implicitHeight: 24 @@ -208,13 +231,8 @@ Scope { anchors.top: parent.top } } - - Loader { - id: tabViewerLoader - } } } - } // This alias exposes the visual bar's visibility to the outside world diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index a6cdae5..2e0aa2d 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -12,10 +12,11 @@ PanelWindow { anchors.top: true anchors.left: true anchors.right: true + focusable: false margins.top: barHeight visible: !activeWindowWrapper.finallyHidden implicitHeight: activeWindowTitleContainer.height - implicitWidth: activeWindowTitleContainer.x + implicitWidth: 0 property int barHeight: 36 color: "transparent" @@ -104,7 +105,7 @@ PanelWindow { bottomLeftRadius: Math.max(0, width / 2) bottomRightRadius: Math.max(0, width / 2) - width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + (Settings.showActiveWindowIcon ? 28 : 22)) + width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + (Settings.settings.showActiveWindowIcon ? 28 : 22)) height: activeWindowTitle.implicitHeight + 12 anchors.top: parent.top @@ -118,7 +119,7 @@ PanelWindow { anchors.leftMargin: 6 anchors.verticalCenter: parent.verticalCenter source: ToplevelManager?.activeToplevel ? getIcon() : "" - visible: Settings.showActiveWindowIcon + visible: Settings.settings.showActiveWindowIcon anchors.verticalCenterOffset: -3 } @@ -129,12 +130,12 @@ PanelWindow { color: Theme.textSecondary elide: Text.ElideRight anchors.left: icon.right - anchors.leftMargin: Settings.showActiveWindowIcon ? 4 : 6 + anchors.leftMargin: Settings.settings.showActiveWindowIcon ? 4 : 6 anchors.right: parent.right anchors.rightMargin: 6 anchors.verticalCenter: parent.verticalCenter anchors.verticalCenterOffset: -3 - horizontalAlignment: Settings.showActiveWindowIcon ? Text.AlignRight : Text.AlignHCenter + horizontalAlignment: Settings.settings.showActiveWindowIcon ? Text.AlignRight : Text.AlignHCenter verticalAlignment: Text.AlignVCenter maximumLineCount: 1 } diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index 09c486b..ed93dac 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -5,380 +5,422 @@ import Quickshell import Quickshell.Io import qs.Components import qs.Settings -import Qt5Compat.GraphicalEffects import Quickshell.Wayland import "../../Helpers/Fuzzysort.js" as Fuzzysort -PanelWindow { +PanelWithOverlay { id: appLauncherPanel - implicitWidth: 460 - implicitHeight: 640 - color: "transparent" - visible: false - WlrLayershell.exclusionMode: ExclusionMode.Ignore - WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None - screen: (typeof modelData !== 'undefined' ? modelData : null) - property bool shouldBeVisible: false - - anchors.top: true - margins.top: 36 function showAt() { - visible = true; - shouldBeVisible = true; - searchField.forceActiveFocus() - root.selectedIndex = 0; - root.appModel = DesktopEntries.applications.values; - root.updateFilter(); + appLauncherPanelRect.showAt(); } function hidePanel() { - shouldBeVisible = false; - searchField.text = ""; - root.selectedIndex = 0; + appLauncherPanelRect.hidePanel(); + } + + function show() { + appLauncherPanelRect.showAt(); + } + + function dismiss() { + appLauncherPanelRect.hidePanel(); } Rectangle { - id: root - width: 400 - height: 640 - x: (parent.width - width) / 2 - color: Theme.backgroundPrimary - bottomLeftRadius: 28 - bottomRightRadius: 28 + id: appLauncherPanelRect + implicitWidth: 460 + implicitHeight: 640 + color: "transparent" + visible: parent.visible + property bool shouldBeVisible: false + anchors.top: parent.top + anchors.horizontalCenter: parent.horizontalCenter - property var appModel: DesktopEntries.applications.values - property var filteredApps: [] - property int selectedIndex: 0 - property int targetY: (parent.height - height) / 2 - y: appLauncherPanel.shouldBeVisible ? targetY : -height - Behavior on y { - NumberAnimation { duration: 300; easing.type: Easing.OutCubic } + function showAt() { + appLauncherPanel.visible = true; + shouldBeVisible = true; + searchField.forceActiveFocus(); + root.selectedIndex = 0; + root.appModel = DesktopEntries.applications.values; + root.updateFilter(); } - scale: appLauncherPanel.shouldBeVisible ? 1 : 0 - Behavior on scale { - NumberAnimation { duration: 200; easing.type: Easing.InOutCubic } - } - onScaleChanged: { - if (scale === 0 && !appLauncherPanel.shouldBeVisible) { - appLauncherPanel.visible = false; - } - } - function isMathExpression(str) { - return /^[-+*/().0-9\s]+$/.test(str); - } - function safeEval(expr) { - try { - return Function('return (' + expr + ')')(); - } catch (e) { - return undefined; - } - } - function updateFilter() { - var query = searchField.text ? searchField.text.toLowerCase() : ""; - var apps = root.appModel.slice(); - var results = []; - // Calculator mode: starts with '=' - if (query.startsWith("=")) { - var expr = searchField.text.slice(1).trim(); - if (expr && isMathExpression(expr)) { - var value = safeEval(expr); - if (value !== undefined && value !== null && value !== "") { - results.push({ - isCalculator: true, - name: `Calculator: ${expr} = ${value}`, - result: value, - expr: expr, - icon: "calculate" - }); - } - } - } - if (!query || query.startsWith("=")) { - results = results.concat(apps.sort(function(a, b) { - return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); - })); - } else { - var fuzzyResults = Fuzzysort.go(query, apps, { keys: ["name", "comment", "genericName"] }); - results = results.concat(fuzzyResults.map(function(r) { return r.obj; })); - } - root.filteredApps = results; + + function hidePanel() { + shouldBeVisible = false; + searchField.text = ""; root.selectedIndex = 0; } - function selectNext() { - if (filteredApps.length > 0) - selectedIndex = Math.min(selectedIndex + 1, filteredApps.length - 1); - } - function selectPrev() { - if (filteredApps.length > 0) - selectedIndex = Math.max(selectedIndex - 1, 0); - } - function activateSelected() { - if (filteredApps.length === 0) - return; + Rectangle { + id: root + width: 460 + height: 640 + x: (parent.width - width) / 2 + color: Theme.backgroundPrimary + bottomLeftRadius: 28 + bottomRightRadius: 28 - var modelData = filteredApps[selectedIndex]; - - if (modelData.isCalculator) { - Qt.callLater(function() { - Quickshell.clipboardText = String(modelData.result); - Quickshell.execDetached([ - "notify-send", - "Calculator Result", - `${modelData.expr} = ${modelData.result} (copied to clipboard)` - ]); - }); - } else if (modelData.execute) { - modelData.execute(); - } else { - var execCmd = modelData.execString || modelData.exec || ""; - if (execCmd) { - execCmd = execCmd.replace(/\s?%[fFuUdDnNiCkvm]/g, ''); - Quickshell.execDetached(["sh", "-c", execCmd.trim()]); + property var appModel: DesktopEntries.applications.values + property var filteredApps: [] + property int selectedIndex: 0 + property int targetY: (parent.height - height) / 2 + y: appLauncherPanelRect.shouldBeVisible ? targetY : -height + Behavior on y { + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic } } - - appLauncherPanel.hidePanel(); - searchField.text = ""; - } - - Component.onCompleted: updateFilter() - - ColumnLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.margins: 32 - spacing: 18 - - // Search Bar - Rectangle { - id: searchBar - color: Theme.surfaceVariant - radius: 22 - height: 48 - Layout.fillWidth: true - border.color: searchField.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: searchField.activeFocus ? 2 : 1 - - RowLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - anchors.leftMargin: 14 - anchors.rightMargin: 14 - spacing: 10 - Text { - text: "search" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader - color: searchField.activeFocus ? Theme.accentPrimary : Theme.textSecondary - verticalAlignment: Text.AlignVCenter - Layout.alignment: Qt.AlignVCenter - } - TextField { - id: searchField - placeholderText: "Search apps..." - color: Theme.textPrimary - placeholderTextColor: Theme.textSecondary - background: null - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - onTextChanged: root.updateFilter() - selectedTextColor: Theme.onAccent - selectionColor: Theme.accentPrimary - padding: 0 - verticalAlignment: TextInput.AlignVCenter - leftPadding: 0 - rightPadding: 0 - topPadding: 0 - bottomPadding: 0 - font.bold: true - Component.onCompleted: contentItem.cursorColor = Theme.textPrimary - onActiveFocusChanged: contentItem.cursorColor = Theme.textPrimary - - Keys.onDownPressed: root.selectNext() - Keys.onUpPressed: root.selectPrev() - Keys.onEnterPressed: root.activateSelected() - Keys.onReturnPressed: root.activateSelected() - Keys.onEscapePressed: appLauncherPanel.hidePanel() + scale: appLauncherPanelRect.shouldBeVisible ? 1 : 0 + Behavior on scale { + NumberAnimation { + duration: 200 + easing.type: Easing.InOutCubic + } + } + onScaleChanged: { + if (scale === 0 && !appLauncherPanelRect.shouldBeVisible) { + appLauncherPanel.visible = false; + } + } + function isMathExpression(str) { + return /^[-+*/().0-9\s]+$/.test(str); + } + function safeEval(expr) { + try { + return Function('return (' + expr + ')')(); + } catch (e) { + return undefined; + } + } + function updateFilter() { + var query = searchField.text ? searchField.text.toLowerCase() : ""; + var apps = root.appModel.slice(); + var results = []; + // Calculator mode: starts with '=' + if (query.startsWith("=")) { + var expr = searchField.text.slice(1).trim(); + if (expr && isMathExpression(expr)) { + var value = safeEval(expr); + if (value !== undefined && value !== null && value !== "") { + results.push({ + isCalculator: true, + name: `Calculator: ${expr} = ${value}`, + result: value, + expr: expr, + icon: "calculate" + }); + } } } - Behavior on border.color { ColorAnimation { duration: 120 } } - Behavior on border.width { NumberAnimation { duration: 120 } } + if (!query || query.startsWith("=")) { + results = results.concat(apps.sort(function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + })); + } else { + var fuzzyResults = Fuzzysort.go(query, apps, { + keys: ["name", "comment", "genericName"] + }); + results = results.concat(fuzzyResults.map(function (r) { + return r.obj; + })); + } + root.filteredApps = results; + root.selectedIndex = 0; + } + function selectNext() { + if (filteredApps.length > 0) + selectedIndex = Math.min(selectedIndex + 1, filteredApps.length - 1); + } + function selectPrev() { + if (filteredApps.length > 0) + selectedIndex = Math.max(selectedIndex - 1, 0); } - // App List Card - Rectangle { - color: Theme.surface - radius: 20 - Layout.fillWidth: true - Layout.fillHeight: true - clip: true - anchors.margins: 0 - property int innerPadding: 16 + function activateSelected() { + if (filteredApps.length === 0) + return; - Item { - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: parent.innerPadding - visible: false + var modelData = filteredApps[selectedIndex]; + + if (modelData.isCalculator) { + Qt.callLater(function () { + Quickshell.clipboardText = String(modelData.result); + Quickshell.execDetached(["notify-send", "Calculator Result", `${modelData.expr} = ${modelData.result} (copied to clipboard)`]); + }); + } else if (modelData.execute) { + modelData.execute(); + } else { + var execCmd = modelData.execString || modelData.exec || ""; + if (execCmd) { + execCmd = execCmd.replace(/\s?%[fFuUdDnNiCkvm]/g, ''); + Quickshell.execDetached(["sh", "-c", execCmd.trim()]); + } } - ListView { - id: appList - anchors.fill: parent - anchors.margins: parent.innerPadding - spacing: 2 - model: root.filteredApps - currentIndex: root.selectedIndex - delegate: Item { - id: appDelegate - width: appList.width - height: 48 - property bool hovered: mouseArea.containsMouse - property bool isSelected: index === root.selectedIndex + appLauncherPanel.hidePanel(); + searchField.text = ""; + } - Rectangle { - anchors.fill: parent - color: hovered || isSelected ? Theme.accentPrimary : "transparent" - radius: 12 - border.color: hovered || isSelected ? Theme.accentPrimary : "transparent" - border.width: hovered || isSelected ? 2 : 0 - Behavior on color { ColorAnimation { duration: 120 } } - Behavior on border.color { ColorAnimation { duration: 120 } } - Behavior on border.width { NumberAnimation { duration: 120 } } + Component.onCompleted: updateFilter() + + ColumnLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.margins: 32 + spacing: 18 + + // Search Bar + Rectangle { + id: searchBar + color: Theme.surfaceVariant + radius: 22 + height: 48 + Layout.fillWidth: true + border.color: searchField.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: searchField.activeFocus ? 2 : 1 + + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + anchors.leftMargin: 14 + anchors.rightMargin: 14 + spacing: 10 + Text { + text: "search" + font.family: "Material Symbols Outlined" + font.pixelSize: Theme.fontSizeHeader + color: searchField.activeFocus ? Theme.accentPrimary : Theme.textSecondary + verticalAlignment: Text.AlignVCenter + Layout.alignment: Qt.AlignVCenter } + TextField { + id: searchField + placeholderText: "Search apps..." + color: Theme.textPrimary + placeholderTextColor: Theme.textSecondary + background: null + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeBody + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + onTextChanged: root.updateFilter() + selectedTextColor: Theme.onAccent + selectionColor: Theme.accentPrimary + padding: 0 + verticalAlignment: TextInput.AlignVCenter + leftPadding: 0 + rightPadding: 0 + topPadding: 0 + bottomPadding: 0 + font.bold: true + Component.onCompleted: contentItem.cursorColor = Theme.textPrimary + onActiveFocusChanged: contentItem.cursorColor = Theme.textPrimary - RowLayout { - anchors.fill: parent - anchors.leftMargin: 10 - anchors.rightMargin: 10 - spacing: 10 - Item { - width: 28; height: 28 - property bool iconLoaded: !modelData.isCalculator && iconImg.status === Image.Ready && iconImg.source !== "" && iconImg.status !== Image.Error - Image { - id: iconImg - anchors.fill: parent - fillMode: Image.PreserveAspectFit - smooth: true - cache: false - asynchronous: true - source: modelData.isCalculator ? "qrc:/icons/calculate.svg" : Quickshell.iconPath(modelData.icon, "application-x-executable") - visible: modelData.isCalculator || parent.iconLoaded - } - Text { - anchors.centerIn: parent - visible: !modelData.isCalculator && !parent.iconLoaded - text: "broken_image" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader - color: Theme.accentPrimary - } - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 1 - Text { - text: modelData.name - color: hovered || isSelected ? Theme.onAccent : Theme.textPrimary - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall - font.bold: hovered || isSelected - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - Layout.fillWidth: true - } - Text { - text: modelData.isCalculator ? (modelData.expr + " = " + modelData.result) - : (modelData.comment || modelData.genericName || "No description available") - color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption - font.italic: !(modelData.comment || modelData.genericName) - opacity: (modelData.comment || modelData.genericName) ? 1.0 : 0.6 - elide: Text.ElideRight - Layout.fillWidth: true - } - } - - Item { Layout.fillWidth: true } - Text { - text: modelData.isCalculator ? "content_copy" : "chevron_right" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody - color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary - verticalAlignment: Text.AlignVCenter - } + Keys.onDownPressed: root.selectNext() + Keys.onUpPressed: root.selectPrev() + Keys.onEnterPressed: root.activateSelected() + Keys.onReturnPressed: root.activateSelected() + Keys.onEscapePressed: appLauncherPanel.hidePanel() } - - Rectangle { - id: ripple - anchors.fill: parent - color: Theme.onAccent - opacity: 0.0 + } + Behavior on border.color { + ColorAnimation { + duration: 120 } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - ripple.opacity = 0.18 - rippleNumberAnimation.start() - root.selectedIndex = index - root.activateSelected() - } - onPressed: ripple.opacity = 0.18 - onReleased: ripple.opacity = 0.0 - } - + } + Behavior on border.width { NumberAnimation { - id: rippleNumberAnimation - target: ripple - property: "opacity" - to: 0.0 - duration: 320 + duration: 120 } + } + } - Rectangle { - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - height: 1 - color: Theme.outline - opacity: index === appList.count - 1 ? 0 : 0.10 + // App List Card + Rectangle { + color: Theme.surface + radius: 20 + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + property int innerPadding: 16 + + Item { + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + height: parent.innerPadding + visible: false + } + + ListView { + id: appList + anchors.fill: parent + anchors.margins: parent.innerPadding + spacing: 2 + model: root.filteredApps + currentIndex: root.selectedIndex + delegate: Item { + id: appDelegate + width: appList.width + height: 48 + property bool hovered: mouseArea.containsMouse + property bool isSelected: index === root.selectedIndex + + Rectangle { + anchors.fill: parent + color: hovered || isSelected ? Theme.accentPrimary : "transparent" + radius: 12 + border.color: hovered || isSelected ? Theme.accentPrimary : "transparent" + border.width: hovered || isSelected ? 2 : 0 + Behavior on color { + ColorAnimation { + duration: 120 + } + } + Behavior on border.color { + ColorAnimation { + duration: 120 + } + } + Behavior on border.width { + NumberAnimation { + duration: 120 + } + } + } + + RowLayout { + anchors.fill: parent + anchors.leftMargin: 10 + anchors.rightMargin: 10 + spacing: 10 + Item { + width: 28 + height: 28 + property bool iconLoaded: !modelData.isCalculator && iconImg.status === Image.Ready && iconImg.source !== "" && iconImg.status !== Image.Error + Image { + id: iconImg + anchors.fill: parent + fillMode: Image.PreserveAspectFit + smooth: true + cache: false + asynchronous: true + source: modelData.isCalculator ? "qrc:/icons/calculate.svg" : Quickshell.iconPath(modelData.icon, "application-x-executable") + visible: modelData.isCalculator || parent.iconLoaded + } + Text { + anchors.centerIn: parent + visible: !modelData.isCalculator && !parent.iconLoaded + text: "broken_image" + font.family: "Material Symbols Outlined" + font.pixelSize: Theme.fontSizeHeader + color: Theme.accentPrimary + } + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 1 + Text { + text: modelData.name + color: hovered || isSelected ? Theme.onAccent : Theme.textPrimary + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeSmall + font.bold: hovered || isSelected + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + Layout.fillWidth: true + } + Text { + text: modelData.isCalculator ? (modelData.expr + " = " + modelData.result) : (modelData.comment || modelData.genericName || "No description available") + color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeCaption + font.italic: !(modelData.comment || modelData.genericName) + opacity: (modelData.comment || modelData.genericName) ? 1.0 : 0.6 + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + Item { + Layout.fillWidth: true + } + Text { + text: modelData.isCalculator ? "content_copy" : "chevron_right" + font.family: "Material Symbols Outlined" + font.pixelSize: Theme.fontSizeBody + color: hovered || isSelected ? Theme.onAccent : Theme.textSecondary + verticalAlignment: Text.AlignVCenter + } + } + + Rectangle { + id: ripple + anchors.fill: parent + color: Theme.onAccent + opacity: 0.0 + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + ripple.opacity = 0.18; + rippleNumberAnimation.start(); + root.selectedIndex = index; + root.activateSelected(); + } + onPressed: ripple.opacity = 0.18 + onReleased: ripple.opacity = 0.0 + } + + NumberAnimation { + id: rippleNumberAnimation + target: ripple + property: "opacity" + to: 0.0 + duration: 320 + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 1 + color: Theme.outline + opacity: index === appList.count - 1 ? 0 : 0.10 + } } } } } } - } - Corners { - id: launcherCornerRight - position: "bottomleft" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: root.top - offsetX: 396 - offsetY: 0 - } + Corners { + id: launcherCornerRight + position: "bottomleft" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: root.top + offsetX: 416 + offsetY: 0 + } - Corners { - id: launcherCornerLeft - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: root.top - offsetX: -396 - offsetY: 0 + Corners { + id: launcherCornerLeft + position: "bottomright" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: root.top + offsetX: -416 + offsetY: 0 + } } -} \ No newline at end of file +} diff --git a/Bar/Modules/Media.qml b/Bar/Modules/Media.qml index 7b60017..632d5a6 100644 --- a/Bar/Modules/Media.qml +++ b/Bar/Modules/Media.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import Qt5Compat.GraphicalEffects import qs.Settings import qs.Services @@ -10,7 +10,7 @@ Item { id: mediaControl width: visible ? mediaRow.width : 0 height: 36 - visible: Settings.showMediaInBar && MusicManager.currentPlayer + visible: Settings.settings.showMediaInBar && MusicManager.currentPlayer RowLayout { id: mediaRow diff --git a/Bar/Modules/SystemInfo.qml b/Bar/Modules/SystemInfo.qml index 48a99b1..07c7144 100644 --- a/Bar/Modules/SystemInfo.qml +++ b/Bar/Modules/SystemInfo.qml @@ -6,7 +6,7 @@ import qs.Services Row { id: layout spacing: 10 - visible: Settings.showSystemInfoInBar + visible: Settings.settings.showSystemInfoInBar Row { id: cpuUsageLayout diff --git a/Components/CircularSpectrum.qml b/Components/CircularSpectrum.qml index 42aeb19..634aefd 100644 --- a/Components/CircularSpectrum.qml +++ b/Components/CircularSpectrum.qml @@ -16,7 +16,7 @@ Item { height: usableOuter * 2 onOuterRadiusChanged: () => { - usableOuter = Settings.visualizerType === "fire" ? outerRadius * 0.85 : outerRadius; + usableOuter = Settings.settings.visualizerType === "fire" ? outerRadius * 0.85 : outerRadius; } Repeater { @@ -25,25 +25,25 @@ Item { property real value: root.values[index] property real angle: (index / root.values.length) * 360 width: Math.max(2, (root.innerRadius * 2 * Math.PI) / root.values.length - 4) - height: Settings.visualizerType === "diamond" ? value * 2 * (usableOuter - root.innerRadius) : value * (usableOuter - root.innerRadius) + height: Settings.settings.visualizerType === "diamond" ? value * 2 * (usableOuter - root.innerRadius) : value * (usableOuter - root.innerRadius) radius: width / 2 color: root.fillColor border.color: root.strokeColor border.width: root.strokeWidth antialiasing: true - x: Settings.visualizerType === "radial" ? root.width / 2 - width / 2 : root.width / 2 + root.innerRadius * Math.cos(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - width / 2 + x: Settings.settings.visualizerType === "radial" ? root.width / 2 - width / 2 : root.width / 2 + root.innerRadius * Math.cos(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - width / 2 - y: Settings.visualizerType === "radial" ? root.height / 2 - height : Settings.visualizerType === "diamond" ? root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height / 2 : root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height + y: Settings.settings.visualizerType === "radial" ? root.height / 2 - height : Settings.settings.visualizerType === "diamond" ? root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height / 2 : root.height / 2 - root.innerRadius * Math.sin(Math.PI / 2 + 2 * Math.PI * index / root.values.length) - height transform: [ Rotation { origin.x: width / 2 - origin.y: Settings.visualizerType === "diamond" ? height / 2 : height - angle: Settings.visualizerType === "radial" ? (index / root.values.length) * 360 : Settings.visualizerType === "fire" ? 0 : (index / root.values.length) * 360 - 90 + origin.y: Settings.settings.visualizerType === "diamond" ? height / 2 : height + angle: Settings.settings.visualizerType === "radial" ? (index / root.values.length) * 360 : Settings.settings.visualizerType === "fire" ? 0 : (index / root.values.length) * 360 - 90 }, Translate { - x: Settings.visualizerType === "radial" ? root.innerRadius * Math.cos(2 * Math.PI * index / root.values.length) : 0 - y: Settings.visualizerType === "radial" ? root.innerRadius * Math.sin(2 * Math.PI * index / root.values.length) : 0 + x: Settings.settings.visualizerType === "radial" ? root.innerRadius * Math.cos(2 * Math.PI * index / root.values.length) : 0 + y: Settings.settings.visualizerType === "radial" ? root.innerRadius * Math.sin(2 * Math.PI * index / root.values.length) : 0 } ] diff --git a/Components/PanelWithOverlay.qml b/Components/PanelWithOverlay.qml new file mode 100644 index 0000000..ca32de0 --- /dev/null +++ b/Components/PanelWithOverlay.qml @@ -0,0 +1,44 @@ +import QtQuick +import Quickshell +import Quickshell.Wayland +import qs.Settings + +PanelWindow { + id: outerPanel + property bool showOverlay: true + property int topMargin: 36 + property color overlayColor: showOverlay ? Theme.overlay : "transparent" + + function dismiss() { + visible = false; + } + + function show() { + visible = true; + } + + implicitWidth: screen.width + implicitHeight: screen.height + color: visible ? overlayColor : "transparent" + visible: false + WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + screen: (typeof modelData !== 'undefined' ? modelData : null) + anchors.top: true + anchors.left: true + anchors.right: true + anchors.bottom: true + margins.top: topMargin + + MouseArea { + anchors.fill: parent + onClicked: outerPanel.dismiss() + } + + Behavior on color { + ColorAnimation { + duration: 350 + easing.type: Easing.InOutCubic + } + } +} \ No newline at end of file diff --git a/Components/StyledTooltip.qml b/Components/StyledTooltip.qml index f8018f4..6fe0f12 100644 --- a/Components/StyledTooltip.qml +++ b/Components/StyledTooltip.qml @@ -1,5 +1,4 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 +import QtQuick import QtQuick.Window 2.15 import qs.Settings @@ -12,8 +11,9 @@ Window { flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint color: "transparent" visible: false - minimumWidth: Math.max(minimumWidth, tooltipText.implicitWidth + 24) - minimumHeight: Math.max(minimumHeight, tooltipText.implicitHeight + 16) + + minimumWidth: tooltipText.implicitWidth + 24 + minimumHeight: tooltipText.implicitHeight + 16 property var _timerObj: null onTooltipVisibleChanged: { if (tooltipVisible) { @@ -33,26 +33,32 @@ Window { x = pos.x - width / 2 + targetItem.width / 2; y = pos.y + 12; visible = true; - console.log("StyledTooltip _showNow called"); - console.log("StyledTooltip Theme.textPrimary:", Theme.textPrimary); } function _hideNow() { visible = false; if (_timerObj) { _timerObj.destroy(); _timerObj = null; } } Connections { - target: targetItem - onXChanged: if (tooltipWindow.visible) tooltipWindow._showNow() - onYChanged: if (tooltipWindow.visible) tooltipWindow._showNow() - onWidthChanged: if (tooltipWindow.visible) tooltipWindow._showNow() - onHeightChanged: if (tooltipWindow.visible) tooltipWindow._showNow() + target: tooltipWindow.targetItem + function onXChanged() { + if (tooltipWindow.visible) tooltipWindow._showNow() + } + function onYChanged() { + if (tooltipWindow.visible) tooltipWindow._showNow() + } + function onWidthChanged() { + if (tooltipWindow.visible) tooltipWindow._showNow() + } + function onHeightChanged() { + if (tooltipWindow.visible) tooltipWindow._showNow() + } } - Component.onCompleted: console.log("Tooltip window loaded") + Rectangle { anchors.fill: parent radius: 6 color: "#222" - border.color: Theme.border || "#444" + border.color: Theme.backgroundTertiary || "#444" border.width: 1 opacity: 0.97 z: 1 diff --git a/Components/Tabs.qml b/Components/Tabs.qml index 4aae681..d3befea 100644 --- a/Components/Tabs.qml +++ b/Components/Tabs.qml @@ -1,11 +1,10 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Layouts import qs.Settings Item { id: root - property var tabsModel: [] // [{icon: "videocam", label: "Video"}, ...] + property var tabsModel: [] property int currentIndex: 0 signal tabChanged(int index) @@ -16,17 +15,20 @@ Item { Repeater { model: root.tabsModel - delegate: Column { - width: 56 - spacing: 2 + delegate: Rectangle { + id: tabWrapper + implicitHeight: tab.height + implicitWidth: 56 + color: "transparent" + property bool hovered: false MouseArea { anchors.fill: parent onClicked: { - if (root.currentIndex !== index) { - root.currentIndex = index; - root.tabChanged(index); + if (currentIndex !== index) { + currentIndex = index; + tabChanged(index); } } cursorShape: Qt.PointingHandCursor @@ -35,43 +37,41 @@ Item { onExited: parent.hovered = false } - // Icon - Text { - text: modelData.icon - font.family: "Material Symbols Outlined" - font.pixelSize: 22 - color: index === root.currentIndex - ? (Theme ? Theme.accentPrimary : "#7C3AED") - : parent.hovered - ? (Theme ? Theme.accentPrimary : "#7C3AED") - : (Theme ? Theme.textSecondary : "#444") - anchors.horizontalCenter: parent.horizontalCenter - } + ColumnLayout { + id: tab + spacing: 2 + anchors.centerIn: parent + Layout.fillWidth: true + Layout.fillHeight: true - // Label - Text { - text: modelData.label - font.pixelSize: 12 - font.bold: index === root.currentIndex - color: index === root.currentIndex - ? (Theme ? Theme.accentPrimary : "#7C3AED") - : parent.hovered - ? (Theme ? Theme.accentPrimary : "#7C3AED") - : (Theme ? Theme.textSecondary : "#444") - anchors.horizontalCenter: parent.horizontalCenter - } + // Icon + Text { + text: modelData.icon + font.family: "Material Symbols Outlined" + font.pixelSize: 22 + color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : tabWrapper.hovered ? (Theme ? Theme.accentPrimary : "#7C3AED") : (Theme ? Theme.textSecondary : "#444") + Layout.alignment: Qt.AlignCenter + } - // Underline for active tab - Rectangle { - width: 24 - height: 2 - radius: 1 - color: index === root.currentIndex - ? (Theme ? Theme.accentPrimary : "#7C3AED") - : "transparent" - anchors.horizontalCenter: parent.horizontalCenter + // Label + Text { + text: modelData.label + font.pixelSize: 12 + font.bold: index === root.currentIndex + color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : tabWrapper.hovered ? (Theme ? Theme.accentPrimary : "#7C3AED") : (Theme ? Theme.textSecondary : "#444") + Layout.alignment: Qt.AlignCenter + } + + // Underline for active tab + Rectangle { + width: 24 + height: 2 + radius: 1 + color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : "transparent" + Layout.alignment: Qt.AlignCenter + } } } } } -} \ No newline at end of file +} diff --git a/Helpers/IPCHandlers.qml b/Helpers/IPCHandlers.qml index d3923aa..9e72395 100644 --- a/Helpers/IPCHandlers.qml +++ b/Helpers/IPCHandlers.qml @@ -3,7 +3,6 @@ import Quickshell.Io IpcHandler { property var appLauncherPanel property var lockScreen - property var tabViewer target: "globalIPC" diff --git a/Services/WallpaperManager.qml b/Services/WallpaperManager.qml index 7f9a281..fee4931 100644 --- a/Services/WallpaperManager.qml +++ b/Services/WallpaperManager.qml @@ -15,18 +15,18 @@ Singleton { toggleRandomWallpaper(); } } - property string wallpaperDirectory: Settings.wallpaperFolder + property string wallpaperDirectory: Settings.settings.wallpaperFolder property var wallpaperList: [] - property string currentWallpaper: Settings.currentWallpaper + property string currentWallpaper: Settings.settings.currentWallpaper property bool scanning: false - property string transitionType: Settings.transitionType + property string transitionType: Settings.settings.transitionType property var randomChoices: ["fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer"] function loadWallpapers() { scanning = true; wallpaperList = []; folderModel.folder = ""; - folderModel.folder = "file://" + (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : ""); + folderModel.folder = "file://" + (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : ""); } function changeWallpaper(path) { @@ -36,14 +36,13 @@ Singleton { function setCurrentWallpaper(path, isInitial) { currentWallpaper = path; if (!isInitial) { - Settings.currentWallpaper = path; - Settings.saveSettings(); + Settings.settings.currentWallpaper = path; } - if (Settings.useSWWW) { - if (Settings.transitionType === "random") { + if (Settings.settings.useSWWW) { + if (Settings.settings.transitionType === "random") { transitionType = randomChoices[Math.floor(Math.random() * randomChoices.length)]; } else { - transitionType = Settings.transitionType; + transitionType = Settings.settings.transitionType; } changeWallpaperProcess.running = true; } @@ -60,16 +59,16 @@ Singleton { } function toggleRandomWallpaper() { - if (Settings.randomWallpaper && !randomWallpaperTimer.running) { + if (Settings.settings.randomWallpaper && !randomWallpaperTimer.running) { randomWallpaperTimer.start(); setRandomWallpaper(); - } else if (!Settings.randomWallpaper && randomWallpaperTimer.running) { + } else if (!Settings.settings.randomWallpaper && randomWallpaperTimer.running) { randomWallpaperTimer.stop(); } } function restartRandomWallpaperTimer() { - if (Settings.randomWallpaper) { + if (Settings.settings.randomWallpaper) { randomWallpaperTimer.stop(); randomWallpaperTimer.start(); setRandomWallpaper(); @@ -77,14 +76,14 @@ Singleton { } function generateTheme() { - if (Settings.useWallpaperTheme) { + if (Settings.settings.useWallpaperTheme) { generateThemeProcess.running = true; } } Timer { id: randomWallpaperTimer - interval: Settings.wallpaperInterval * 1000 + interval: Settings.settings.wallpaperInterval * 1000 running: false repeat: true onTriggered: setRandomWallpaper() @@ -100,7 +99,7 @@ Singleton { if (status === FolderListModel.Ready) { var files = []; for (var i = 0; i < count; i++) { - var fileph = (Settings.wallpaperFolder !== undefined ? Settings.wallpaperFolder : "") + "/" + get(i, "fileName"); + var fileph = (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "") + "/" + get(i, "fileName"); files.push(fileph); } wallpaperList = files; @@ -111,7 +110,7 @@ Singleton { Process { id: changeWallpaperProcess - command: ["swww", "img", "--resize", Settings.wallpaperResize, "--transition-fps", Settings.transitionFps.toString(), "--transition-type", transitionType, "--transition-duration", Settings.transitionDuration.toString(), currentWallpaper] + command: ["swww", "img", "--resize", Settings.settings.wallpaperResize, "--transition-fps", Settings.settings.transitionFps.toString(), "--transition-type", transitionType, "--transition-duration", Settings.settings.transitionDuration.toString(), currentWallpaper] running: false } diff --git a/Services/WorkspaceManager.qml b/Services/WorkspaceManager.qml index 80f6ab2..f2f7bcb 100644 --- a/Services/WorkspaceManager.qml +++ b/Services/WorkspaceManager.qml @@ -89,7 +89,7 @@ Singleton { id: i, idx: ws.id, name: ws.name || "", - output: ws.monitor.name || "", + output: ws.monitor?.name || "", isActive: ws.active === true, isFocused: ws.focused === true, isUrgent: ws.urgent === true diff --git a/Settings/Settings.qml b/Settings/Settings.qml index 5596970..e9ffec0 100644 --- a/Settings/Settings.qml +++ b/Settings/Settings.qml @@ -1,98 +1,66 @@ pragma Singleton import QtQuick -import QtCore import Quickshell +import Quickshell.Io import qs.Services -QtObject { +Singleton { - Component.onCompleted: { - Qt.application.name = "quickshell" - Qt.application.organization = "quickshell" - Qt.application.domain = "quickshell.app" - loadSettings() - } - property string weatherCity: "Dinslaken" - property string profileImage: "/home/" + Quickshell.env("USER") + "/.face" - property bool useFahrenheit: false - property string wallpaperFolder: "/usr/share/wallpapers" - property string currentWallpaper: "" - property string videoPath: "~/Videos/" - property bool showActiveWindowIcon: false - property bool useSWWW: false - property bool randomWallpaper: false - property bool useWallpaperTheme: false - property bool showSystemInfoInBar: true - property bool showMediaInBar: false - property int wallpaperInterval: 300 - property string wallpaperResize: "crop" - property int transitionFps: 60 - property string transitionType: "random" - property real transitionDuration: 1.1 - property string visualizerType: "radial" // Options: "fire", "diamond", "radial" + property string shellName: "Noctalia" + property string settingsDir: Quickshell.env("HOME") + "/.config/" + shellName + "/" + property string settingsFile: settingsDir + "Settings.json" + property var settings: settingAdapter - // Settings persistence - property var settings: Settings { - category: "quickshell" + Item { + Component.onCompleted: { + // ensure settings dir + Quickshell.execDetached(["mkdir", "-p", settingsDir]); + } } - function loadSettings() { - weatherCity = settings.value("weatherCity", weatherCity) - profileImage = settings.value("profileImage", profileImage) - let tempUnit = settings.value("weatherTempUnit", "celsius") - useFahrenheit = (tempUnit === "fahrenheit") - wallpaperFolder = settings.value("wallpaperFolder", wallpaperFolder) - currentWallpaper = settings.value("currentWallpaper", currentWallpaper) - videoPath = settings.value("videoPath", videoPath) - let showActiveWindowIconFlag = settings.value("showActiveWindowIconFlag", "false") - showActiveWindowIcon = showActiveWindowIconFlag === "true" - let showSystemInfoInBarFlag = settings.value("showSystemInfoInBarFlag", "true") - showSystemInfoInBar = showSystemInfoInBarFlag === "true" - let showMediaInBarFlag = settings.value("showMediaInBarFlag", "true") - showMediaInBar = showMediaInBarFlag === "true" - let useSWWWFlag = settings.value("useSWWWFlag", "false") - useSWWW = useSWWWFlag === "true" - let randomWallpaperFlag = settings.value("randomWallpaperFlag", "false") - randomWallpaper = randomWallpaperFlag === "true" - let useWallpaperThemeFlag = settings.value("useWallpaperThemeFlag", "false") - useWallpaperTheme = useWallpaperThemeFlag === "true" - wallpaperInterval = settings.value("wallpaperInterval", wallpaperInterval) - wallpaperResize = settings.value("wallpaperResize", wallpaperResize) - transitionFps = settings.value("transitionFps", transitionFps) - transitionType = settings.value("transitionType", transitionType) - transitionDuration = settings.value("transitionDuration", transitionDuration) - visualizerType = settings.value("visualizerType", visualizerType) - - WallpaperManager.setCurrentWallpaper(currentWallpaper, true); + FileView { + id: settingFileView + path: settingsFile + watchChanges: true + onFileChanged: reload() + onAdapterUpdated: writeAdapter() + Component.onCompleted: function() { + reload() + } + onLoaded: function() { + WallpaperManager.setCurrentWallpaper(settings.currentWallpaper, true); + } + onLoadFailed: function(error) { + settingAdapter = {} + writeAdapter() + } + JsonAdapter { + id: settingAdapter + property string weatherCity: "Dinslaken" + property string profileImage: Quickshell.env("HOME") + "/.face" + property bool useFahrenheit: false + property string wallpaperFolder: "/usr/share/wallpapers" + property string currentWallpaper: "" + property string videoPath: "~/Videos/" + property bool showActiveWindowIcon: false + property bool showSystemInfoInBar: false + property bool showMediaInBar: false + property bool useSWWW: false + property bool randomWallpaper: false + property bool useWallpaperTheme: false + property int wallpaperInterval: 300 + property string wallpaperResize: "crop" + property int transitionFps: 60 + property string transitionType: "random" + property real transitionDuration: 1.1 + property string visualizerType: "radial" + } } - function saveSettings() { - settings.setValue("weatherCity", weatherCity) - settings.setValue("profileImage", profileImage) - settings.setValue("weatherTempUnit", useFahrenheit ? "fahrenheit" : "celsius") - settings.setValue("wallpaperFolder", wallpaperFolder) - settings.setValue("currentWallpaper", currentWallpaper) - settings.setValue("videoPath", videoPath) - settings.setValue("showActiveWindowIconFlag", showActiveWindowIcon ? "true" : "false") - settings.setValue("showSystemInfoInBarFlag", showSystemInfoInBar ? "true" : "false") - settings.setValue("showMediaInBarFlag", showMediaInBar ? "true" : "false") - settings.setValue("useSWWWFlag", useSWWW ? "true" : "false") - settings.setValue("randomWallpaperFlag", randomWallpaper ? "true" : "false") - settings.setValue("useWallpaperThemeFlag", useWallpaperTheme ? "true" : "false") - settings.setValue("wallpaperInterval", wallpaperInterval) - settings.setValue("wallpaperResize", wallpaperResize) - settings.setValue("transitionFps", transitionFps) - settings.setValue("transitionType", transitionType) - settings.setValue("transitionDuration", transitionDuration) - settings.setValue("visualizerType", visualizerType) - settings.sync() + Connections { + target: settingAdapter + function onRandomWallpaperChanged() { WallpaperManager.toggleRandomWallpaper() } + function onWallpaperIntervalChanged() { WallpaperManager.restartRandomWallpaperTimer() } + function onWallpaperFolderChanged() { WallpaperManager.loadWallpapers() } } - - // Property change handlers to auto-save (all commented out for explicit save only) - // onWeatherCityChanged: saveSettings() - // onProfileImageChanged: saveSettings() - // onUseFahrenheitChanged: saveSettings() - onRandomWallpaperChanged: WallpaperManager.toggleRandomWallpaper() - onWallpaperIntervalChanged: WallpaperManager.restartRandomWallpaperTimer() - onWallpaperFolderChanged: WallpaperManager.loadWallpapers() -} +} \ No newline at end of file diff --git a/Settings/Theme.qml b/Settings/Theme.qml index ac3b6a5..f825cef 100644 --- a/Settings/Theme.qml +++ b/Settings/Theme.qml @@ -3,6 +3,7 @@ pragma Singleton import QtQuick import Quickshell import Quickshell.Io +import qs.Settings Singleton { id: root @@ -14,11 +15,16 @@ Singleton { // FileView to load theme data from JSON file FileView { id: themeFile - path: Quickshell.configDir + "/Settings/Theme.json" + path: Settings.settingsDir + "Theme.json" watchChanges: true onFileChanged: reload() onAdapterUpdated: writeAdapter() - + onLoadFailed: function(error) { + if (error.includes("No such file")) { + themeData = {} + writeAdapter() + } + } JsonAdapter { id: themeData diff --git a/Templates/wallust.toml b/Templates/wallust.toml index f68156a..e50c5bb 100644 --- a/Templates/wallust.toml +++ b/Templates/wallust.toml @@ -44,4 +44,4 @@ check_contrast = true # target: ABSOLUTE path in which to place a file with generated templated values. # ¡ If either one is a directory, then both SHOULD be one. ! # zathura = { template = 'zathura', target = '~/.config/zathura/zathurarc' } -Quickshell = { template = 'quickshell.json', target = 'Settings/Theme.json' } \ No newline at end of file +Quickshell = { template = 'quickshell.json', target = '~/.config/Noctalia/Theme.json' } \ No newline at end of file diff --git a/Widgets/Background.qml b/Widgets/Background.qml index 5c40efd..ee042d0 100644 --- a/Widgets/Background.qml +++ b/Widgets/Background.qml @@ -5,7 +5,7 @@ import qs.Services import qs.Settings ShellRoot { - property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.useSWWW ? WallpaperManager.currentWallpaper : "" + property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.settings.useSWWW ? WallpaperManager.currentWallpaper : "" PanelWindow { visible: wallpaperSource !== "" anchors { diff --git a/Widgets/LockScreen.qml b/Widgets/LockScreen.qml index 78ab2ef..d95a17c 100644 --- a/Widgets/LockScreen.qml +++ b/Widgets/LockScreen.qml @@ -18,7 +18,7 @@ WlSessionLock { property bool authenticating: false property string password: "" property bool pamAvailable: typeof PamContext !== "undefined" - property string weatherCity: Settings.weatherCity + property string weatherCity: Settings.settings.weatherCity property var weatherData: null property string weatherError: "" property string weatherInfo: "" @@ -161,7 +161,7 @@ WlSessionLock { id: avatarImage anchors.fill: parent anchors.margins: 4 - source: Settings.profileImage + source: Settings.settings.profileImage fillMode: Image.PreserveAspectCrop visible: false // Only show the masked version asynchronous: true @@ -175,7 +175,7 @@ WlSessionLock { radius: avatarImage.width / 2 visible: false } - visible: Settings.profileImage !== "" + visible: Settings.settings.profileImage !== "" } // Fallback icon Text { @@ -184,7 +184,7 @@ WlSessionLock { font.family: "Material Symbols Outlined" font.pixelSize: 32 color: Theme.onAccent - visible: Settings.profileImage === "" + visible: Settings.settings.profileImage === "" } // Glow effect layer.enabled: true @@ -357,7 +357,7 @@ WlSessionLock { verticalAlignment: Text.AlignVCenter } Text { - text: weatherData && weatherData.current_weather ? (Settings.useFahrenheit ? `${Math.round(weatherData.current_weather.temperature * 9 / 5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : (Settings.useFahrenheit ? "--°F" : "--°C") + text: weatherData && weatherData.current_weather ? (Settings.settings.useFahrenheit ? `${Math.round(weatherData.current_weather.temperature * 9 / 5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : (Settings.settings.useFahrenheit ? "--°F" : "--°C") font.family: Theme.fontFamily font.pixelSize: 18 color: Theme.textSecondary diff --git a/Widgets/Notification/NotificationHistory.qml b/Widgets/Notification/NotificationHistory.qml index 11758c5..ffdca25 100644 --- a/Widgets/Notification/NotificationHistory.qml +++ b/Widgets/Notification/NotificationHistory.qml @@ -1,56 +1,30 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 +import QtQuick import Quickshell import Quickshell.Io -import qs.Components import qs.Settings -import QtQuick.Layouts 1.15 +import QtQuick.Layouts +import qs.Components -Item { - id: root - property string configDir: Quickshell.configDir - property string historyFilePath: configDir + "/notification_history.json" - property bool hasUnread: notificationHistoryWin.hasUnread && !notificationHistoryWin.visible - function addToHistory(notification) { notificationHistoryWin.addToHistory(notification) } - width: 22; height: 22 - - // Bell icon/button - Item { - id: bell - width: 22; height: 22 - Text { - anchors.centerIn: parent - text: root.hasUnread ? "notifications_unread" : "notifications" - font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 - font.weight: root.hasUnread ? Font.Bold : Font.Normal - color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (root.hasUnread ? Theme.accentPrimary : Theme.textDisabled) - } - MouseArea { - id: mouseAreaBell - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: notificationHistoryWin.visible = !notificationHistoryWin.visible - } - } - - // The popup window - PanelWindow { - id: notificationHistoryWin - width: 400 - property int maxPopupHeight: 500 +// The popup window +PanelWithOverlay { + id: notificationHistoryWin + property string historyFilePath: Settings.settingsDir + "notification_history.json" + property bool hasUnread: notificationHistoryWinRect.hasUnread && !notificationHistoryWinRect.visible + function addToHistory(notification) { notificationHistoryWinRect.addToHistory(notification) } + Rectangle { + id: notificationHistoryWinRect + implicitWidth: 400 + property int maxPopupHeight: 800 property int minPopupHeight: 230 property int contentHeight: headerRow.height + historyList.contentHeight + 56 - height: Math.max(Math.min(contentHeight, maxPopupHeight), minPopupHeight) - color: "transparent" - visible: false - screen: Quickshell.primaryScreen - focusable: true - anchors.top: true - anchors.right: true - margins.top: 4 - margins.right: 4 + implicitHeight: Math.max(Math.min(contentHeight, maxPopupHeight), minPopupHeight) + visible: parent.visible + anchors.top: parent.top + anchors.right: parent.right + anchors.topMargin: 4 + anchors.rightMargin: 4 + color: Theme.backgroundPrimary + radius: 20 property int maxHistory: 100 property bool hasUnread: false @@ -62,25 +36,24 @@ Item { FileView { id: historyFileView - path: root.historyFilePath + path: notificationHistoryWin.historyFilePath blockLoading: true printErrors: true watchChanges: true - + JsonAdapter { id: historyAdapter property var notifications: [] } onFileChanged: historyFileView.reload() - onLoaded: notificationHistoryWin.loadHistory() - onLoadFailed: function(error) { - if (error.includes("No such file")) { - historyAdapter.notifications = [] - writeAdapter() - } + onLoaded: notificationHistoryWinRect.loadHistory() + onLoadFailed: function (error) { + historyAdapter.notifications = []; + historyFileView.writeAdapter(); } - Component.onCompleted: if (path) reload() + Component.onCompleted: if (path) + reload() } function updateHasUnread() { @@ -99,16 +72,18 @@ Item { function loadHistory() { if (historyAdapter.notifications) { - historyModel.clear() - const notifications = historyAdapter.notifications - const count = Math.min(notifications.length, maxHistory) + historyModel.clear(); + const notifications = historyAdapter.notifications; + const count = Math.min(notifications.length, maxHistory); for (let i = 0; i < count; i++) { - let n = notifications[i] + let n = notifications[i]; if (typeof n === 'object' && n !== null) { - if (n.read === undefined) n.read = false; + if (n.read === undefined) + n.read = false; // Mark as read if window is open - if (visible) n.read = true; - historyModel.append(n) + if (notificationHistoryWinRect.visible) + n.read = true; + historyModel.append(n); } } updateHasUnread(); @@ -116,10 +91,10 @@ Item { } function saveHistory() { - const historyArray = [] - const count = Math.min(historyModel.count, maxHistory) + const historyArray = []; + const count = Math.min(historyModel.count, maxHistory); for (let i = 0; i < count; ++i) { - let obj = historyModel.get(i) + let obj = historyModel.get(i); if (typeof obj === 'object' && obj !== null) { historyArray.push({ id: obj.id, @@ -128,51 +103,55 @@ Item { body: obj.body, timestamp: obj.timestamp, read: obj.read === undefined ? false : obj.read - }) + }); } } - historyAdapter.notifications = historyArray - Qt.callLater(function() { - historyFileView.writeAdapter() - }) + historyAdapter.notifications = historyArray; + Qt.callLater(function () { + historyFileView.writeAdapter(); + }); updateHasUnread(); } function addToHistory(notification) { - if (!notification.id) notification.id = Date.now() - if (!notification.timestamp) notification.timestamp = new Date().toISOString() - + if (!notification.id) + notification.id = Date.now(); + if (!notification.timestamp) + notification.timestamp = new Date().toISOString(); + // Mark as read if window is open - notification.read = visible - + notification.read = visible; + // Remove duplicate by id for (let i = 0; i < historyModel.count; ++i) { if (historyModel.get(i).id === notification.id) { - historyModel.remove(i) - break + historyModel.remove(i); + break; } } - - historyModel.insert(0, notification) - - if (historyModel.count > maxHistory) historyModel.remove(maxHistory) - saveHistory() + + historyModel.insert(0, notification); + + if (historyModel.count > maxHistory) + historyModel.remove(maxHistory); + saveHistory(); } function clearHistory() { - historyModel.clear() - historyAdapter.notifications = [] - historyFileView.writeAdapter() + historyModel.clear(); + historyAdapter.notifications = []; + historyFileView.writeAdapter(); } function formatTimestamp(ts) { - if (!ts) return ""; + if (!ts) + return ""; var date = typeof ts === "number" ? new Date(ts) : new Date(Date.parse(ts)); var y = date.getFullYear(); - var m = (date.getMonth()+1).toString().padStart(2,'0'); - var d = date.getDate().toString().padStart(2,'0'); - var h = date.getHours().toString().padStart(2,'0'); - var min = date.getMinutes().toString().padStart(2,'0'); + var m = (date.getMonth() + 1).toString().padStart(2, '0'); + var d = date.getDate().toString().padStart(2, '0'); + var h = date.getHours().toString().padStart(2, '0'); + var min = date.getMinutes().toString().padStart(2, '0'); return `${y}-${m}-${d} ${h}:${min}`; } @@ -186,13 +165,14 @@ Item { changed = true; } } - if (changed) saveHistory(); + if (changed) + saveHistory(); } } Rectangle { - width: notificationHistoryWin.width - height: notificationHistoryWin.height + width: notificationHistoryWinRect.width + height: notificationHistoryWinRect.height anchors.fill: parent color: Theme.backgroundPrimary radius: 20 @@ -205,10 +185,12 @@ Item { RowLayout { id: headerRow spacing: 4 - anchors.top: parent.top anchors.topMargin: 4 anchors.left: parent.left anchors.right: parent.right + Layout.fillHeight: true + Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: 52 anchors.leftMargin: 16 anchors.rightMargin: 16 Text { @@ -218,7 +200,9 @@ Item { color: Theme.textPrimary Layout.alignment: Qt.AlignVCenter } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } Rectangle { id: clearAllButton width: 90 @@ -251,7 +235,7 @@ Item { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: notificationHistoryWin.clearHistory() + onClicked: notificationHistoryWinRect.clearHistory() } } } @@ -264,11 +248,10 @@ Item { } Rectangle { - anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right anchors.topMargin: 56 - anchors.bottom: parent.bottom + height: notificationHistoryWinRect.height - 56 - 12 color: Theme.surfaceVariant radius: 20 @@ -288,7 +271,10 @@ Item { Column { anchors.fill: parent spacing: 0 - Item { id: topSpacer; height: (parent.height - historyList.height) / 2 } + Item { + id: topSpacer + height: (parent.height - historyList.height) / 2 + } ListView { id: historyList width: parent.width @@ -315,7 +301,7 @@ Item { anchors.margins: 14 spacing: 6 RowLayout { - id: headerRow + id: headerRow2 spacing: 8 Rectangle { id: iconBackground @@ -349,14 +335,16 @@ Item { } Text { visible: !model.isPlaceholder - text: model.timestamp ? notificationHistoryWin.formatTimestamp(model.timestamp) : "" + text: model.timestamp ? notificationHistoryWinRect.formatTimestamp(model.timestamp) : "" color: Theme.textDisabled font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeCaption verticalAlignment: Text.AlignVCenter } } - Item { Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } } Text { text: model.summary || (model.isPlaceholder ? "You're all caught up!" : "") @@ -382,7 +370,11 @@ Item { } } - Rectangle { width: 1; height: 24; color: "transparent" } + Rectangle { + width: 1 + height: 24 + color: "transparent" + } ListModel { id: placeholderModel @@ -396,4 +388,4 @@ Item { } } } -} \ No newline at end of file +} diff --git a/Widgets/Notification/NotificationIcon.qml b/Widgets/Notification/NotificationIcon.qml new file mode 100644 index 0000000..b6f08b8 --- /dev/null +++ b/Widgets/Notification/NotificationIcon.qml @@ -0,0 +1,30 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.Settings + +Item { + id: root + width: 22; height: 22 + + // Bell icon/button + Item { + id: bell + width: 22; height: 22 + Text { + anchors.centerIn: parent + text: notificationHistoryWin.hasUnread ? "notifications_unread" : "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) + } + MouseArea { + id: mouseAreaBell + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: notificationHistoryWin.visible = !notificationHistoryWin.visible + } + } +} \ No newline at end of file diff --git a/Widgets/Notification/NotificationPopup.qml b/Widgets/Notification/NotificationPopup.qml index 41641f4..b80018f 100644 --- a/Widgets/Notification/NotificationPopup.qml +++ b/Widgets/Notification/NotificationPopup.qml @@ -111,11 +111,7 @@ PanelWindow { border.width: 1.5 // Get all possible icon sources from notification - property var iconSources: [ - rawNotification?.image || "", - rawNotification?.appIcon || "", - rawNotification?.icon || "" - ] + property var iconSources: [rawNotification?.image || "", rawNotification?.appIcon || "", rawNotification?.icon || ""] // Try to load notification icon Image { @@ -131,7 +127,8 @@ PanelWindow { source: { for (var i = 0; i < iconBackground.iconSources.length; i++) { var icon = iconBackground.iconSources[i]; - if (!icon) continue; + if (!icon) + continue; if (icon.includes("?path=")) { const [name, path] = icon.split("?path="); @@ -202,7 +199,8 @@ PanelWindow { repeat: false onTriggered: { dismissAnimation.start(); - if (rawNotification) rawNotification.expire(); + if (rawNotification) + rawNotification.expire(); } } @@ -210,28 +208,63 @@ PanelWindow { anchors.fill: parent onClicked: { dismissAnimation.start(); - if (rawNotification) rawNotification.dismiss(); + if (rawNotification) + rawNotification.dismiss(); } } ParallelAnimation { id: dismissAnimation - NumberAnimation { target: notificationDelegate; property: "opacity"; to: 0; duration: 150 } - NumberAnimation { target: notificationDelegate; property: "height"; to: 0; duration: 150 } - NumberAnimation { target: notificationDelegate; property: "x"; to: width; duration: 150; easing.type: Easing.InQuad } + NumberAnimation { + target: notificationDelegate + property: "opacity" + to: 0 + duration: 150 + } + NumberAnimation { + target: notificationDelegate + property: "height" + to: 0 + duration: 150 + } + NumberAnimation { + target: notificationDelegate + property: "x" + to: width + duration: 150 + easing.type: Easing.InQuad + } onFinished: { - var idx = notificationRepeater.indexOf(notificationDelegate); - if (idx !== -1) { - notificationModel.remove(idx); + for (let i = 0; i < notificationModel.count; i++) { + if (notificationModel.get(i).id === notificationDelegate.id) { + notificationModel.remove(i); + break; + } } } } ParallelAnimation { id: appearAnimation - NumberAnimation { target: notificationDelegate; property: "opacity"; to: 1; duration: 150 } - NumberAnimation { target: notificationDelegate; property: "height"; to: contentRow.height + 20; duration: 150 } - NumberAnimation { target: notificationDelegate; property: "x"; to: 0; duration: 150; easing.type: Easing.OutQuad } + NumberAnimation { + target: notificationDelegate + property: "opacity" + to: 1 + duration: 150 + } + NumberAnimation { + target: notificationDelegate + property: "height" + to: contentRow.height + 20 + duration: 150 + } + NumberAnimation { + target: notificationDelegate + property: "x" + to: 0 + duration: 150 + easing.type: Easing.OutQuad + } } Component.onCompleted: { @@ -240,18 +273,21 @@ PanelWindow { height = 0; x = width; appearAnimation.start(); - var idx = notificationRepeater.indexOf(notificationDelegate); - if (idx !== -1) { - var oldItem = notificationModel.get(idx); - notificationModel.set(idx, { - id: oldItem.id, - appName: oldItem.appName, - summary: oldItem.summary, - body: oldItem.body, - rawNotification: oldItem.rawNotification, - appeared: true, - dismissed: oldItem.dismissed - }); + for (let i = 0; i < notificationModel.count; i++) { + if (notificationModel.get(i).id === notificationDelegate.id) { + var oldItem = notificationModel.get(i); + notificationModel.set(i, { + id: oldItem.id, + appName: oldItem.appName, + summary: oldItem.summary, + body: oldItem.body, + rawNotification: oldItem.rawNotification, + appeared: true, + read: oldItem.read, + dismissed: oldItem.dismissed + }); + break; + } } } } @@ -263,7 +299,7 @@ PanelWindow { target: Quickshell function onScreensChanged() { if (window.screen) { - x = window.screen.width - width - 20 + x = window.screen.width - width - 20; } } } diff --git a/Widgets/Overview.qml b/Widgets/Overview.qml index ff136ba..2404854 100644 --- a/Widgets/Overview.qml +++ b/Widgets/Overview.qml @@ -6,7 +6,7 @@ import qs.Services import qs.Settings ShellRoot { - property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.useSWWW ? WallpaperManager.currentWallpaper : "" + property string wallpaperSource: WallpaperManager.currentWallpaper !== "" && !Settings.settings.useSWWW ? WallpaperManager.currentWallpaper : "" PanelWindow { visible: wallpaperSource !== "" anchors { diff --git a/Widgets/Sidebar/Button.qml b/Widgets/Sidebar/Button.qml index f9d3ee7..c73cb53 100644 --- a/Widgets/Sidebar/Button.qml +++ b/Widgets/Sidebar/Button.qml @@ -22,19 +22,6 @@ Item { hoverEnabled: true onClicked: { if (sidebarPopup.visible) { - // Close all modals if open - if (sidebarPopup.settingsModal && sidebarPopup.settingsModal.visible) { - sidebarPopup.settingsModal.visible = false; - } - if (sidebarPopup.wallpaperPanelModal && sidebarPopup.wallpaperPanelModal.visible) { - sidebarPopup.wallpaperPanelModal.visible = false; - } - if (sidebarPopup.wifiPanelModal && sidebarPopup.wifiPanelModal.visible) { - sidebarPopup.wifiPanelModal.visible = false; - } - if (sidebarPopup.bluetoothPanelModal && sidebarPopup.bluetoothPanelModal.visible) { - sidebarPopup.bluetoothPanelModal.visible = false; - } sidebarPopup.hidePopup(); } else { sidebarPopup.showAt(); diff --git a/Widgets/Sidebar/Config/CollapsibleCategory.qml b/Widgets/Sidebar/Config/CollapsibleCategory.qml index 97d3c3e..d0c2eb7 100644 --- a/Widgets/Sidebar/Config/CollapsibleCategory.qml +++ b/Widgets/Sidebar/Config/CollapsibleCategory.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import qs.Settings ColumnLayout { diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index 98153fe..111f607 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import Qt5Compat.GraphicalEffects import qs.Settings @@ -9,19 +9,10 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 340 color: Theme.surface - Layout.topMargin: 12 radius: 18 border.color: "transparent" border.width: 0 Layout.bottomMargin: 16 - property bool showActiveWindowIcon: false - signal showAWIconChanged(bool showActiveWindowIcon) - property bool showSystemInfoInBar: true - signal showSystemInfoChanged(bool showSystemInfoInBar) - property bool showMediaInBar: false - signal showMediaChanged(bool showMediaInBar) - property string visualizerType: Settings.visualizerType - signal visualizerTypeUpdated(string type) ColumnLayout { anchors.fill: parent @@ -66,7 +57,7 @@ Rectangle { id: avatarImage anchors.fill: parent anchors.margins: 2 - source: Settings.profileImage + source: Settings.settings.profileImage fillMode: Image.PreserveAspectCrop visible: false asynchronous: true @@ -83,7 +74,7 @@ Rectangle { radius: avatarImage.width / 2 visible: false } - visible: Settings.profileImage !== "" + visible: Settings.settings.profileImage !== "" } // Fallback icon @@ -93,7 +84,7 @@ Rectangle { font.family: "Material Symbols Outlined" font.pixelSize: 18 color: Theme.accentPrimary - visible: Settings.profileImage === "" + visible: Settings.settings.profileImage === "" } } @@ -110,7 +101,7 @@ Rectangle { id: profileImageInput anchors.fill: parent anchors.margins: 12 - text: Settings.profileImage + text: Settings.settings.profileImage font.pixelSize: 13 color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter @@ -120,7 +111,7 @@ Rectangle { activeFocusOnTab: true inputMethodHints: Qt.ImhNone onTextChanged: { - Settings.profileImage = text + Settings.settings.profileImage = text } MouseArea { anchors.fill: parent @@ -154,8 +145,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant - border.color: showActiveWindowIcon ? Theme.accentPrimary : Theme.outline + color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { @@ -167,7 +158,7 @@ Rectangle { border.color: Theme.outline border.width: 1 y: 2 - x: showActiveWindowIcon ? customSwitch.width - width - 2 : 2 + x: Settings.settings.showActiveWindowIcon ? customSwitch.width - width - 2 : 2 Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } @@ -177,7 +168,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - showAWIconChanged(!showActiveWindowIcon) + Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon } } } @@ -206,8 +197,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: showSystemInfoInBar ? Theme.accentPrimary : Theme.outline + color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { @@ -219,7 +210,7 @@ Rectangle { border.color: Theme.outline border.width: 1 y: 2 - x: showSystemInfoInBar ? customSwitch2.width - width - 2 : 2 + x: Settings.settings.showSystemInfoInBar ? customSwitch2.width - width - 2 : 2 Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } @@ -229,7 +220,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - showSystemInfoChanged(!showSystemInfoInBar) + Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar } } } @@ -258,8 +249,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: showMediaInBar ? Theme.accentPrimary : Theme.outline + color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { @@ -271,7 +262,7 @@ Rectangle { border.color: Theme.outline border.width: 1 y: 2 - x: showMediaInBar ? customSwitch3.width - width - 2 : 2 + x: Settings.settings.showMediaInBar ? customSwitch3.width - width - 2 : 2 Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } @@ -281,7 +272,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - showMediaChanged(!showMediaInBar) + Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar } } } @@ -318,9 +309,9 @@ Rectangle { anchors.left: parent.left anchors.leftMargin: 12 anchors.verticalCenter: parent.verticalCenter - text: visualizerType === "fire" ? "Fire" : - visualizerType === "diamond" ? "Diamond" : - visualizerType === "radial" ? "Radial" : "Radial" + text: Settings.settings.visualizerType === "fire" ? "Fire" : + Settings.settings.visualizerType === "diamond" ? "Diamond" : + Settings.settings.visualizerType === "radial" ? "Radial" : "Radial" font.pixelSize: 13 color: Theme.textPrimary } @@ -350,19 +341,19 @@ Rectangle { MenuItem { text: "Fire" onTriggered: { - visualizerTypeUpdated("fire") + Settings.settings.visualizerType = "fire" } } MenuItem { text: "Diamond" onTriggered: { - visualizerTypeUpdated("diamond") + Settings.settings.visualizerType = "diamond" } } MenuItem { text: "Radial" onTriggered: { - visualizerTypeUpdated("radial") + Settings.settings.visualizerType = "radial" } } } @@ -394,7 +385,7 @@ Rectangle { id: videoPathInput anchors.fill: parent anchors.margins: 12 - text: Settings.videoPath !== undefined ? Settings.videoPath : "" + text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" font.pixelSize: 13 color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter @@ -403,7 +394,7 @@ Rectangle { activeFocusOnTab: true inputMethodHints: Qt.ImhUrlCharactersOnly onTextChanged: { - Settings.videoPath = text + Settings.settings.videoPath = text } MouseArea { anchors.fill: parent diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index fe738f4..3a9dabd 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import Quickshell import Quickshell.Wayland import qs.Settings @@ -22,24 +22,6 @@ PanelWindow { //border.width: 1 WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None - // Local properties for editing (not saved until apply) - property string tempWeatherCity: (Settings.weatherCity !== undefined && Settings.weatherCity !== null) ? Settings.weatherCity : "" - property bool tempUseFahrenheit: Settings.useFahrenheit - property string tempProfileImage: (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : "" - property string tempWallpaperFolder: (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : "" - property bool tempShowActiveWindowIcon: Settings.showActiveWindowIcon - property bool tempUseSWWW: Settings.useSWWW - property bool tempRandomWallpaper: Settings.randomWallpaper - property bool tempUseWallpaperTheme: Settings.useWallpaperTheme - property int tempWallpaperInterval: Settings.wallpaperInterval - property string tempWallpaperResize: Settings.wallpaperResize - property int tempTransitionFps: Settings.transitionFps - property string tempTransitionType: Settings.transitionType - property real tempTransitionDuration: Settings.transitionDuration - property bool tempShowSystemInfoInBar: Settings.showSystemInfoInBar - property bool tempShowMediaInBar: Settings.showMediaInBar - property string tempVisualizerType: Settings.visualizerType - Rectangle { anchors.fill: parent color: Theme.backgroundPrimary @@ -145,14 +127,6 @@ PanelWindow { Layout.fillWidth: true Layout.alignment: Qt.AlignTop anchors.margins: 16 - weatherCity: (typeof tempWeatherCity !== 'undefined' && tempWeatherCity !== null) ? tempWeatherCity : "" - useFahrenheit: tempUseFahrenheit - onCityChanged: function (city) { - tempWeatherCity = city; - } - onTemperatureUnitChanged: function (useFahrenheit) { - tempUseFahrenheit = useFahrenheit; - } } } } @@ -164,22 +138,6 @@ PanelWindow { Layout.fillWidth: true Layout.alignment: Qt.AlignTop anchors.margins: 16 - showActiveWindowIcon: tempShowActiveWindowIcon - onShowAWIconChanged: function (showActiveWindowIcon) { - tempShowActiveWindowIcon = showActiveWindowIcon; - } - showSystemInfoInBar: tempShowSystemInfoInBar - onShowSystemInfoChanged: function (showSystemInfoInBar) { - tempShowSystemInfoInBar = showSystemInfoInBar; - } - showMediaInBar: tempShowMediaInBar - onShowMediaChanged: function (showMediaInBar) { - tempShowMediaInBar = showMediaInBar; - } - visualizerType: tempVisualizerType - onVisualizerTypeUpdated: function (type) { - tempVisualizerType = type; - } } } } @@ -192,118 +150,15 @@ PanelWindow { Layout.fillWidth: true Layout.alignment: Qt.AlignTop anchors.margins: 16 - wallpaperFolder: (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : "" - useSWWW: tempUseSWWW - randomWallpaper: tempRandomWallpaper - useWallpaperTheme: tempUseWallpaperTheme - wallpaperInterval: tempWallpaperInterval - wallpaperResize: tempWallpaperResize - transitionFps: tempTransitionFps - transitionType: tempTransitionType - transitionDuration: tempTransitionDuration - onWallpaperFolderEdited: function (folder) { - tempWallpaperFolder = folder; - } - onUseSWWWChangedUpdated: function(useSWWW) { - tempUseSWWW = useSWWW; - } - onRandomWallpaperChangedUpdated: function(randomWallpaper) { - tempRandomWallpaper = randomWallpaper; - } - onUseWallpaperThemeChangedUpdated: function(useWallpaperTheme) { - tempUseWallpaperTheme = useWallpaperTheme; - } - onWallpaperIntervalChangedUpdated: function(wallpaperInterval) { - tempWallpaperInterval = wallpaperInterval; - } - onWallpaperResizeChangedUpdated: function(resize) { - tempWallpaperResize = resize; - } - onTransitionFpsChangedUpdated: function(fps) { - tempTransitionFps = fps; - } - onTransitionTypeChangedUpdated: function(type) { - tempTransitionType = type; - } - onTransitionDurationChangedUpdated: function(duration) { - tempTransitionDuration = duration; - } } } } } - - // Apply Button - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 52 - radius: 16 - color: applyButtonArea.containsMouse ? Theme.accentPrimary : Theme.accentPrimary - border.color: "transparent" - border.width: 0 - opacity: 1.0 - Text { - anchors.centerIn: parent - text: "Apply Changes" - font.pixelSize: 17 - font.bold: true - color: applyButtonArea.containsMouse ? Theme.onAccent : Theme.onAccent - } - MouseArea { - id: applyButtonArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - Settings.weatherCity = (typeof tempWeatherCity !== 'undefined' && tempWeatherCity !== null) ? tempWeatherCity : ""; - Settings.useFahrenheit = tempUseFahrenheit; - Settings.profileImage = (typeof tempProfileImage !== 'undefined' && tempProfileImage !== null) ? tempProfileImage : ""; - Settings.wallpaperFolder = (typeof tempWallpaperFolder !== 'undefined' && tempWallpaperFolder !== null) ? tempWallpaperFolder : ""; - Settings.showActiveWindowIcon = tempShowActiveWindowIcon; - Settings.useSWWW = tempUseSWWW; - Settings.randomWallpaper = tempRandomWallpaper; - Settings.useWallpaperTheme = tempUseWallpaperTheme; - Settings.wallpaperInterval = tempWallpaperInterval; - Settings.wallpaperResize = tempWallpaperResize; - Settings.transitionFps = tempTransitionFps; - Settings.transitionType = tempTransitionType; - Settings.transitionDuration = tempTransitionDuration; - Settings.showSystemInfoInBar = tempShowSystemInfoInBar; - Settings.showMediaInBar = tempShowMediaInBar; - Settings.visualizerType = tempVisualizerType; - Settings.saveSettings(); - if (typeof weather !== 'undefined' && weather) { - weather.fetchCityWeather(); - } - settingsModal.closeSettings(); - } - } - } } } // Function to open the modal and initialize temp values - function openSettings() { - tempWeatherCity = (Settings.weatherCity !== undefined && Settings.weatherCity !== null) ? Settings.weatherCity : ""; - tempUseFahrenheit = Settings.useFahrenheit; - tempShowActiveWindowIcon = Settings.showActiveWindowIcon; - tempProfileImage = (Settings.profileImage !== undefined && Settings.profileImage !== null) ? Settings.profileImage : ""; - tempWallpaperFolder = (Settings.wallpaperFolder !== undefined && Settings.wallpaperFolder !== null) ? Settings.wallpaperFolder : ""; - if (tempWallpaperFolder === undefined || tempWallpaperFolder === null) - tempWallpaperFolder = ""; - - // Initialize wallpaper settings - tempUseSWWW = Settings.useSWWW; - tempRandomWallpaper = Settings.randomWallpaper; - tempUseWallpaperTheme = Settings.useWallpaperTheme; - tempWallpaperInterval = Settings.wallpaperInterval; - tempWallpaperResize = Settings.wallpaperResize; - tempTransitionFps = Settings.transitionFps; - tempTransitionType = Settings.transitionType; - tempTransitionDuration = Settings.transitionDuration; - tempShowSystemInfoInBar = Settings.showSystemInfoInBar; - tempShowMediaInBar = Settings.showMediaInBar; - tempVisualizerType = Settings.visualizerType; - + function openSettings() { visible = true; // Force focus on the text input after a short delay focusTimer.start(); diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml index 71c8e75..96aaa55 100644 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ b/Widgets/Sidebar/Config/WallpaperSettings.qml @@ -1,35 +1,15 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import qs.Settings Rectangle { id: wallpaperSettingsCard Layout.fillWidth: true Layout.preferredHeight: 680 - Layout.topMargin: 12 color: Theme.surface radius: 18 - // Property for binding - property string wallpaperFolder: "" - signal wallpaperFolderEdited(string folder) - property bool useSWWW: false - signal useSWWWChangedUpdated(bool useSWWW) - property bool randomWallpaper: false - signal randomWallpaperChangedUpdated(bool randomWallpaper) - property bool useWallpaperTheme: false - signal useWallpaperThemeChangedUpdated(bool useWallpaperTheme) - property int wallpaperInterval: 300 - signal wallpaperIntervalChangedUpdated(int wallpaperInterval) - property string wallpaperResize: "crop" - signal wallpaperResizeChangedUpdated(string resize) - property int transitionFps: 60 - signal transitionFpsChangedUpdated(int fps) - property string transitionType: "random" - signal transitionTypeChangedUpdated(string type) - property real transitionDuration: 1.1 - signal transitionDurationChangedUpdated(real duration) ColumnLayout { anchors.fill: parent @@ -74,7 +54,7 @@ Rectangle { anchors.rightMargin: 12 anchors.topMargin: 6 anchors.bottomMargin: 6 - text: wallpaperFolder + text: Settings.settings.wallpaperFolder font.family: Theme.fontFamily font.pixelSize: 13 color: Theme.textPrimary @@ -84,7 +64,7 @@ Rectangle { activeFocusOnTab: true inputMethodHints: Qt.ImhUrlCharactersOnly onTextChanged: { - wallpaperFolderEdited(text) + Settings.settings.wallpaperFolder = text } MouseArea { anchors.fill: parent @@ -116,8 +96,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: useSWWW ? Theme.accentPrimary : Theme.outline + color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { @@ -129,7 +109,7 @@ Rectangle { border.color: Theme.outline border.width: 1 y: 2 - x: useSWWW ? swwwSwitch.width - width - 2 : 2 + x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } @@ -139,7 +119,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - useSWWWChangedUpdated(!useSWWW) + Settings.settings.useSWWW = !Settings.settings.useSWWW } } } @@ -168,8 +148,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: randomWallpaper ? Theme.accentPrimary : Theme.outline + color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { @@ -181,7 +161,7 @@ Rectangle { border.color: Theme.outline border.width: 1 y: 2 - x: randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 + x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } @@ -191,7 +171,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - randomWallpaperChangedUpdated(!randomWallpaper) + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper } } } @@ -220,8 +200,8 @@ Rectangle { width: 52 height: 32 radius: 16 - color: useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: useWallpaperTheme ? Theme.accentPrimary : Theme.outline + color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { @@ -233,7 +213,7 @@ Rectangle { border.color: Theme.outline border.width: 1 y: 2 - x: useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 + x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } @@ -243,7 +223,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - useWallpaperThemeChangedUpdated(!useWallpaperTheme) + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme } } } @@ -269,7 +249,7 @@ Rectangle { } Text { - text: wallpaperInterval + text: Settings.settings.wallpaperInterval font.pixelSize: 13 color: Theme.textPrimary } @@ -281,7 +261,7 @@ Rectangle { from: 10 to: 900 stepSize: 10 - value: wallpaperInterval + value: Settings.settings.wallpaperInterval snapMode: Slider.SnapAlways background: Rectangle { @@ -314,7 +294,7 @@ Rectangle { } onMoved: { - wallpaperIntervalChangedUpdated(Math.round(value)) + Settings.settings.wallpaperInterval = Math.round(value) } } } @@ -337,7 +317,7 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 40 model: ["no", "crop", "fit", "stretch"] - currentIndex: model.indexOf(wallpaperResize) + currentIndex: model.indexOf(Settings.settings.wallpaperResize) background: Rectangle { implicitWidth: 120 @@ -409,7 +389,7 @@ Rectangle { } onActivated: { - wallpaperResizeChangedUpdated(model[index]) + Settings.settings.wallpaperResize = model[index] } } } @@ -432,7 +412,7 @@ Rectangle { Layout.fillWidth: true Layout.preferredHeight: 40 model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] - currentIndex: model.indexOf(transitionType) + currentIndex: model.indexOf(Settings.settings.transitionType) background: Rectangle { implicitWidth: 120 @@ -504,7 +484,7 @@ Rectangle { } onActivated: { - transitionTypeChangedUpdated(model[index]) + Settings.settings.transitionType = model[index] } } } @@ -529,7 +509,7 @@ Rectangle { } Text { - text: transitionFps + text: Settings.settings.transitionFps font.pixelSize: 13 color: Theme.textPrimary } @@ -541,7 +521,7 @@ Rectangle { from: 30 to: 500 stepSize: 5 - value: transitionFps + value: Settings.settings.transitionFps snapMode: Slider.SnapAlways background: Rectangle { @@ -574,7 +554,7 @@ Rectangle { } onMoved: { - transitionFpsChangedUpdated(Math.round(value)) + Settings.settings.transitionFps = Math.round(value) } } } @@ -599,7 +579,7 @@ Rectangle { } Text { - text: transitionDuration.toFixed(3) + text: Settings.settings.transitionDuration.toFixed(3) font.pixelSize: 13 color: Theme.textPrimary } @@ -611,7 +591,7 @@ Rectangle { from: 0.250 to: 10.0 stepSize: 0.050 - value: transitionDuration + value: Settings.settings.transitionDuration snapMode: Slider.SnapAlways background: Rectangle { @@ -644,7 +624,7 @@ Rectangle { } onMoved: { - transitionDurationChangedUpdated(value) + Settings.settings.transitionDuration = value } } } diff --git a/Widgets/Sidebar/Config/WeatherSettings.qml b/Widgets/Sidebar/Config/WeatherSettings.qml index dc53574..da433fd 100644 --- a/Widgets/Sidebar/Config/WeatherSettings.qml +++ b/Widgets/Sidebar/Config/WeatherSettings.qml @@ -1,23 +1,14 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts import qs.Settings Rectangle { id: weatherSettingsCard Layout.fillWidth: true Layout.preferredHeight: 180 - Layout.topMargin: 12 color: Theme.surface radius: 18 - // Properties for binding - property string weatherCity: "" - property bool useFahrenheit: false - - signal cityChanged(string city) - signal temperatureUnitChanged(bool useFahrenheit) - ColumnLayout { anchors.fill: parent anchors.margins: 18 @@ -76,7 +67,7 @@ Rectangle { anchors.rightMargin: 12 anchors.topMargin: 6 anchors.bottomMargin: 6 - text: weatherCity + text: Settings.settings.weatherCity font.family: Theme.fontFamily font.pixelSize: 13 color: Theme.textPrimary @@ -88,7 +79,7 @@ Rectangle { inputMethodHints: Qt.ImhNone onTextChanged: { - cityChanged(text) + Settings.settings.weatherCity = text } MouseArea { @@ -137,11 +128,11 @@ Rectangle { border.color: Theme.outline border.width: 1 y: 2 - x: useFahrenheit ? customSwitch.width - width - 2 : 2 + x: Settings.settings.useFahrenheit ? customSwitch.width - width - 2 : 2 Text { anchors.centerIn: parent - text: useFahrenheit ? "\u00b0F" : "\u00b0C" + text: Settings.settings.useFahrenheit ? "\u00b0F" : "\u00b0C" font.family: Theme.fontFamily font.pixelSize: 12 font.bold: true @@ -156,7 +147,7 @@ Rectangle { MouseArea { anchors.fill: parent onClicked: { - temperatureUnitChanged(!useFahrenheit) + Settings.settings.useFahrenheit = !Settings.settings.useFahrenheit } } } diff --git a/Widgets/Sidebar/Panel/BluetoothPanel.qml b/Widgets/Sidebar/Panel/BluetoothPanel.qml index defd564..57e8bcc 100644 --- a/Widgets/Sidebar/Panel/BluetoothPanel.qml +++ b/Widgets/Sidebar/Panel/BluetoothPanel.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import Quickshell.Wayland import Quickshell import Quickshell.Bluetooth diff --git a/Widgets/Sidebar/Panel/Music.qml b/Widgets/Sidebar/Panel/Music.qml index 35bd226..521351a 100644 --- a/Widgets/Sidebar/Panel/Music.qml +++ b/Widgets/Sidebar/Panel/Music.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Controls 2.15 -import QtQuick.Layouts 1.15 +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts import Qt5Compat.GraphicalEffects import qs.Settings import qs.Components diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index f59035a..1d76b0c 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -7,386 +7,422 @@ import qs.Settings import qs.Widgets.Sidebar.Config import qs.Components -PanelWindow { - id: panelPopup - implicitWidth: 500 - implicitHeight: 800 - visible: false - color: "transparent" - screen: modelData - anchors.top: true - anchors.right: true - margins.top: 0 - WlrLayershell.keyboardFocus: (settingsModal.visible && mouseArea.containsMouse) ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None - - // Animation properties - property real slideOffset: width - property bool isAnimating: false +PanelWithOverlay { + id: sidebarPopup function showAt() { - if (!visible) { - visible = true; - forceActiveFocus(); - slideAnim.from = width; - slideAnim.to = 0; - slideAnim.running = true; - if (weather) weather.startWeatherFetch(); - if (systemWidget) systemWidget.panelVisible = true; - if (quickAccessWidget) quickAccessWidget.panelVisible = true; - } + sidebarPopupRect.showAt(); } function hidePopup() { - if (visible) { - slideAnim.from = 0; - slideAnim.to = width; - slideAnim.running = true; - } - } - - NumberAnimation { - id: slideAnim - target: panelPopup - property: "slideOffset" - duration: 300 - easing.type: Easing.OutCubic - - onStopped: { - if (panelPopup.slideOffset === panelPopup.width) { - panelPopup.visible = false; - // Stop monitoring and background tasks when hidden - if (weather) weather.stopWeatherFetch(); - if (systemWidget) systemWidget.panelVisible = false; - if (quickAccessWidget) quickAccessWidget.panelVisible = false; - } - panelPopup.isAnimating = false; - } - - onStarted: { - panelPopup.isAnimating = true; - } + sidebarPopupRect.hidePopup(); } - property int leftPadding: 20 - property int bottomPadding: 20 + function show() { + sidebarPopupRect.showAt(); + } + + function dismiss() { + sidebarPopupRect.hidePopup(); + } Rectangle { - id: mainRectangle - width: parent.width - leftPadding - height: parent.height - bottomPadding + id: sidebarPopupRect + implicitWidth: 500 + implicitHeight: 800 + visible: parent.visible + color: "transparent" anchors.top: parent.top - x: leftPadding + slideOffset - y: 0 - color: Theme.backgroundPrimary - bottomLeftRadius: 20 - z: 0 - - Behavior on x { - enabled: !panelPopup.isAnimating - NumberAnimation { duration: 300; easing.type: Easing.OutCubic } - } - } + anchors.right: parent.right - property alias settingsModal: settingsModal - property alias wallpaperPanelModal: wallpaperPanelModal - property alias wifiPanelModal: wifiPanel.panel - property alias bluetoothPanelModal: bluetoothPanel.panel - SettingsModal { - id: settingsModal - } + // Animation properties + property real slideOffset: width + property bool isAnimating: false - Item { - anchors.fill: mainRectangle - x: slideOffset - - Behavior on x { - enabled: !panelPopup.isAnimating - NumberAnimation { duration: 300; easing.type: Easing.OutCubic } - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 16 - - System { - id: systemWidget - Layout.alignment: Qt.AlignHCenter - z: 3 + function showAt() { + if (!sidebarPopup.visible) { + sidebarPopup.visible = true; + forceActiveFocus(); + slideAnim.from = width; + slideAnim.to = 0; + slideAnim.running = true; + if (weather) + weather.startWeatherFetch(); + if (systemWidget) + systemWidget.panelVisible = true; + if (quickAccessWidget) + quickAccessWidget.panelVisible = true; } + } - Weather { - id: weather - Layout.alignment: Qt.AlignHCenter - z: 2 + function hidePopup() { + if (sidebarPopupRect.settingsModal && sidebarPopupRect.settingsModal.visible) { + sidebarPopupRect.settingsModal.visible = false; } - - // Music and System Monitor row - RowLayout { - spacing: 12 - Layout.fillWidth: true - Layout.alignment: Qt.AlignHCenter - - Music { - z: 2 + if (sidebarPopupRect.wallpaperPanelModal && sidebarPopupRect.wallpaperPanelModal.visible) { + sidebarPopupRect.wallpaperPanelModal.visible = false; + } + if (sidebarPopupRect.wifiPanelModal && sidebarPopupRect.wifiPanelModal.visible) { + sidebarPopupRect.wifiPanelModal.visible = false; } + if (sidebarPopupRect.bluetoothPanelModal && sidebarPopupRect.bluetoothPanelModal.visible) { + sidebarPopupRect.bluetoothPanelModal.visible = false; + } + if (sidebarPopup.visible) { + slideAnim.from = 0; + slideAnim.to = width; + slideAnim.running = true; + } + } - SystemMonitor { - id: systemMonitor - z: 2 + NumberAnimation { + id: slideAnim + target: sidebarPopupRect + property: "slideOffset" + duration: 300 + easing.type: Easing.OutCubic + + onStopped: { + if (sidebarPopupRect.slideOffset === sidebarPopupRect.width) { + sidebarPopup.visible = false; + // Stop monitoring and background tasks when hidden + if (weather) + weather.stopWeatherFetch(); + if (systemWidget) + systemWidget.panelVisible = false; + if (quickAccessWidget) + quickAccessWidget.panelVisible = false; + } + sidebarPopupRect.isAnimating = false; + } + + onStarted: { + sidebarPopupRect.isAnimating = true; + } + } + + property int leftPadding: 20 + property int bottomPadding: 20 + + Rectangle { + id: mainRectangle + width: sidebarPopupRect.width - sidebarPopupRect.leftPadding + height: sidebarPopupRect.height - sidebarPopupRect.bottomPadding + anchors.top: sidebarPopupRect.top + x: sidebarPopupRect.leftPadding + sidebarPopupRect.slideOffset + y: 0 + color: Theme.backgroundPrimary + bottomLeftRadius: 20 + z: 0 + + Behavior on x { + enabled: !sidebarPopupRect.isAnimating + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } + } + } + + property alias settingsModal: settingsModal + property alias wallpaperPanelModal: wallpaperPanelModal + property alias wifiPanelModal: wifiPanel.panel + property alias bluetoothPanelModal: bluetoothPanel.panel + SettingsModal { + id: settingsModal + } + + Item { + anchors.fill: mainRectangle + x: sidebarPopupRect.slideOffset + + Behavior on x { + enabled: !sidebarPopupRect.isAnimating + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic } } - // Power profile, Wifi and Bluetooth row - RowLayout { - Layout.alignment: Qt.AlignLeft - Layout.preferredHeight: 80 + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 20 spacing: 16 - z: 3 - PowerProfile { + System { + id: systemWidget + Layout.alignment: Qt.AlignHCenter + z: 3 + } + + Weather { + id: weather + Layout.alignment: Qt.AlignHCenter + z: 2 + } + + // Music and System Monitor row + RowLayout { + spacing: 12 + Layout.fillWidth: true + Layout.alignment: Qt.AlignHCenter + + Music { + z: 2 + } + + SystemMonitor { + id: systemMonitor + z: 2 + } + } + + // Power profile, Wifi and Bluetooth row + RowLayout { Layout.alignment: Qt.AlignLeft Layout.preferredHeight: 80 - } + spacing: 16 + z: 3 - // Network card containing Wifi and Bluetooth - Rectangle { - Layout.preferredHeight: 80 - Layout.preferredWidth: 140 - Layout.fillWidth: false - color: Theme.surface - radius: 18 - - Row { - anchors.horizontalCenter: parent.horizontalCenter - anchors.verticalCenter: parent.verticalCenter - spacing: 20 - - // Wifi button - Rectangle { - id: wifiButton - width: 36; height: 36 - radius: 18 - border.color: Theme.accentPrimary - border.width: 1 - color: wifiButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + PowerProfile { + Layout.alignment: Qt.AlignLeft + Layout.preferredHeight: 80 + } - Text { - anchors.centerIn: parent - text: "wifi" - font.family: "Material Symbols Outlined" - font.pixelSize: 22 - color: wifiButtonArea.containsMouse - ? Theme.backgroundPrimary - : Theme.accentPrimary - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter + // Network card containing Wifi and Bluetooth + Rectangle { + Layout.preferredHeight: 80 + Layout.preferredWidth: 140 + Layout.fillWidth: false + color: Theme.surface + radius: 18 + + Row { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + spacing: 20 + + // Wifi button + Rectangle { + id: wifiButton + width: 36 + height: 36 + radius: 18 + border.color: Theme.accentPrimary + border.width: 1 + color: wifiButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + + Text { + anchors.centerIn: parent + text: "wifi" + font.family: "Material Symbols Outlined" + font.pixelSize: 22 + color: wifiButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } + + MouseArea { + id: wifiButtonArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: wifiPanel.showAt() + } } - MouseArea { - id: wifiButtonArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: wifiPanel.showAt() - } - } - - // Bluetooth button - Rectangle { - id: bluetoothButton - width: 36; height: 36 - radius: 18 - border.color: Theme.accentPrimary - border.width: 1 - color: bluetoothButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + // Bluetooth button + Rectangle { + id: bluetoothButton + width: 36 + height: 36 + radius: 18 + border.color: Theme.accentPrimary + border.width: 1 + color: bluetoothButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - Text { - anchors.centerIn: parent - text: "bluetooth" - font.family: "Material Symbols Outlined" - font.pixelSize: 22 - color: bluetoothButtonArea.containsMouse - ? Theme.backgroundPrimary - : Theme.accentPrimary - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - } + Text { + anchors.centerIn: parent + text: "bluetooth" + font.family: "Material Symbols Outlined" + font.pixelSize: 22 + color: bluetoothButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + } - MouseArea { - id: bluetoothButtonArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: bluetoothPanel.showAt() + MouseArea { + id: bluetoothButtonArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: bluetoothPanel.showAt() + } } } } } + + // Hidden panel components for modal functionality + WifiPanel { + id: wifiPanel + visible: false + } + BluetoothPanel { + id: bluetoothPanel + visible: false + } + + Item { + Layout.fillHeight: true + } + + // QuickAccess widget + QuickAccess { + id: quickAccessWidget + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: -16 + z: 2 + isRecording: sidebarPopupRect.isRecording + + onRecordingRequested: { + startRecording(); + } + + onStopRecordingRequested: { + stopRecording(); + } + + onRecordingStateMismatch: function (actualState) { + isRecording = actualState; + quickAccessWidget.isRecording = actualState; + } + + onSettingsRequested: { + settingsModal.visible = true; + } + onWallpaperRequested: { + wallpaperPanelModal.visible = true; + } + } + } + Keys.onEscapePressed: sidebarPopupRect.hidePopup() + } + + // Recording properties + property bool isRecording: false + property var recordingProcess: null + property var recordingPid: null + + // Start screen recording + function startRecording() { + var currentDate = new Date(); + var hours = String(currentDate.getHours()).padStart(2, '0'); + var minutes = String(currentDate.getMinutes()).padStart(2, '0'); + var day = String(currentDate.getDate()).padStart(2, '0'); + var month = String(currentDate.getMonth() + 1).padStart(2, '0'); + var year = currentDate.getFullYear(); + + var filename = hours + "-" + minutes + "-" + day + "-" + month + "-" + year + ".mp4"; + var outputPath = Settings.settings.videoPath + filename; + var command = "gpu-screen-recorder -w portal -f 60 -a default_output -o " + outputPath; + var qmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "' + command + '"]; running: true }'; + + recordingProcess = Qt.createQmlObject(qmlString, sidebarPopup); + isRecording = true; + quickAccessWidget.isRecording = true; + } + + // Stop recording with cleanup + function stopRecording() { + if (recordingProcess && isRecording) { + var stopQmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -SIGINT -f \'gpu-screen-recorder.*portal\'"]; running: true; onExited: function() { destroy() } }'; + + var stopProcess = Qt.createQmlObject(stopQmlString, sidebarPopup); + + var cleanupTimer = Qt.createQmlObject('import QtQuick; Timer { interval: 3000; running: true; repeat: false }', sidebarPopup); + cleanupTimer.triggered.connect(function () { + if (recordingProcess) { + recordingProcess.running = false; + recordingProcess.destroy(); + recordingProcess = null; + } + + var forceKillQml = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -9 -f \'gpu-screen-recorder.*portal\' 2>/dev/null || true"]; running: true; onExited: function() { destroy() } }'; + var forceKillProcess = Qt.createQmlObject(forceKillQml, sidebarPopup); + + cleanupTimer.destroy(); + }); } - // Hidden panel components for modal functionality - WifiPanel { - id: wifiPanel - visible: false - } - BluetoothPanel { - id: bluetoothPanel - visible: false - } + isRecording = false; + quickAccessWidget.isRecording = false; + recordingPid = null; + } - Item { - Layout.fillHeight: true + // Clean up processes on destruction + Component.onDestruction: { + if (isRecording) { + stopRecording(); } + if (recordingProcess) { + recordingProcess.running = false; + recordingProcess.destroy(); + recordingProcess = null; + } + } - // QuickAccess widget - QuickAccess { - id: quickAccessWidget - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: -16 - z: 2 - isRecording: panelPopup.isRecording - - onRecordingRequested: { - startRecording() - } - - onStopRecordingRequested: { - stopRecording() - } - - onRecordingStateMismatch: function(actualState) { - isRecording = actualState - quickAccessWidget.isRecording = actualState - } - - onSettingsRequested: { - settingsModal.visible = true - } - onWallpaperRequested: { - wallpaperPanelModal.visible = true + Corners { + id: sidebarCornerLeft + position: "bottomright" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: mainRectangle.top + offsetX: -447 + sidebarPopupRect.slideOffset + offsetY: 0 + + Behavior on offsetX { + enabled: !sidebarPopupRect.isAnimating + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic } } } - Keys.onEscapePressed: panelPopup.hidePopup() - } - onVisibleChanged: if (!visible) {/* cleanup if needed */} - - // Update height when screen changes - onScreenChanged: { - if (screen) { - // Height is now hardcoded to 720, no need to update - } - } - - // Recording properties - property bool isRecording: false - property var recordingProcess: null - property var recordingPid: null - - // Start screen recording - function startRecording() { - var currentDate = new Date() - var hours = String(currentDate.getHours()).padStart(2, '0') - var minutes = String(currentDate.getMinutes()).padStart(2, '0') - var day = String(currentDate.getDate()).padStart(2, '0') - var month = String(currentDate.getMonth() + 1).padStart(2, '0') - var year = currentDate.getFullYear() + Corners { + id: sidebarCornerBottom + position: "bottomright" + size: 1.1 + fillColor: Theme.backgroundPrimary + offsetX: 33 + sidebarPopupRect.slideOffset + offsetY: 46 - var filename = hours + "-" + minutes + "-" + day + "-" + month + "-" + year + ".mp4" - var outputPath = Settings.videoPath + filename - var command = "gpu-screen-recorder -w portal -f 60 -a default_output -o " + outputPath - var qmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "' + command + '"]; running: true }' - - recordingProcess = Qt.createQmlObject(qmlString, panelPopup) - isRecording = true - quickAccessWidget.isRecording = true - } - - // Stop recording with cleanup - function stopRecording() { - if (recordingProcess && isRecording) { - var stopQmlString = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -SIGINT -f \'gpu-screen-recorder.*portal\'"]; running: true; onExited: function() { destroy() } }' - - var stopProcess = Qt.createQmlObject(stopQmlString, panelPopup) - - var cleanupTimer = Qt.createQmlObject('import QtQuick; Timer { interval: 3000; running: true; repeat: false }', panelPopup) - cleanupTimer.triggered.connect(function() { - if (recordingProcess) { - recordingProcess.running = false - recordingProcess.destroy() - recordingProcess = null + Behavior on offsetX { + enabled: !sidebarPopupRect.isAnimating + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic } - - var forceKillQml = 'import Quickshell.Io; Process { command: ["sh", "-c", "pkill -9 -f \'gpu-screen-recorder.*portal\' 2>/dev/null || true"]; running: true; onExited: function() { destroy() } }' - var forceKillProcess = Qt.createQmlObject(forceKillQml, panelPopup) - - cleanupTimer.destroy() - }) - } - - isRecording = false - quickAccessWidget.isRecording = false - recordingPid = null - } - - // Clean up processes on destruction - Component.onDestruction: { - if (isRecording) { - stopRecording() - } - if (recordingProcess) { - recordingProcess.running = false - recordingProcess.destroy() - recordingProcess = null - } - } - - Corners { - id: sidebarCornerLeft - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: mainRectangle.top - offsetX: -447 + panelPopup.slideOffset - offsetY: 0 - - Behavior on offsetX { - enabled: !panelPopup.isAnimating - NumberAnimation { duration: 300; easing.type: Easing.OutCubic } - } - } - - Corners { - id: sidebarCornerBottom - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - offsetX: 33 + panelPopup.slideOffset - offsetY: 46 - - Behavior on offsetX { - enabled: !panelPopup.isAnimating - NumberAnimation { duration: 300; easing.type: Easing.OutCubic } - } - } - - WallpaperPanel { - id: wallpaperPanelModal - visible: false - Component.onCompleted: { - if (parent) { - wallpaperPanelModal.anchors.top = parent.top; - wallpaperPanelModal.anchors.right = parent.right; } } - // Add a close button inside WallpaperPanel.qml for user to close the modal + + WallpaperPanel { + id: wallpaperPanelModal + visible: false + Component.onCompleted: { + if (parent) { + wallpaperPanelModal.anchors.top = parent.top; + wallpaperPanelModal.anchors.right = parent.right; + } + } + // Add a close button inside WallpaperPanel.qml for user to close the modal + } } } diff --git a/Widgets/Sidebar/Panel/PowerProfile.qml b/Widgets/Sidebar/Panel/PowerProfile.qml index 48c7f55..c68fa4f 100644 --- a/Widgets/Sidebar/Panel/PowerProfile.qml +++ b/Widgets/Sidebar/Panel/PowerProfile.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import Quickshell.Services.UPower import qs.Settings import qs.Components diff --git a/Widgets/Sidebar/Panel/System.qml b/Widgets/Sidebar/Panel/System.qml index 50d42be..200d035 100644 --- a/Widgets/Sidebar/Panel/System.qml +++ b/Widgets/Sidebar/Panel/System.qml @@ -53,7 +53,7 @@ Rectangle { source: Image { id: avatarImage anchors.fill: parent - source: Settings.profileImage !== undefined ? Settings.profileImage : "" + source: Settings.settings.profileImage !== undefined ? Settings.settings.profileImage : "" fillMode: Image.PreserveAspectCrop asynchronous: true cache: false @@ -66,7 +66,7 @@ Rectangle { radius: 22 visible: false } - visible: Settings.profileImage !== undefined && Settings.profileImage !== "" + visible: Settings.settings.profileImage !== undefined && Settings.settings.profileImage !== "" z: 1 } @@ -77,7 +77,7 @@ Rectangle { font.family: "Material Symbols Outlined" font.pixelSize: 24 color: Theme.onAccent - visible: Settings.profileImage === undefined || Settings.profileImage === "" + visible: Settings.settings.profileImage === undefined || Settings.settings.profileImage === "" z: 0 } } diff --git a/Widgets/Sidebar/Panel/SystemMonitor.qml b/Widgets/Sidebar/Panel/SystemMonitor.qml index d22e836..b9380e4 100644 --- a/Widgets/Sidebar/Panel/SystemMonitor.qml +++ b/Widgets/Sidebar/Panel/SystemMonitor.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import Quickshell.Io import qs.Components import qs.Services diff --git a/Widgets/Sidebar/Panel/WallpaperPanel.qml b/Widgets/Sidebar/Panel/WallpaperPanel.qml index c364a97..83d0869 100644 --- a/Widgets/Sidebar/Panel/WallpaperPanel.qml +++ b/Widgets/Sidebar/Panel/WallpaperPanel.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import Quickshell import Quickshell.Io import Quickshell.Widgets @@ -126,8 +126,8 @@ PanelWindow { anchors.margins: 4 color: Qt.darker(Theme.backgroundPrimary, 1.1) radius: 12 - border.color: Settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline - border.width: Settings.currentWallpaper === modelData ? 3 : 1 + border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline + border.width: Settings.settings.currentWallpaper === modelData ? 3 : 1 Image { id: wallpaperImage anchors.fill: parent diff --git a/Widgets/Sidebar/Panel/Weather.qml b/Widgets/Sidebar/Panel/Weather.qml index da28eb0..f9feb53 100644 --- a/Widgets/Sidebar/Panel/Weather.qml +++ b/Widgets/Sidebar/Panel/Weather.qml @@ -1,6 +1,6 @@ -import QtQuick 2.15 -import QtQuick.Layouts 1.15 -import QtQuick.Controls 2.15 +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls import qs.Settings import "../../../Helpers/Weather.js" as WeatherHelper @@ -11,7 +11,7 @@ Rectangle { color: "transparent" anchors.horizontalCenterOffset: -2 - property string city: Settings.weatherCity !== undefined ? Settings.weatherCity : "" + property string city: Settings.settings.weatherCity !== undefined ? Settings.settings.weatherCity : "" property var weatherData: null property string errorString: "" property bool isVisible: false @@ -95,7 +95,7 @@ Rectangle { } } Text { - text: weatherData && weatherData.current_weather ? ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? `${Math.round(weatherData.current_weather.temperature * 9/5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? "--°F" : "--°C") + text: weatherData && weatherData.current_weather ? ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? `${Math.round(weatherData.current_weather.temperature * 9/5 + 32)}°F` : `${Math.round(weatherData.current_weather.temperature)}°C`) : ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? "--°F" : "--°C") font.family: Theme.fontFamily font.pixelSize: 24 font.bold: true @@ -151,7 +151,7 @@ Rectangle { } Text { // High/low temp - text: weatherData && weatherData.daily ? ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? `${Math.round(weatherData.daily.temperature_2m_max[index] * 9/5 + 32)}° / ${Math.round(weatherData.daily.temperature_2m_min[index] * 9/5 + 32)}°` : `${Math.round(weatherData.daily.temperature_2m_max[index])}° / ${Math.round(weatherData.daily.temperature_2m_min[index])}°`) : ((Settings.useFahrenheit !== undefined ? Settings.useFahrenheit : false) ? "--° / --°" : "--° / --°") + text: weatherData && weatherData.daily ? ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? `${Math.round(weatherData.daily.temperature_2m_max[index] * 9/5 + 32)}° / ${Math.round(weatherData.daily.temperature_2m_min[index] * 9/5 + 32)}°` : `${Math.round(weatherData.daily.temperature_2m_max[index])}° / ${Math.round(weatherData.daily.temperature_2m_min[index])}°`) : ((Settings.settings.useFahrenheit !== undefined ? Settings.settings.useFahrenheit : false) ? "--° / --°" : "--° / --°") font.family: Theme.fontFamily font.pixelSize: 12 color: Theme.textPrimary From d40709952beb15f6788c795ff49902866b1f4d09 Mon Sep 17 00:00:00 2001 From: ferreo Date: Fri, 18 Jul 2025 16:57:11 +0100 Subject: [PATCH 2/2] fix: fix weirdness --- Components/PanelWithOverlay.qml | 2 +- Services/WallpaperManager.qml | 1 - Widgets/Sidebar/Config/ProfileSettings.qml | 325 +++++++++++-------- Widgets/Sidebar/Config/SettingsModal.qml | 11 +- Widgets/Sidebar/Config/WallpaperSettings.qml | 221 +++++++------ Widgets/Sidebar/Panel/BluetoothPanel.qml | 2 +- Widgets/Sidebar/Panel/WifiPanel.qml | 2 +- 7 files changed, 318 insertions(+), 246 deletions(-) diff --git a/Components/PanelWithOverlay.qml b/Components/PanelWithOverlay.qml index ca32de0..7344165 100644 --- a/Components/PanelWithOverlay.qml +++ b/Components/PanelWithOverlay.qml @@ -22,7 +22,7 @@ PanelWindow { color: visible ? overlayColor : "transparent" visible: false WlrLayershell.exclusionMode: ExclusionMode.Ignore - WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.Exclusive : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand screen: (typeof modelData !== 'undefined' ? modelData : null) anchors.top: true anchors.left: true diff --git a/Services/WallpaperManager.qml b/Services/WallpaperManager.qml index fee4931..84c0e4f 100644 --- a/Services/WallpaperManager.qml +++ b/Services/WallpaperManager.qml @@ -71,7 +71,6 @@ Singleton { if (Settings.settings.randomWallpaper) { randomWallpaperTimer.stop(); randomWallpaperTimer.start(); - setRandomWallpaper(); } } diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index 111f607..91ac069 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -7,7 +7,7 @@ import qs.Settings Rectangle { id: profileSettingsCard Layout.fillWidth: true - Layout.preferredHeight: 340 + Layout.preferredHeight: 500 color: Theme.surface radius: 18 border.color: "transparent" @@ -19,20 +19,20 @@ Rectangle { anchors.margins: 18 spacing: 12 - // Profile Image Header RowLayout { Layout.fillWidth: true spacing: 12 Text { - text: "person" + text: "settings" font.family: "Material Symbols Outlined" font.pixelSize: 20 color: Theme.accentPrimary } Text { - text: "Profile Image" + text: "System Settings" + font.family: Theme.fontFamily font.pixelSize: 16 font.bold: true color: Theme.textPrimary @@ -40,83 +40,102 @@ Rectangle { } } - // Profile Image Input Row - RowLayout { + // Profile Image Input Section + ColumnLayout { spacing: 8 Layout.fillWidth: true - Rectangle { - width: 36 - height: 36 - radius: 18 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - Image { - id: avatarImage - anchors.fill: parent - anchors.margins: 2 - source: Settings.settings.profileImage - fillMode: Image.PreserveAspectCrop - visible: false - asynchronous: true - cache: false - sourceSize.width: 64 - sourceSize.height: 64 - } - OpacityMask { - anchors.fill: avatarImage - source: avatarImage - maskSource: Rectangle { - width: avatarImage.width - height: avatarImage.height - radius: avatarImage.width / 2 - visible: false - } - visible: Settings.settings.profileImage !== "" - } - - // Fallback icon - Text { - anchors.centerIn: parent - text: "person" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: Theme.accentPrimary - visible: Settings.settings.profileImage === "" - } + Text { + text: "Profile Image" + font.family: Theme.fontFamily + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary } - // Text input styled exactly like weather city - Rectangle { + RowLayout { + spacing: 8 Layout.fillWidth: true - Layout.preferredHeight: 36 - radius: 8 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - TextInput { - id: profileImageInput - anchors.fill: parent - anchors.margins: 12 - text: Settings.settings.profileImage - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - focus: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhNone - onTextChanged: { - Settings.settings.profileImage = text - } - MouseArea { + Rectangle { + width: 40 + height: 40 + radius: 20 + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + Image { + id: avatarImage anchors.fill: parent - onClicked: { - profileImageInput.forceActiveFocus() + anchors.margins: 2 + source: Settings.settings.profileImage + fillMode: Image.PreserveAspectCrop + visible: false + asynchronous: true + cache: false + sourceSize.width: 64 + sourceSize.height: 64 + } + OpacityMask { + anchors.fill: avatarImage + source: avatarImage + maskSource: Rectangle { + width: avatarImage.width + height: avatarImage.height + radius: avatarImage.width / 2 + visible: false + } + visible: Settings.settings.profileImage !== "" + } + + // Fallback icon + Text { + anchors.centerIn: parent + text: "person" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.accentPrimary + visible: Settings.settings.profileImage === "" + } + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 8 + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: profileImageInput + 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.profileImage + 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.profileImage = text + } + MouseArea { + anchors.fill: parent + onClicked: { + profileImageInput.forceActiveFocus() + } } } } @@ -127,9 +146,11 @@ Rectangle { RowLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 Text { text: "Show Active Window Icon" + font.family: Theme.fontFamily font.pixelSize: 13 font.bold: true color: Theme.textPrimary @@ -148,7 +169,7 @@ Rectangle { color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline border.width: 2 - + Rectangle { id: thumb width: 28 @@ -159,12 +180,12 @@ Rectangle { border.width: 1 y: 2 x: Settings.settings.showActiveWindowIcon ? customSwitch.width - width - 2 : 2 - + Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } } - + MouseArea { anchors.fill: parent onClicked: { @@ -178,9 +199,11 @@ Rectangle { RowLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 Text { text: "Show System Info In Bar" + font.family: Theme.fontFamily font.pixelSize: 13 font.bold: true color: Theme.textPrimary @@ -200,7 +223,7 @@ Rectangle { color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline border.width: 2 - + Rectangle { id: thumb2 width: 28 @@ -211,12 +234,12 @@ Rectangle { border.width: 1 y: 2 x: Settings.settings.showSystemInfoInBar ? customSwitch2.width - width - 2 : 2 - + Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } } - + MouseArea { anchors.fill: parent onClicked: { @@ -230,9 +253,11 @@ Rectangle { RowLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 Text { text: "Show Media In Bar" + font.family: Theme.fontFamily font.pixelSize: 13 font.bold: true color: Theme.textPrimary @@ -252,7 +277,7 @@ Rectangle { color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline border.width: 2 - + Rectangle { id: thumb3 width: 28 @@ -263,12 +288,12 @@ Rectangle { border.width: 1 y: 2 x: Settings.settings.showMediaInBar ? customSwitch3.width - width - 2 : 2 - + Behavior on x { NumberAnimation { duration: 200; easing.type: Easing.OutCubic } } } - + MouseArea { anchors.fill: parent onClicked: { @@ -279,103 +304,118 @@ Rectangle { } // Visualizer Type Selection - RowLayout { + ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 16 Text { text: "Visualizer Type" + font.family: Theme.fontFamily font.pixelSize: 13 font.bold: true color: Theme.textPrimary - Layout.alignment: Qt.AlignVCenter } - Item { + ComboBox { + id: visualizerTypeComboBox Layout.fillWidth: true - } + Layout.preferredHeight: 40 + model: ["radial", "fire", "diamond"] + currentIndex: model.indexOf(Settings.settings.visualizerType) - // Dropdown for visualizer type - Rectangle { - width: 120 - height: 36 - radius: 8 - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 8 + } - Text { - id: visualizerTypeText - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.verticalCenter: parent.verticalCenter - text: Settings.settings.visualizerType === "fire" ? "Fire" : - Settings.settings.visualizerType === "diamond" ? "Diamond" : - Settings.settings.visualizerType === "radial" ? "Radial" : "Radial" + 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 } - Text { + 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: 20 + font.pixelSize: 24 color: Theme.textPrimary - anchors.right: parent.right - anchors.rightMargin: 8 - anchors.verticalCenter: parent.verticalCenter } - MouseArea { - anchors.fill: parent - onClicked: { - visualizerTypeMenu.open() + 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: 8 } } - Menu { - id: visualizerTypeMenu - width: 120 - y: parent.height + 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 - MenuItem { - text: "Fire" - onTriggered: { - Settings.settings.visualizerType = "fire" - } - } - MenuItem { - text: "Diamond" - onTriggered: { - Settings.settings.visualizerType = "diamond" - } - } - MenuItem { - text: "Radial" - onTriggered: { - Settings.settings.visualizerType = "radial" - } + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } } + + onActivated: { + Settings.settings.visualizerType = model[index]; + } } } - // Video Path Input Row - RowLayout { + // Video Path Input Section + ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 Text { text: "Video Path" + font.family: Theme.fontFamily font.pixelSize: 13 font.bold: true color: Theme.textPrimary - Layout.alignment: Qt.AlignVCenter } Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 + Layout.preferredHeight: 40 radius: 8 color: Theme.surfaceVariant border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline @@ -383,9 +423,16 @@ Rectangle { TextInput { id: videoPathInput - anchors.fill: parent - anchors.margins: 12 + 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.videoPath !== undefined ? Settings.settings.videoPath : "" + font.family: Theme.fontFamily font.pixelSize: 13 color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter @@ -404,4 +451,4 @@ Rectangle { } } } -} +} diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index 3a9dabd..9e17195 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -20,7 +20,7 @@ PanelWindow { //z: 100 //border.color: Theme.outline //border.width: 1 - WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand Rectangle { anchors.fill: parent @@ -31,12 +31,17 @@ PanelWindow { z: 0 ColumnLayout { + id: content anchors.fill: parent - anchors.margins: 32 + anchors.leftMargin: 32 + anchors.rightMargin: 32 + anchors.topMargin: 32 + spacing: 24 // Header ColumnLayout { + id: header Layout.fillWidth: true spacing: 4 RowLayout { @@ -99,7 +104,7 @@ PanelWindow { // Scrollable settings area Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 520 + Layout.preferredHeight: content.height - settingsTabs.height - header.height - 128 color: "transparent" border.width: 0 radius: 20 diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml index 96aaa55..f1aa93a 100644 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ b/Widgets/Sidebar/Config/WallpaperSettings.qml @@ -1,4 +1,4 @@ -import QtQuick +import QtQuick import QtQuick.Layouts import QtQuick.Controls import qs.Settings @@ -6,11 +6,10 @@ import qs.Settings Rectangle { id: wallpaperSettingsCard Layout.fillWidth: true - Layout.preferredHeight: 680 + Layout.preferredHeight: 720 color: Theme.surface radius: 18 - ColumnLayout { anchors.fill: parent anchors.margins: 18 @@ -27,7 +26,7 @@ Rectangle { color: Theme.accentPrimary } Text { - text: "Wallpaper Folder" + text: "Wallpaper Settings" font.family: Theme.fontFamily font.pixelSize: 16 font.bold: true @@ -36,39 +35,52 @@ Rectangle { } } - // Folder Path Input - Rectangle { + ColumnLayout { + spacing: 8 Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 8 - 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 + + Text { + text: "Wallpaper Path" font.family: Theme.fontFamily font.pixelSize: 13 + font.bold: true color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.wallpaperFolder = text - } - MouseArea { - anchors.fill: parent - onClicked: folderInput.forceActiveFocus() + } + + // Folder Path Input + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 8 + 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 + onClicked: folderInput.forceActiveFocus() + } } } } @@ -99,7 +111,7 @@ Rectangle { 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 @@ -110,16 +122,19 @@ Rectangle { border.width: 1 y: 2 x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - + Behavior on x { - NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } } } - + MouseArea { anchors.fill: parent onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW + Settings.settings.useSWWW = !Settings.settings.useSWWW; } } } @@ -151,7 +166,7 @@ Rectangle { 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 @@ -162,16 +177,19 @@ Rectangle { border.width: 1 y: 2 x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - + Behavior on x { - NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } } } - + MouseArea { anchors.fill: parent onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; } } } @@ -203,7 +221,7 @@ Rectangle { 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 @@ -214,16 +232,19 @@ Rectangle { border.width: 1 y: 2 x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - + Behavior on x { - NumberAnimation { duration: 200; easing.type: Easing.OutCubic } + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } } } - + MouseArea { anchors.fill: parent onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; } } } @@ -243,18 +264,18 @@ Rectangle { 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 @@ -263,7 +284,7 @@ Rectangle { stepSize: 10 value: Settings.settings.wallpaperInterval snapMode: Slider.SnapAlways - + background: Rectangle { x: intervalSlider.leftPadding y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 @@ -273,7 +294,7 @@ Rectangle { height: implicitHeight radius: 2 color: Theme.surfaceVariant - + Rectangle { width: intervalSlider.visualPosition * parent.width height: parent.height @@ -281,7 +302,7 @@ Rectangle { radius: 2 } } - + handle: Rectangle { x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 @@ -294,11 +315,11 @@ Rectangle { } onMoved: { - Settings.settings.wallpaperInterval = Math.round(value) + Settings.settings.wallpaperInterval = Math.round(value); } } } - + // Resize Mode Setting ColumnLayout { spacing: 12 @@ -311,14 +332,14 @@ Rectangle { 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) - + background: Rectangle { implicitWidth: 120 implicitHeight: 40 @@ -327,7 +348,7 @@ Rectangle { border.width: 1 radius: 8 } - + contentItem: Text { leftPadding: 12 rightPadding: resizeComboBox.indicator.width + resizeComboBox.spacing @@ -338,7 +359,7 @@ Rectangle { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - + indicator: Text { x: resizeComboBox.width - width - 12 y: resizeComboBox.topPadding + (resizeComboBox.availableHeight - height) / 2 @@ -347,22 +368,22 @@ Rectangle { 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 { } + + ScrollIndicator.vertical: ScrollIndicator {} } - + background: Rectangle { color: Theme.surfaceVariant border.color: Theme.outline @@ -370,7 +391,7 @@ Rectangle { radius: 8 } } - + delegate: ItemDelegate { width: resizeComboBox.width contentItem: Text { @@ -382,18 +403,18 @@ Rectangle { elide: Text.ElideRight } highlighted: resizeComboBox.highlightedIndex === index - + background: Rectangle { color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } } - + onActivated: { - Settings.settings.wallpaperResize = model[index] + Settings.settings.wallpaperResize = model[index]; } } } - + // Transition Type Setting ColumnLayout { spacing: 12 @@ -406,14 +427,14 @@ Rectangle { 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) - + background: Rectangle { implicitWidth: 120 implicitHeight: 40 @@ -422,7 +443,7 @@ Rectangle { border.width: 1 radius: 8 } - + contentItem: Text { leftPadding: 12 rightPadding: transitionTypeComboBox.indicator.width + transitionTypeComboBox.spacing @@ -433,7 +454,7 @@ Rectangle { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - + indicator: Text { x: transitionTypeComboBox.width - width - 12 y: transitionTypeComboBox.topPadding + (transitionTypeComboBox.availableHeight - height) / 2 @@ -442,22 +463,22 @@ Rectangle { 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 { } + + ScrollIndicator.vertical: ScrollIndicator {} } - + background: Rectangle { color: Theme.surfaceVariant border.color: Theme.outline @@ -465,7 +486,7 @@ Rectangle { radius: 8 } } - + delegate: ItemDelegate { width: transitionTypeComboBox.width contentItem: Text { @@ -477,18 +498,18 @@ Rectangle { elide: Text.ElideRight } highlighted: transitionTypeComboBox.highlightedIndex === index - + background: Rectangle { color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } } - + onActivated: { - Settings.settings.transitionType = model[index] + Settings.settings.transitionType = model[index]; } } } - + // Transition FPS Setting ColumnLayout { spacing: 12 @@ -503,18 +524,18 @@ Rectangle { 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 @@ -523,7 +544,7 @@ Rectangle { stepSize: 5 value: Settings.settings.transitionFps snapMode: Slider.SnapAlways - + background: Rectangle { x: fpsSlider.leftPadding y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 @@ -533,7 +554,7 @@ Rectangle { height: implicitHeight radius: 2 color: Theme.surfaceVariant - + Rectangle { width: fpsSlider.visualPosition * parent.width height: parent.height @@ -541,7 +562,7 @@ Rectangle { radius: 2 } } - + handle: Rectangle { x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 @@ -554,11 +575,11 @@ Rectangle { } onMoved: { - Settings.settings.transitionFps = Math.round(value) + Settings.settings.transitionFps = Math.round(value); } } } - + // Transition Duration Setting ColumnLayout { spacing: 12 @@ -573,18 +594,18 @@ Rectangle { 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 @@ -593,7 +614,7 @@ Rectangle { stepSize: 0.050 value: Settings.settings.transitionDuration snapMode: Slider.SnapAlways - + background: Rectangle { x: durationSlider.leftPadding y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 @@ -603,7 +624,7 @@ Rectangle { height: implicitHeight radius: 2 color: Theme.surfaceVariant - + Rectangle { width: durationSlider.visualPosition * parent.width height: parent.height @@ -611,7 +632,7 @@ Rectangle { radius: 2 } } - + handle: Rectangle { x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 @@ -624,9 +645,9 @@ Rectangle { } onMoved: { - Settings.settings.transitionDuration = value + Settings.settings.transitionDuration = value; } } } } -} \ No newline at end of file +} diff --git a/Widgets/Sidebar/Panel/BluetoothPanel.qml b/Widgets/Sidebar/Panel/BluetoothPanel.qml index 57e8bcc..599c100 100644 --- a/Widgets/Sidebar/Panel/BluetoothPanel.qml +++ b/Widgets/Sidebar/Panel/BluetoothPanel.qml @@ -80,7 +80,7 @@ Item { anchors.right: true margins.right: 0 margins.top: 0 - WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand onVisibleChanged: { if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) diff --git a/Widgets/Sidebar/Panel/WifiPanel.qml b/Widgets/Sidebar/Panel/WifiPanel.qml index eeb5b8c..7c7a93a 100644 --- a/Widgets/Sidebar/Panel/WifiPanel.qml +++ b/Widgets/Sidebar/Panel/WifiPanel.qml @@ -342,7 +342,7 @@ Item { anchors.right: true margins.right: 0 margins.top: 0 - WlrLayershell.keyboardFocus: visible ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand Component.onCompleted: { wifiLogic.refreshNetworks() }