From fb68300746829574e682d39acfc8840e725449a2 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Tue, 5 Aug 2025 17:41:08 +0200 Subject: [PATCH 01/43] Settings rework... --- Bar/Bar.qml | 35 +- Bar/Modules/ActiveWindow.qml | 7 +- Bar/Modules/Applauncher.qml | 893 ++++++++++++------ Bar/Modules/Battery.qml | 42 +- Bar/Modules/Bluetooth.qml | 272 ++++++ Bar/Modules/Brightness.qml | 1 + Bar/Modules/ClockWidget.qml | 1 + Bar/Modules/CustomTrayMenu.qml | 474 ++++++++-- Bar/Modules/Media.qml | 10 +- Bar/Modules/SettingsButton.qml | 80 ++ Bar/Modules/SystemInfo.qml | 2 + Bar/Modules/SystemTray.qml | 68 +- Bar/Modules/Taskbar.qml | 5 +- Bar/Modules/Volume.qml | 3 +- Bar/Modules/Wifi.qml | 370 ++++++++ Bar/Modules/Workspace.qml | 16 +- Components/Avatar.qml | 51 + Components/CircularProgressBar.qml | 22 +- Components/StyledTooltip.qml | 45 +- Helpers/IPCHandlers.qml | 16 +- Helpers/IdleInhibitor.qml | 6 +- Helpers/Time.js | 18 + README.md | 8 + Services/MusicManager.qml | 33 +- Services/Network.qml | 348 +++++++ Services/WallpaperManager.qml | 13 +- Settings/Settings.qml | 17 + Widgets/Background.qml | 1 + Widgets/Dock.qml | 350 +++++++ Widgets/LockScreen/BatteryCharge.qml | 6 +- Widgets/LockScreen/LockScreen.qml | 68 +- Widgets/Notification/NotificationHistory.qml | 2 +- Widgets/Notification/NotificationIcon.qml | 9 +- Widgets/Notification/NotificationManager.qml | 4 +- Widgets/Notification/NotificationPopup.qml | 22 +- Widgets/Overview.qml | 10 +- Widgets/SettingsWindow/SettingsWindow.qml | 369 ++++++++ Widgets/SettingsWindow/Tabs/About.qml | 405 ++++++++ Widgets/SettingsWindow/Tabs/Bar.qml | 380 ++++++++ .../Tabs/Components/UnitSelector.qml | 97 ++ Widgets/SettingsWindow/Tabs/Display.qml | 354 +++++++ Widgets/SettingsWindow/Tabs/General.qml | 339 +++++++ Widgets/SettingsWindow/Tabs/Misc.qml | 137 +++ Widgets/SettingsWindow/Tabs/Network.qml | 193 ++++ Widgets/SettingsWindow/Tabs/Record.qml | 19 + Widgets/SettingsWindow/Tabs/Recording.qml | 812 ++++++++++++++++ Widgets/SettingsWindow/Tabs/TimeWeather.qml | 283 ++++++ Widgets/Sidebar/Config/ProfileSettings.qml | 127 ++- Widgets/Sidebar/Config/SettingsModal.qml | 82 +- Widgets/Sidebar/Config/WallpaperSettings.qml | 271 ++++-- Widgets/Sidebar/Config/WeatherSettings.qml | 10 +- Widgets/Sidebar/Panel/BluetoothPanel.qml | 6 +- Widgets/Sidebar/Panel/Music.qml | 165 +++- Widgets/Sidebar/Panel/PanelPopup.qml | 41 +- Widgets/Sidebar/Panel/PowerProfile.qml | 6 +- Widgets/Sidebar/Panel/QuickAccess.qml | 14 +- Widgets/Sidebar/Panel/System.qml | 87 +- Widgets/Sidebar/Panel/SystemMonitor.qml | 13 +- Widgets/Sidebar/Panel/WallpaperPanel.qml | 29 +- Widgets/Sidebar/Panel/Weather.qml | 38 +- Widgets/Sidebar/Panel/WifiPanel.qml | 505 +++++++--- qmlls.ini | 0 shell.qml | 55 +- 63 files changed, 7139 insertions(+), 1026 deletions(-) create mode 100644 Bar/Modules/Bluetooth.qml create mode 100644 Bar/Modules/SettingsButton.qml create mode 100644 Bar/Modules/Wifi.qml create mode 100644 Components/Avatar.qml create mode 100644 Helpers/Time.js create mode 100644 Services/Network.qml create mode 100644 Widgets/Dock.qml create mode 100644 Widgets/SettingsWindow/SettingsWindow.qml create mode 100644 Widgets/SettingsWindow/Tabs/About.qml create mode 100644 Widgets/SettingsWindow/Tabs/Bar.qml create mode 100644 Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml create mode 100644 Widgets/SettingsWindow/Tabs/Display.qml create mode 100644 Widgets/SettingsWindow/Tabs/General.qml create mode 100644 Widgets/SettingsWindow/Tabs/Misc.qml create mode 100644 Widgets/SettingsWindow/Tabs/Network.qml create mode 100644 Widgets/SettingsWindow/Tabs/Record.qml create mode 100644 Widgets/SettingsWindow/Tabs/Recording.qml create mode 100644 Widgets/SettingsWindow/Tabs/TimeWeather.qml create mode 100644 qmlls.ini diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 986bacc..8d355ba 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -4,7 +4,7 @@ import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Wayland -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import qs.Bar.Modules import qs.Settings import qs.Services @@ -15,6 +15,7 @@ import qs.Widgets.Sidebar import qs.Widgets.Sidebar.Panel import qs.Widgets.Notification +// Main bar component - creates panels on selected monitors with widgets and corners Scope { id: rootScope property var shell @@ -38,7 +39,8 @@ Scope { anchors.left: true anchors.right: true - visible: true + visible: Settings.settings.barMonitors.includes(modelData.name) || + (Settings.settings.barMonitors.length === 0) Rectangle { id: barBackground @@ -103,6 +105,14 @@ Scope { anchors.verticalCenter: parent.verticalCenter } + Wifi { + anchors.verticalCenter: parent.verticalCenter + } + + Bluetooth { + anchors.verticalCenter: parent.verticalCenter + } + Battery { id: widgetsBattery anchors.verticalCenter: parent.verticalCenter @@ -110,6 +120,7 @@ Scope { Brightness { id: widgetsBrightness + screen: modelData anchors.verticalCenter: parent.verticalCenter } @@ -124,6 +135,10 @@ Scope { anchors.verticalCenter: parent.verticalCenter } + SettingsButton { + anchors.verticalCenter: parent.verticalCenter + } + PanelPopup { id: sidebarPopup } @@ -149,7 +164,8 @@ Scope { screen: modelData margins.top: 36 WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: true + visible: Settings.settings.barMonitors.includes(modelData.name) || + (Settings.settings.barMonitors.length === 0) WlrLayershell.layer: WlrLayer.Background aboveWindows: false WlrLayershell.namespace: "swww-daemon" @@ -175,7 +191,8 @@ Scope { screen: modelData margins.top: 36 WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: true + visible: Settings.settings.barMonitors.includes(modelData.name) || + (Settings.settings.barMonitors.length === 0) WlrLayershell.layer: WlrLayer.Background aboveWindows: false WlrLayershell.namespace: "swww-daemon" @@ -201,7 +218,8 @@ Scope { color: "transparent" screen: modelData WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: true + visible: Settings.settings.barMonitors.includes(modelData.name) || + (Settings.settings.barMonitors.length === 0) WlrLayershell.layer: WlrLayer.Background aboveWindows: false WlrLayershell.namespace: "swww-daemon" @@ -227,7 +245,8 @@ Scope { color: "transparent" screen: modelData WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: true + visible: Settings.settings.barMonitors.includes(modelData.name) || + (Settings.settings.barMonitors.length === 0) WlrLayershell.layer: WlrLayer.Background aboveWindows: false WlrLayershell.namespace: "swww-daemon" @@ -249,6 +268,6 @@ Scope { } } - // This alias exposes the visual bar's visibility to the outside world + property alias visible: barRootItem.visible -} \ No newline at end of file +} diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index c01d58d..cd6ab68 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -1,9 +1,9 @@ import QtQuick import Quickshell -import qs.Components -import qs.Settings import Quickshell.Wayland import Quickshell.Widgets +import qs.Components +import qs.Settings PanelWindow { id: activeWindowPanel @@ -14,7 +14,7 @@ PanelWindow { anchors.right: true focusable: false margins.top: barHeight - visible: !activeWindowWrapper.finallyHidden + visible: Settings.settings.showActiveWindow && !activeWindowWrapper.finallyHidden implicitHeight: activeWindowTitleContainer.height implicitWidth: 0 property int barHeight: 36 @@ -121,6 +121,7 @@ PanelWindow { source: ToplevelManager?.activeToplevel ? getIcon() : "" visible: Settings.settings.showActiveWindowIcon anchors.verticalCenterOffset: -3 + } Text { diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index 4536c15..a02ee37 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -3,18 +3,159 @@ import QtQuick.Controls import QtQuick.Layouts import Quickshell import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Widgets import qs.Components import qs.Settings -import Quickshell.Wayland + import "../../Helpers/Fuzzysort.js" as Fuzzysort PanelWithOverlay { + Timer { + id: clipboardTimer + interval: 1000 + repeat: true + running: appLauncherPanel.visible && searchField.text.startsWith(">clip") + onTriggered: { + updateClipboardHistory(); + } + } + + property var clipboardHistory: [] + property bool clipboardInitialized: false + + Process { + id: clipboardTypeProcess + property bool isLoading: false + property var currentTypes: [] + + onExited: (exitCode, exitStatus) => { + if (exitCode === 0) { + currentTypes = String(stdout.text).trim().split('\n').filter(t => t); + + const imageType = currentTypes.find(t => t.startsWith('image/')); + if (imageType) { + clipboardImageProcess.mimeType = imageType; + clipboardImageProcess.command = ["sh", "-c", `wl-paste -n -t "${imageType}" | base64 -w 0`]; + clipboardImageProcess.running = true; + } else { + + clipboardHistoryProcess.command = ["wl-paste", "-n", "--type", "text/plain"]; + clipboardHistoryProcess.running = true; + } + } else { + + clipboardTypeProcess.isLoading = false; + } + } + + stdout: StdioCollector {} + } + + Process { + id: clipboardImageProcess + property string mimeType: "" + + onExited: (exitCode, exitStatus) => { + if (exitCode === 0) { + const base64 = stdout.text.trim(); + if (base64) { + const entry = { + type: 'image', + mimeType: mimeType, + data: `data:${mimeType};base64,${base64}`, + timestamp: new Date().getTime() + }; + + + const exists = clipboardHistory.find(item => + item.type === 'image' && item.data === entry.data + ); + + if (!exists) { + clipboardHistory = [entry, ...clipboardHistory].slice(0, 20); + root.updateFilter(); + } + } + } + + if (!clipboardHistoryProcess.isLoading) { + clipboardInitialized = true; + } + clipboardTypeProcess.isLoading = false; + } + + stdout: StdioCollector {} + } + + Process { + id: clipboardHistoryProcess + property bool isLoading: false + + onExited: (exitCode, exitStatus) => { + if (exitCode === 0) { + const content = String(stdout.text).trim(); + if (content && !content.startsWith("vscode-file://")) { + const entry = { + type: 'text', + content: content, + timestamp: new Date().getTime() + }; + + + const exists = clipboardHistory.find(item => { + if (item.type === 'text') { + return item.content === content; + } + + return item === content; + }); + + if (!exists) { + + const newHistory = clipboardHistory.map(item => { + if (typeof item === 'string') { + return { + type: 'text', + content: item, + timestamp: new Date().getTime() + }; + } + return item; + }); + + clipboardHistory = [entry, ...newHistory].slice(0, 20); + } + } + } else { + + clipboardHistoryProcess.isLoading = false; + } + clipboardInitialized = true; + clipboardTypeProcess.isLoading = false; + root.updateFilter(); + } + + stdout: StdioCollector {} + } + + + + function updateClipboardHistory() { + if (!clipboardTypeProcess.isLoading && !clipboardHistoryProcess.isLoading) { + clipboardTypeProcess.isLoading = true; + clipboardTypeProcess.command = ["wl-paste", "-l"]; + clipboardTypeProcess.running = true; + } + } + id: appLauncherPanel WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand function isPinned(app) { return app && app.execString && Settings.settings.pinnedExecs.indexOf(app.execString) !== -1; } + function togglePin(app) { if (!app || !app.execString) return; var arr = Settings.settings.pinnedExecs ? Settings.settings.pinnedExecs.slice() : []; @@ -101,9 +242,11 @@ PanelWithOverlay { appLauncherPanel.visible = false; } } + function isMathExpression(str) { return /^[-+*/().0-9\s]+$/.test(str); } + function safeEval(expr) { try { return Function('return (' + expr + ')')(); @@ -111,13 +254,114 @@ PanelWithOverlay { 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 (query === ">") { + results.push({ + isCommand: true, + name: ">calc", + content: "Calculator - evaluate mathematical expressions", + icon: "calculate", + execute: function() { + searchField.text = ">calc "; + searchField.cursorPosition = searchField.text.length; + } + }); + + results.push({ + isCommand: true, + name: ">clip", + content: "Clipboard history - browse and restore clipboard items", + icon: "content_paste", + execute: function() { + searchField.text = ">clip "; + searchField.cursorPosition = searchField.text.length; + } + }); + + root.filteredApps = results; + return; + } + + + if (query.startsWith(">clip")) { + if (!clipboardInitialized) { + updateClipboardHistory(); + } + const searchTerm = query.slice(5).trim(); + + clipboardHistory.forEach(function(clip, index) { + let searchContent = clip.type === 'image' ? + clip.mimeType : + clip.content || clip; // Support both new object format and old string format + + if (!searchTerm || searchContent.toLowerCase().includes(searchTerm)) { + let entry; + if (clip.type === 'image') { + entry = { + isClipboard: true, + name: "Image from " + new Date(clip.timestamp).toLocaleTimeString(), + content: "Image: " + clip.mimeType, + icon: "image", + type: 'image', + data: clip.data, + execute: function() { + // Convert base64 image data back to binary and copy to clipboard + const base64Data = clip.data.split(',')[1]; + clipboardTypeProcess.command = ["sh", "-c", `echo '${base64Data}' | base64 -d | wl-copy -t '${clip.mimeType}'`]; + clipboardTypeProcess.running = true; + } + }; + } else { + const textContent = clip.content || clip; // Support both new object format and old string format + let displayContent = textContent; + let previewContent = ""; + + // Clean up whitespace for display + displayContent = displayContent.replace(/\s+/g, ' ').trim(); + + // Truncate long content and show preview + if (displayContent.length > 50) { + previewContent = displayContent; + // Show first line or first 50 characters as title + displayContent = displayContent.split('\n')[0].substring(0, 50) + "..."; + } + + entry = { + isClipboard: true, + name: displayContent, + content: previewContent || textContent, + icon: "content_paste", + execute: function() { + Quickshell.execDetached(["sh", "-c", "echo -n '" + textContent.replace(/'/g, "'\\''") + "' | wl-copy"]); + } + }; + } + results.push(entry); + } + }); + + if (results.length === 0) { + results.push({ + isClipboard: true, + name: "No clipboard history", + content: "No matching clipboard entries found", + icon: "content_paste_off" + }); + } + + root.filteredApps = results; + return; + } + + + if (query.startsWith(">calc")) { + var expr = searchField.text.slice(5).trim(); if (expr && isMathExpression(expr)) { var value = safeEval(expr); if (value !== undefined && value !== null && value !== "") { @@ -130,8 +374,27 @@ PanelWithOverlay { }); } } + + + var pinned = []; + var unpinned = []; + for (var i = 0; i < results.length; ++i) { + var app = results[i]; + if (app.execString && Settings.settings.pinnedExecs.indexOf(app.execString) !== -1) { + pinned.push(app); + } else { + unpinned.push(app); + } + } + // Sort pinned apps alphabetically for consistent display + pinned.sort(function(a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + root.filteredApps = pinned.concat(unpinned); + root.selectedIndex = 0; + return; } - if (!query || query.startsWith("=")) { + if (!query) { results = results.concat(apps.sort(function (a, b) { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); })); @@ -143,7 +406,7 @@ PanelWithOverlay { return r.obj; })); } - // Pinning logic: split into pinned and unpinned + var pinned = []; var unpinned = []; for (var i = 0; i < results.length; ++i) { @@ -161,10 +424,12 @@ PanelWithOverlay { root.filteredApps = pinned.concat(unpinned); 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); @@ -182,6 +447,10 @@ PanelWithOverlay { Quickshell.clipboardText = String(modelData.result); Quickshell.execDetached(["notify-send", "Calculator Result", `${modelData.expr} = ${modelData.result} (copied to clipboard)`]); }); + } else if (modelData.isCommand) { + + modelData.execute(); + return; } else if (modelData.runInTerminal && termEmu){ Quickshell.execDetached([termEmu, "-e", modelData.execString.trim()]); } else if (modelData.execute) { @@ -200,310 +469,382 @@ PanelWithOverlay { Component.onCompleted: updateFilter() - ColumnLayout { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom + RowLayout { + anchors.fill: parent 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 + id: previewPanel + Layout.preferredWidth: 200 + Layout.fillHeight: true + color: Theme.surface + radius: 20 + visible: false - 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 + Rectangle { + anchors.fill: parent + anchors.margins: 16 + color: "transparent" + clip: true - Keys.onDownPressed: root.selectNext() - Keys.onUpPressed: root.selectPrev() - Keys.onEnterPressed: root.activateSelected() - Keys.onReturnPressed: root.activateSelected() - Keys.onEscapePressed: appLauncherPanel.hidePanel() - } - } - Behavior on border.color { - ColorAnimation { - duration: 120 - } - } - Behavior on border.width { - NumberAnimation { - duration: 120 + Image { + id: previewImage + anchors.fill: parent + fillMode: Image.PreserveAspectFit + asynchronous: true + cache: true + smooth: true } } } - // App List Card - Rectangle { - color: Theme.surface - radius: 20 + + ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true - clip: true - property int innerPadding: 16 + spacing: 18 - Item { - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - height: parent.innerPadding - visible: false + + Rectangle { + id: searchBar + color: Theme.surfaceVariant + radius: 20 + 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() + } + } + + Behavior on border.color { + ColorAnimation { + duration: 120 + } + } + + Behavior on border.width { + NumberAnimation { + duration: 120 + } + } } - 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 { + color: Theme.surface + radius: 20 + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + property int innerPadding: 16 - Rectangle { - anchors.fill: parent - color: (hovered || isSelected) - ? Theme.accentPrimary - : (appLauncherPanel.isPinned(modelData) ? Theme.surfaceVariant : "transparent") - radius: 12 - border.color: appLauncherPanel.isPinned(modelData) - ? "transparent" - : (hovered || isSelected ? Theme.accentPrimary : "transparent") - border.width: appLauncherPanel.isPinned(modelData) ? 0 : (hovered || isSelected ? 2 : 0) - Behavior on color { - ColorAnimation { - duration: 120 - } - } - Behavior on border.color { - ColorAnimation { - duration: 120 - } - } - Behavior on border.width { - NumberAnimation { - duration: 120 - } - } - } + 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: (modelData.isClipboard || modelData.isCommand) ? 64 : 48 + property bool hovered: mouseArea.containsMouse + property bool isSelected: index === root.selectedIndex - RowLayout { - anchors.fill: parent - anchors.leftMargin: 10 - anchors.rightMargin: 10 - spacing: 10 + Rectangle { + anchors.fill: parent + color: (hovered || isSelected) + ? Theme.accentPrimary + : (appLauncherPanel.isPinned(modelData) ? Theme.surfaceVariant : "transparent") + radius: 12 + border.color: appLauncherPanel.isPinned(modelData) + ? "transparent" + : (hovered || isSelected ? Theme.accentPrimary : "transparent") + border.width: appLauncherPanel.isPinned(modelData) ? 0 : (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 && !modelData.isClipboard && !modelData.isCommand && iconImg.status === Image.Ready && iconImg.source !== "" && iconImg.status !== Image.Error + + Image { + id: clipboardImage + anchors.fill: parent + visible: modelData.type === 'image' + source: modelData.data || "" + fillMode: Image.PreserveAspectCrop + asynchronous: true + cache: true + + MouseArea { + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + onContainsMouseChanged: { + if (containsMouse && modelData.type === 'image') { + previewImage.source = modelData.data; + previewPanel.visible = true; + } else { + previewPanel.visible = false; + } + } + onMouseXChanged: mouse.accepted = false + onMouseYChanged: mouse.accepted = false + onClicked: mouse.accepted = false + } + } + + IconImage { + id: iconImg + anchors.fill: parent + asynchronous: true + source: modelData.isCalculator ? "qrc:/icons/calculate.svg" : + modelData.isClipboard ? "qrc:/icons/" + modelData.icon + ".svg" : + modelData.isCommand ? "qrc:/icons/" + modelData.icon + ".svg" : + Quickshell.iconPath(modelData.icon, "application-x-executable") + visible: (modelData.isCalculator || modelData.isClipboard || modelData.isCommand || parent.iconLoaded) && modelData.type !== 'image' + } + + Text { + anchors.centerIn: parent + visible: !modelData.isCalculator && !modelData.isClipboard && !modelData.isCommand && !parent.iconLoaded && modelData.type !== 'image' + 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 : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : 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.isClipboard ? modelData.content : + modelData.isCommand ? modelData.content : + (modelData.comment || modelData.genericName || "No description available") + color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textSecondary : Theme.textSecondary) + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeCaption + font.italic: !(modelData.comment || modelData.genericName) + opacity: modelData.isClipboard ? 0.8 : modelData.isCommand ? 0.9 : ((modelData.comment || modelData.genericName) ? 1.0 : 0.6) + elide: Text.ElideRight + maximumLineCount: (modelData.isClipboard || modelData.isCommand) ? 2 : 1 + wrapMode: (modelData.isClipboard || modelData.isCommand) ? Text.WordWrap : Text.NoWrap + Layout.fillWidth: true + Layout.preferredHeight: (modelData.isClipboard || modelData.isCommand) ? implicitHeight : contentHeight + } + } + + 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 + : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textSecondary) + verticalAlignment: Text.AlignVCenter + Layout.rightMargin: 8 + } + + + Item { width: 8; height: 1 } + } + + Rectangle { + id: ripple + anchors.fill: parent + color: Theme.onAccent + opacity: 0.0 + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + + if (pinArea.containsMouse) return; + if (mouse.button === Qt.RightButton) { + appLauncherPanel.togglePin(modelData); + return; + } + ripple.opacity = 0.18; + rippleNumberAnimation.start(); + root.selectedIndex = index; + root.activateSelected(); + } + cursorShape: Qt.PointingHandCursor + 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 + } + + Item { - width: 28 - height: 28 - property bool iconLoaded: !modelData.isCalculator && iconImg.status === Image.Ready && iconImg.source !== "" && iconImg.status !== Image.Error - Image { - id: iconImg + id: pinArea + width: 28; height: 28 + z: 100 + anchors.right: parent.right + anchors.verticalCenter: parent.verticalCenter + + MouseArea { 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 + preventStealing: true + z: 100 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton | Qt.RightButton + propagateComposedEvents: false + onClicked: { + appLauncherPanel.togglePin(modelData); + event.accepted = true; + } } + Text { anchors.centerIn: parent - visible: !modelData.isCalculator && !parent.iconLoaded - text: "broken_image" + text: "star" 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 : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textPrimary) - font.family: Theme.fontFamily font.pixelSize: Theme.fontSizeSmall - font.bold: hovered || isSelected + color: (parent.MouseArea.containsMouse || hovered || isSelected) + ? Theme.onAccent + : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textDisabled) 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 : (appLauncherPanel.isPinned(modelData) ? Theme.textSecondary : 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 - : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textSecondary) - verticalAlignment: Text.AlignVCenter - Layout.rightMargin: 8 // Add margin to separate from star - } - // Add a spacing item between chevron and star - Item { width: 8; height: 1 } - } - - Rectangle { - id: ripple - anchors.fill: parent - color: Theme.onAccent - opacity: 0.0 - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - // Prevent app launch if click is inside pinArea - if (pinArea.containsMouse) return; - if (mouse.button === Qt.RightButton) { - appLauncherPanel.togglePin(modelData); - return; - } - ripple.opacity = 0.18; - rippleNumberAnimation.start(); - root.selectedIndex = index; - root.activateSelected(); - } - cursorShape: Qt.PointingHandCursor - 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 - } - // Pin/Unpin button (move to last child for stacking) - Item { - id: pinArea - width: 28; height: 28 - z: 100 // Ensure above everything else - anchors.right: parent.right - anchors.verticalCenter: parent.verticalCenter - MouseArea { - anchors.fill: parent - preventStealing: true - z: 100 - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - acceptedButtons: Qt.LeftButton | Qt.RightButton - propagateComposedEvents: false - onClicked: { - appLauncherPanel.togglePin(modelData); - event.accepted = true; - } - } - Text { - anchors.centerIn: parent - text: "star" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeSmall - color: (parent.MouseArea.containsMouse || hovered || isSelected) - ? Theme.onAccent - : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textDisabled) - verticalAlignment: Text.AlignVCenter } } } } } } - } - Corners { - id: launcherCornerRight - position: "bottomleft" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: root.top - offsetX: 416 - 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: -416 - 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/Battery.qml b/Bar/Modules/Battery.qml index af41d66..03af9c3 100644 --- a/Bar/Modules/Battery.qml +++ b/Bar/Modules/Battery.qml @@ -4,6 +4,7 @@ import Quickshell.Services.UPower import QtQuick.Layouts import qs.Components import qs.Settings +import "../../Helpers/Time.js" as Time Item { id: batteryWidget @@ -73,19 +74,40 @@ Item { } StyledTooltip { id: batteryTooltip + positionAbove: false text: { let lines = []; - if (batteryWidget.isReady) { + if (!batteryWidget.isReady) { + return ""; + } + + if (batteryWidget.battery.timeToEmpty > 0) { + lines.push("Time left: " + Time.formatVagueHumanReadableTime(batteryWidget.battery.timeToEmpty)); + } + + if (batteryWidget.battery.timeToFull > 0) { + lines.push("Time until full: " + Time.formatVagueHumanReadableTime(batteryWidget.battery.timeToFull)); + } + + if (batteryWidget.battery.changeRate !== undefined) { + const rate = batteryWidget.battery.changeRate; + if (rate > 0) { + lines.push(batteryWidget.charging ? "Charging rate: " + rate.toFixed(2) + " W" : "Discharging rate: " + rate.toFixed(2) + " W"); + } + else if (rate < 0) { + lines.push("Discharging rate: " + Math.abs(rate).toFixed(2) + " W"); + } + else { + lines.push("Estimating..."); + } + } + else { lines.push(batteryWidget.charging ? "Charging" : "Discharging"); - lines.push(Math.round(batteryWidget.percent) + "%"); - if (batteryWidget.battery.changeRate !== undefined) - lines.push("Rate: " + batteryWidget.battery.changeRate.toFixed(2) + " W"); - if (batteryWidget.battery.timeToEmpty > 0) - lines.push("Time left: " + Math.floor(batteryWidget.battery.timeToEmpty / 60) + " min"); - if (batteryWidget.battery.timeToFull > 0) - lines.push("Time to full: " + Math.floor(batteryWidget.battery.timeToFull / 60) + " min"); - if (batteryWidget.battery.healthPercentage !== undefined) - lines.push("Health: " + Math.round(batteryWidget.battery.healthPercentage) + "%"); + } + + + if (batteryWidget.battery.healthPercentage !== undefined && batteryWidget.battery.healthPercentage > 0) { + lines.push("Health: " + Math.round(batteryWidget.battery.healthPercentage) + "%"); } return lines.join("\n"); } diff --git a/Bar/Modules/Bluetooth.qml b/Bar/Modules/Bluetooth.qml new file mode 100644 index 0000000..80beb61 --- /dev/null +++ b/Bar/Modules/Bluetooth.qml @@ -0,0 +1,272 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import Quickshell.Bluetooth +import qs.Settings +import qs.Components + +Item { + id: root + width: Settings.settings.bluetoothEnabled ? 22 : 0 + height: Settings.settings.bluetoothEnabled ? 22 : 0 + + property bool menuVisible: false + + // Bluetooth icon/button + Item { + id: bluetoothIcon + width: 22; height: 22 + visible: Settings.settings.bluetoothEnabled + + // Check if any devices are currently connected + property bool hasConnectedDevices: { + if (!Bluetooth.defaultAdapter) return false; + + for (let i = 0; i < Bluetooth.defaultAdapter.devices.count; i++) { + if (Bluetooth.defaultAdapter.devices.valueAt(i).connected) { + return true; + } + } + return false; + } + + Text { + id: bluetoothText + anchors.centerIn: parent + text: { + if (!Bluetooth.defaultAdapter || !Bluetooth.defaultAdapter.enabled) { + return "bluetooth_disabled" + } else if (parent.hasConnectedDevices) { + return "bluetooth_connected" + } else { + return "bluetooth" + } + } + font.family: mouseAreaBluetooth.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 16 + color: mouseAreaBluetooth.containsMouse ? Theme.accentPrimary : Theme.textPrimary + } + + MouseArea { + id: mouseAreaBluetooth + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + bluetoothMenu.visible = !bluetoothMenu.visible; + // Enable adapter and start discovery when menu opens + if (bluetoothMenu.visible && Bluetooth.defaultAdapter) { + if (!Bluetooth.defaultAdapter.enabled) { + Bluetooth.defaultAdapter.enabled = true; + } + if (!Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = true; + } + } + } + onEntered: bluetoothTooltip.tooltipVisible = true + onExited: bluetoothTooltip.tooltipVisible = false + } + } + + StyledTooltip { + id: bluetoothTooltip + text: "Bluetooth Devices" + positionAbove: false + tooltipVisible: false + targetItem: bluetoothIcon + delay: 200 + } + + PanelWindow { + id: bluetoothMenu + implicitWidth: 320 + implicitHeight: 480 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + + onVisibleChanged: { + // Stop discovery when menu closes to save battery + if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = false; + } + } + + Rectangle { + anchors.fill: parent + color: Theme.backgroundPrimary + radius: 12 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 16 + + RowLayout { + Layout.fillWidth: true + spacing: 12 + + Text { + text: "bluetooth" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.accentPrimary + } + + Text { + text: "Bluetooth Devices" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + + IconButton { + icon: "close" + onClicked: { + bluetoothMenu.visible = false; + if (Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = false; + } + } + } + } + + Rectangle { + Layout.fillWidth: true + height: 1 + color: Theme.outline + opacity: 0.12 + } + + ListView { + id: deviceList + Layout.fillWidth: true + Layout.fillHeight: true + model: Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.devices : [] + spacing: 8 + clip: true + + delegate: Item { + width: parent.width + height: 48 + + Rectangle { + anchors.fill: parent + radius: 8 + color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (deviceMouseArea.containsMouse ? Theme.highlight : "transparent") + + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 + + Text { + text: modelData.connected ? "bluetooth" : "bluetooth_disabled" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + + Text { + text: { + let deviceName = modelData.name || modelData.deviceName || "Unknown Device"; + // Hide MAC addresses and show "Unknown Device" instead + let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; + if (macPattern.test(deviceName)) { + return "Unknown Device"; + } + return deviceName; + } + color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) + font.pixelSize: 14 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: { + let deviceName = modelData.name || modelData.deviceName || ""; + let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; + if (macPattern.test(deviceName)) { + // Show MAC address in subtitle for unnamed devices + return modelData.address + " • " + (modelData.paired ? "Paired" : "Available"); + } else { + // Show only status for named devices + return modelData.paired ? "Paired" : "Available"; + } + } + color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + Item { + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + visible: modelData.pairing || modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting + + Spinner { + visible: parent.visible + running: parent.visible + color: Theme.accentPrimary + anchors.centerIn: parent + size: 22 + } + } + } + + MouseArea { + id: deviceMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + // Handle device actions: disconnect, pair, or connect + if (modelData.connected) { + modelData.disconnect(); + } else if (!modelData.paired) { + modelData.pair(); + } else { + modelData.connect(); + } + } + } + } + } + } + + // Discovering indicator + RowLayout { + Layout.fillWidth: true + spacing: 8 + visible: Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering + + Text { + text: "Scanning for devices..." + font.pixelSize: 12 + color: Theme.textSecondary + } + + Spinner { + running: true + color: Theme.accentPrimary + size: 16 + } + } + } + } + } +} \ No newline at end of file diff --git a/Bar/Modules/Brightness.qml b/Bar/Modules/Brightness.qml index be04cfd..6ce1158 100644 --- a/Bar/Modules/Brightness.qml +++ b/Bar/Modules/Brightness.qml @@ -128,6 +128,7 @@ Item { StyledTooltip { id: brightnessTooltip text: "Brightness: " + brightness + "%" + positionAbove: false tooltipVisible: false targetItem: pill delay: 1500 diff --git a/Bar/Modules/ClockWidget.qml b/Bar/Modules/ClockWidget.qml index f899d0d..eba2afb 100644 --- a/Bar/Modules/ClockWidget.qml +++ b/Bar/Modules/ClockWidget.qml @@ -41,6 +41,7 @@ Rectangle { StyledTooltip { id: dateTooltip text: Time.dateString + positionAbove: false tooltipVisible: showTooltip && !calendar.visible targetItem: clockWidget delay: 200 diff --git a/Bar/Modules/CustomTrayMenu.qml b/Bar/Modules/CustomTrayMenu.qml index 5366d7f..a64ac7b 100644 --- a/Bar/Modules/CustomTrayMenu.qml +++ b/Bar/Modules/CustomTrayMenu.qml @@ -1,7 +1,7 @@ pragma ComponentBehavior: Bound -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 import Quickshell import qs.Settings @@ -21,120 +21,456 @@ PopupWindow { anchor.rect.x: anchorX anchor.rect.y: anchorY - 4 + // Recursive function to destroy all open submenus in delegate tree, safely avoiding infinite recursion + function destroySubmenusRecursively(item) { + if (!item || !item.contentItem) return; + var children = item.contentItem.children; + for (var i = 0; i < children.length; ++i) { + var child = children[i]; + if (child.subMenu) { + child.subMenu.hideMenu(); + child.subMenu.destroy(); + child.subMenu = null; + } + // Recursively destroy submenus only if the child has contentItem to prevent issues + if (child.contentItem) { + destroySubmenusRecursively(child); + } + } + } + function showAt(item, x, y) { if (!item) { - console.warn("CustomTrayMenu: anchorItem is undefined, not showing menu."); + console.warn("CustomTrayMenu: anchorItem is undefined, won't show menu."); return; } - anchorItem = item - anchorX = x - anchorY = y - visible = true - forceActiveFocus() - Qt.callLater(() => trayMenu.anchor.updateAnchor()) + anchorItem = item; + anchorX = x; + anchorY = y; + visible = true; + forceActiveFocus(); + Qt.callLater(() => trayMenu.anchor.updateAnchor()); } function hideMenu() { - visible = false + visible = false; + destroySubmenusRecursively(listView); } Item { - anchors.fill: parent - Keys.onEscapePressed: trayMenu.hideMenu() + anchors.fill: parent; + Keys.onEscapePressed: trayMenu.hideMenu(); } QsMenuOpener { - id: opener - menu: trayMenu.menu + id: opener; + menu: trayMenu.menu; } Rectangle { - id: bg - anchors.fill: parent - color: Theme.surfaceVariant || "#222" - border.color: Theme.outline || "#444" - border.width: 1 - radius: 12 - z: 0 + id: bg; + anchors.fill: parent; + color: Theme.backgroundPrimary || "#222"; + border.color: Theme.outline || "#444"; + border.width: 1; + radius: 12; + z: 0; } ListView { - id: listView - anchors.fill: parent - anchors.margins: 6 - spacing: 2 - interactive: false - enabled: trayMenu.visible - clip: true + id: listView; + anchors.fill: parent; + anchors.margins: 6; + spacing: 2; + interactive: false; + enabled: trayMenu.visible; + clip: true; model: ScriptModel { values: opener.children ? [...opener.children.values] : [] } delegate: Rectangle { - id: entry - required property var modelData + id: entry; + required property var modelData; - width: listView.width - height: (modelData?.isSeparator) ? 8 : 32 - color: "transparent" - radius: 12 + width: listView.width; + height: (modelData?.isSeparator) ? 8 : 32; + color: "transparent"; + radius: 12; + + property var subMenu: null; Rectangle { - anchors.centerIn: parent - width: parent.width - 20 - height: 1 - color: Qt.darker(Theme.surfaceVariant || "#222", 1.4) - visible: modelData?.isSeparator ?? false + anchors.centerIn: parent; + width: parent.width - 20; + height: 1; + color: Qt.darker(Theme.backgroundPrimary || "#222", 1.4); + visible: modelData?.isSeparator ?? false; } Rectangle { - id: bg - anchors.fill: parent - color: mouseArea.containsMouse ? Theme.highlight : "transparent" - radius: 8 - visible: !(modelData?.isSeparator ?? false) - property color hoverTextColor: mouseArea.containsMouse ? Theme.onAccent : Theme.textPrimary + id: bg; + anchors.fill: parent; + color: mouseArea.containsMouse ? Theme.highlight : "transparent"; + radius: 8; + visible: !(modelData?.isSeparator ?? false); + property color hoverTextColor: mouseArea.containsMouse ? Theme.onAccent : Theme.textPrimary; RowLayout { - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - spacing: 8 + anchors.fill: parent; + anchors.leftMargin: 12; + anchors.rightMargin: 12; + spacing: 8; Text { - Layout.fillWidth: true - color: (modelData?.enabled ?? true) ? bg.hoverTextColor : Theme.textDisabled - text: modelData?.text ?? "" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight + Layout.fillWidth: true; + color: (modelData?.enabled ?? true) ? bg.hoverTextColor : Theme.textDisabled; + text: modelData?.text ?? ""; + font.family: Theme.fontFamily; + font.pixelSize: Theme.fontSizeSmall; + verticalAlignment: Text.AlignVCenter; + elide: Text.ElideRight; } Image { - Layout.preferredWidth: 16 - Layout.preferredHeight: 16 - source: modelData?.icon ?? "" - visible: (modelData?.icon ?? "") !== "" - fillMode: Image.PreserveAspectFit + Layout.preferredWidth: 16; + Layout.preferredHeight: 16; + source: modelData?.icon ?? ""; + visible: (modelData?.icon ?? "") !== ""; + fillMode: Image.PreserveAspectFit; + } + + Text { + // Material Symbols Outlined chevron right for submenu + text: modelData?.hasChildren ? "menu" : ""; + font.family: "Material Symbols Outlined"; + font.pixelSize: 18; + verticalAlignment: Text.AlignVCenter; + visible: modelData?.hasChildren ?? false; + color: Theme.textPrimary; } } MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - enabled: (modelData?.enabled ?? true) && !(modelData?.isSeparator ?? false) && trayMenu.visible + id: mouseArea; + anchors.fill: parent; + hoverEnabled: true; + enabled: (modelData?.enabled ?? true) && !(modelData?.isSeparator ?? false) && trayMenu.visible; onClicked: { if (modelData && !modelData.isSeparator) { - modelData.triggered() - trayMenu.hideMenu() + if (modelData.hasChildren) { + // Submenus open on hover; ignore click here + return; + } + modelData.triggered(); + trayMenu.hideMenu(); + } + } + + onEntered: { + if (!trayMenu.visible) return; + + if (modelData?.hasChildren) { + // Close sibling submenus immediately + for (let i = 0; i < listView.contentItem.children.length; i++) { + const sibling = listView.contentItem.children[i]; + if (sibling !== entry && sibling.subMenu) { + sibling.subMenu.hideMenu(); + sibling.subMenu.destroy(); + sibling.subMenu = null; + } + } + if (entry.subMenu) { + entry.subMenu.hideMenu(); + entry.subMenu.destroy(); + entry.subMenu = null; + } + var globalPos = entry.mapToGlobal(0, 0); + var submenuWidth = 180; + var gap = 12; + var openLeft = (globalPos.x + entry.width + submenuWidth > Screen.width); + var anchorX = openLeft ? -submenuWidth - gap : entry.width + gap; + + entry.subMenu = subMenuComponent.createObject(trayMenu, { + menu: modelData, + anchorItem: entry, + anchorX: anchorX, + anchorY: 0 + }); + entry.subMenu.showAt(entry, anchorX, 0); + } else { + // Hovered item without submenu; close siblings + for (let i = 0; i < listView.contentItem.children.length; i++) { + const sibling = listView.contentItem.children[i]; + if (sibling.subMenu) { + sibling.subMenu.hideMenu(); + sibling.subMenu.destroy(); + sibling.subMenu = null; + } + } + if (entry.subMenu) { + entry.subMenu.hideMenu(); + entry.subMenu.destroy(); + entry.subMenu = null; + } + } + } + + onExited: { + if (entry.subMenu && !entry.subMenu.containsMouse()) { + entry.subMenu.hideMenu(); + entry.subMenu.destroy(); + entry.subMenu = null; + } + } + } + } + + // Simplified containsMouse without recursive calls to avoid stack overflow + function containsMouse() { + return mouseArea.containsMouse; + } + + Component.onDestruction: { + if (subMenu) { + subMenu.destroy(); + subMenu = null; + } + } + } + } + + Component { + id: subMenuComponent; + + PopupWindow { + id: subMenu; + implicitWidth: 180; + implicitHeight: Math.max(40, listView.contentHeight + 12); + visible: false; + color: "transparent"; + + property QsMenuHandle menu; + property var anchorItem: null; + property real anchorX; + property real anchorY; + + anchor.item: anchorItem ? anchorItem : null; + anchor.rect.x: anchorX; + anchor.rect.y: anchorY; + + function showAt(item, x, y) { + if (!item) { + console.warn("subMenuComponent: anchorItem is undefined, not showing menu."); + return; + } + anchorItem = item; + anchorX = x; + anchorY = y; + visible = true; + Qt.callLater(() => subMenu.anchor.updateAnchor()); + } + + function hideMenu() { + visible = false; + // Close all submenus recursively in this submenu + for (let i = 0; i < listView.contentItem.children.length; i++) { + const child = listView.contentItem.children[i]; + if (child.subMenu) { + child.subMenu.hideMenu(); + child.subMenu.destroy(); + child.subMenu = null; + } + } + } + + // Simplified containsMouse avoiding recursive calls + function containsMouse() { + return subMenu.containsMouse; + } + + Item { + anchors.fill: parent; + Keys.onEscapePressed: subMenu.hideMenu(); + } + + QsMenuOpener { + id: opener; + menu: subMenu.menu; + } + + Rectangle { + id: bg; + anchors.fill: parent; + color: Theme.backgroundPrimary || "#222"; + border.color: Theme.outline || "#444"; + border.width: 1; + radius: 12; + z: 0; + } + + ListView { + id: listView; + anchors.fill: parent; + anchors.margins: 6; + spacing: 2; + interactive: false; + enabled: subMenu.visible; + clip: true; + + model: ScriptModel { + values: opener.children ? [...opener.children.values] : []; + } + + delegate: Rectangle { + id: entry; + required property var modelData; + + width: listView.width; + height: (modelData?.isSeparator) ? 8 : 32; + color: "transparent"; + radius: 12; + + property var subMenu: null; + + Rectangle { + anchors.centerIn: parent; + width: parent.width - 20; + height: 1; + color: Qt.darker(Theme.surfaceVariant || "#222", 1.4); + visible: modelData?.isSeparator ?? false; + } + + Rectangle { + id: bg; + anchors.fill: parent; + color: mouseArea.containsMouse ? Theme.highlight : "transparent"; + radius: 8; + visible: !(modelData?.isSeparator ?? false); + property color hoverTextColor: mouseArea.containsMouse ? Theme.onAccent : Theme.textPrimary; + + RowLayout { + anchors.fill: parent; + anchors.leftMargin: 12; + anchors.rightMargin: 12; + spacing: 8; + + Text { + Layout.fillWidth: true; + color: (modelData?.enabled ?? true) ? bg.hoverTextColor : Theme.textDisabled; + text: modelData?.text ?? ""; + font.family: Theme.fontFamily; + font.pixelSize: Theme.fontSizeSmall; + verticalAlignment: Text.AlignVCenter; + elide: Text.ElideRight; + } + + Image { + Layout.preferredWidth: 16; + Layout.preferredHeight: 16; + source: modelData?.icon ?? ""; + visible: (modelData?.icon ?? "") !== ""; + fillMode: Image.PreserveAspectFit; + } + + Text { + text: modelData?.hasChildren ? "\uE5CC" : ""; + font.family: "Material Symbols Outlined"; + font.pixelSize: 18; + verticalAlignment: Text.AlignVCenter; + visible: modelData?.hasChildren ?? false; + color: Theme.textPrimary; + } + } + + MouseArea { + id: mouseArea; + anchors.fill: parent; + hoverEnabled: true; + enabled: (modelData?.enabled ?? true) && !(modelData?.isSeparator ?? false) && subMenu.visible; + + onClicked: { + if (modelData && !modelData.isSeparator) { + if (modelData.hasChildren) { + return; + } + modelData.triggered(); + trayMenu.hideMenu(); + } + } + + onEntered: { + if (!subMenu.visible) return; + + if (modelData?.hasChildren) { + for (let i = 0; i < listView.contentItem.children.length; i++) { + const sibling = listView.contentItem.children[i]; + if (sibling !== entry && sibling.subMenu) { + sibling.subMenu.hideMenu(); + sibling.subMenu.destroy(); + sibling.subMenu = null; + } + } + if (entry.subMenu) { + entry.subMenu.hideMenu(); + entry.subMenu.destroy(); + entry.subMenu = null; + } + var globalPos = entry.mapToGlobal(0, 0); + var submenuWidth = 180; + var gap = 12; + var openLeft = (globalPos.x + entry.width + submenuWidth > Screen.width); + var anchorX = openLeft ? -submenuWidth - gap : entry.width + gap; + + entry.subMenu = subMenuComponent.createObject(subMenu, { + menu: modelData, + anchorItem: entry, + anchorX: anchorX, + anchorY: 0 + }); + entry.subMenu.showAt(entry, anchorX, 0); + } else { + for (let i = 0; i < listView.contentItem.children.length; i++) { + const sibling = listView.contentItem.children[i]; + if (sibling.subMenu) { + sibling.subMenu.hideMenu(); + sibling.subMenu.destroy(); + sibling.subMenu = null; + } + } + if (entry.subMenu) { + entry.subMenu.hideMenu(); + entry.subMenu.destroy(); + entry.subMenu = null; + } + } + } + + onExited: { + if (entry.subMenu && !entry.subMenu.containsMouse()) { + entry.subMenu.hideMenu(); + entry.subMenu.destroy(); + entry.subMenu = null; + } + } + } + } + + // Simplified & safe containsMouse avoiding recursion + function containsMouse() { + return mouseArea.containsMouse; + } + + Component.onDestruction: { + if (subMenu) { + subMenu.destroy(); + subMenu = null; } } } } } } -} \ No newline at end of file +} diff --git a/Bar/Modules/Media.qml b/Bar/Modules/Media.qml index ca30cd6..f2dbd1f 100644 --- a/Bar/Modules/Media.qml +++ b/Bar/Modules/Media.qml @@ -1,7 +1,8 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import Qt5Compat.GraphicalEffects +import Quickshell.Widgets +import QtQuick.Effects import qs.Settings import qs.Services import qs.Components @@ -54,17 +55,16 @@ Item { anchors.margins: 1 fillMode: Image.PreserveAspectCrop smooth: true + mipmap: true cache: false asynchronous: true - sourceSize.width: 24 - sourceSize.height: 24 source: MusicManager.trackArtUrl visible: source.toString() !== "" // Rounded corners using layer layer.enabled: true - layer.effect: OpacityMask { - cached: true + layer.effect: MultiEffect { + maskEnabled: true maskSource: Rectangle { width: albumArt.width height: albumArt.height diff --git a/Bar/Modules/SettingsButton.qml b/Bar/Modules/SettingsButton.qml new file mode 100644 index 0000000..7a9bede --- /dev/null +++ b/Bar/Modules/SettingsButton.qml @@ -0,0 +1,80 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components +import qs.Widgets.SettingsWindow + +Item { + id: root + width: 22 + height: 22 + + property var settingsWindow: null + + Rectangle { + id: button + anchors.fill: parent + color: "transparent" + radius: width / 2 + + Text { + anchors.centerIn: parent + text: "settings" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 + color: mouseArea.containsMouse ? Theme.accentPrimary : Theme.textPrimary + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { + if (!settingsWindow) { + // Create new window + settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues + if (settingsWindow) { + settingsWindow.visible = true; + // Handle window closure + settingsWindow.visibleChanged.connect(function() { + if (settingsWindow && !settingsWindow.visible) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + }); + } + } else if (settingsWindow.visible) { + // Close and destroy window + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } + } + } + + StyledTooltip { + text: "Settings" + targetItem: mouseArea + tooltipVisible: mouseArea.containsMouse + } + } + + Component { + id: settingsComponent + SettingsWindow {} + } + + // Clean up on destruction + Component.onDestruction: { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + } +} \ No newline at end of file diff --git a/Bar/Modules/SystemInfo.qml b/Bar/Modules/SystemInfo.qml index 07c7144..110deb1 100644 --- a/Bar/Modules/SystemInfo.qml +++ b/Bar/Modules/SystemInfo.qml @@ -8,6 +8,8 @@ Row { spacing: 10 visible: Settings.settings.showSystemInfoInBar + width: Math.floor(cpuUsageLayout.width + cpuTempLayout.width + memoryUsageLayout.width + (2 * 10)) + Row { id: cpuUsageLayout spacing: 6 diff --git a/Bar/Modules/SystemTray.qml b/Bar/Modules/SystemTray.qml index 90d30fb..7db68bf 100644 --- a/Bar/Modules/SystemTray.qml +++ b/Bar/Modules/SystemTray.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls import Quickshell -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell.Services.SystemTray import Quickshell.Widgets import qs.Settings @@ -14,10 +14,10 @@ Row { property var trayMenu spacing: 8 Layout.alignment: Qt.AlignVCenter - + property bool containsMouse: false property var systemTray: SystemTray - + Repeater { model: systemTray.items delegate: Item { @@ -26,7 +26,7 @@ Row { // Hide Spotify icon, or adjust to your liking visible: modelData && modelData.id !== "spotify" property bool isHovered: trayMouseArea.containsMouse - + // Hover scale animation scale: isHovered ? 1.15 : 1.0 Behavior on scale { @@ -35,7 +35,7 @@ Row { easing.type: Easing.OutCubic } } - + // Subtle rotation on hover rotation: isHovered ? 5 : 0 Behavior on rotation { @@ -44,7 +44,7 @@ Row { easing.type: Easing.OutCubic } } - + Rectangle { anchors.centerIn: parent width: 16 @@ -63,7 +63,8 @@ Row { backer.fillMode: Image.PreserveAspectFit source: { let icon = modelData?.icon || ""; - if (!icon) return ""; + if (!icon) + return ""; // Process icon path if (icon.includes("?path=")) { const [name, path] = icon.split("?path="); @@ -80,72 +81,71 @@ Row { easing.type: Easing.OutCubic } } - Component.onCompleted: { - - } + Component.onCompleted: {} } } - + MouseArea { id: trayMouseArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton - onClicked: (mouse) => { - if (!modelData) return; - + onClicked: mouse => { + if (!modelData) + return; + if (mouse.button === Qt.LeftButton) { // Close any open menu first if (trayMenu && trayMenu.visible) { - trayMenu.hideMenu() + trayMenu.hideMenu(); } - + if (!modelData.onlyMenu) { - modelData.activate() + modelData.activate(); } } else if (mouse.button === Qt.MiddleButton) { // Close any open menu first if (trayMenu && trayMenu.visible) { - trayMenu.hideMenu() + trayMenu.hideMenu(); } - - modelData.secondaryActivate && modelData.secondaryActivate() + + modelData.secondaryActivate && modelData.secondaryActivate(); } else if (mouse.button === Qt.RightButton) { - trayTooltip.tooltipVisible = false - console.log("Right click on", modelData.id, "hasMenu:", modelData.hasMenu, "menu:", modelData.menu) + trayTooltip.tooltipVisible = false; // If menu is already visible, close it if (trayMenu && trayMenu.visible) { - trayMenu.hideMenu() - return + trayMenu.hideMenu(); + return; } - + if (modelData.hasMenu && modelData.menu && trayMenu) { // Anchor the menu to the tray icon item (parent) and position it below the icon const menuX = (width / 2) - (trayMenu.width / 2); const menuY = height + 20; trayMenu.menu = modelData.menu; trayMenu.showAt(parent, menuX, menuY); - } else { - // console.log("No menu available for", modelData.id, "or trayMenu not set") - } + } else + // console.log("No menu available for", modelData.id, "or trayMenu not set") + {} } } onEntered: trayTooltip.tooltipVisible = true onExited: trayTooltip.tooltipVisible = false } - + StyledTooltip { id: trayTooltip text: modelData.tooltipTitle || modelData.name || modelData.id || "Tray Item" + positionAbove: false tooltipVisible: false targetItem: trayIcon delay: 200 } - - Component.onDestruction: { - // No cache cleanup needed - } + + Component.onDestruction: + // No cache cleanup needed + {} } } -} \ No newline at end of file +} diff --git a/Bar/Modules/Taskbar.qml b/Bar/Modules/Taskbar.qml index 46fd78b..792babc 100644 --- a/Bar/Modules/Taskbar.qml +++ b/Bar/Modules/Taskbar.qml @@ -15,6 +15,7 @@ Item { // Attach custom tooltip StyledTooltip { id: styledTooltip + positionAbove: false } function getAppIcon(toplevel: Toplevel): string { @@ -74,7 +75,6 @@ Item { height: Math.max(12, Settings.settings.taskbarIconSize * 0.625) anchors.centerIn: parent source: getAppIcon(modelData) - smooth: true visible: source.toString() !== "" } @@ -95,7 +95,8 @@ Item { cursorShape: Qt.PointingHandCursor onEntered: { - styledTooltip.text = appTitle || appId; + var text = appTitle || appId; + styledTooltip.text = text.length > 60 ? text.substring(0, 60) + "..." : text; styledTooltip.targetItem = appButton; styledTooltip.tooltipVisible = true; } diff --git a/Bar/Modules/Volume.qml b/Bar/Modules/Volume.qml index f6fb87e..36e2be6 100644 --- a/Bar/Modules/Volume.qml +++ b/Bar/Modules/Volume.qml @@ -47,7 +47,8 @@ Item { StyledTooltip { id: volumeTooltip - text: "Volume: " + volume + "%\nScroll up/down to change volume.\nLeft click to open the input/output selection." + text: "Volume: " + volume + "%\nLeft click for advanced settings.\nScroll up/down to change volume." + positionAbove: false tooltipVisible: !ioSelector.visible && volumeDisplay.containsMouse targetItem: pillIndicator delay: 1500 diff --git a/Bar/Modules/Wifi.qml b/Bar/Modules/Wifi.qml new file mode 100644 index 0000000..5c75ba1 --- /dev/null +++ b/Bar/Modules/Wifi.qml @@ -0,0 +1,370 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import qs.Settings +import qs.Components +import qs.Services + +Item { + id: root + width: Settings.settings.wifiEnabled ? 22 : 0 + height: Settings.settings.wifiEnabled ? 22 : 0 + + property bool menuVisible: false + property string passwordPromptSsid: "" + property string passwordInput: "" + property bool showPasswordPrompt: false + + Network { + id: network + } + + // WiFi icon/button + Item { + id: wifiIcon + width: 22; height: 22 + visible: Settings.settings.wifiEnabled + + property int currentSignal: { + let maxSignal = 0; + for (const net in network.networks) { + if (network.networks[net].connected && network.networks[net].signal > maxSignal) { + maxSignal = network.networks[net].signal; + } + } + return maxSignal; + } + + Text { + id: wifiText + anchors.centerIn: parent + text: { + let connected = false; + for (const net in network.networks) { + if (network.networks[net].connected) { + connected = true; + break; + } + } + return connected ? network.signalIcon(parent.currentSignal) : "wifi_off" + } + font.family: mouseAreaWifi.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 16 + color: mouseAreaWifi.containsMouse ? Theme.accentPrimary : Theme.textPrimary + } + + MouseArea { + id: mouseAreaWifi + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + wifiMenu.visible = !wifiMenu.visible; + if (wifiMenu.visible) { + network.onMenuOpened(); + } else { + network.onMenuClosed(); + } + } + onEntered: wifiTooltip.tooltipVisible = true + onExited: wifiTooltip.tooltipVisible = false + } + } + + StyledTooltip { + id: wifiTooltip + text: "WiFi Networks" + positionAbove: false + tooltipVisible: false + targetItem: wifiIcon + delay: 200 + } + + PanelWindow { + id: wifiMenu + implicitWidth: 320 + implicitHeight: 480 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + + Rectangle { + anchors.fill: parent + color: Theme.backgroundPrimary + radius: 12 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 16 + + RowLayout { + Layout.fillWidth: true + spacing: 12 + + Text { + text: "wifi" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.accentPrimary + } + + Text { + text: "WiFi Networks" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + + IconButton { + icon: "refresh" + onClicked: network.refreshNetworks() + } + + IconButton { + icon: "close" + onClicked: { + wifiMenu.visible = false; + network.onMenuClosed(); + } + } + } + + Rectangle { + Layout.fillWidth: true + height: 1 + color: Theme.outline + opacity: 0.12 + } + + ListView { + id: networkList + Layout.fillWidth: true + Layout.fillHeight: true + model: Object.values(network.networks) + spacing: 8 + clip: true + + delegate: Item { + width: parent.width + height: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 108 : 48 // 48 for network + 60 for password prompt + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 48 + radius: 8 + color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (networkMouseArea.containsMouse ? Theme.highlight : "transparent") + + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 + + Text { + text: network.signalIcon(modelData.signal) + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + } + + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + + Text { + text: modelData.ssid || "Unknown Network" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) + font.pixelSize: 14 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: modelData.security && modelData.security !== "--" ? modelData.security : "Open" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + visible: network.connectStatusSsid === modelData.ssid && network.connectStatus === "error" && network.connectError.length > 0 + text: network.connectError + color: Theme.error + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + Item { + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + visible: network.connectStatusSsid === modelData.ssid && (network.connectStatus !== "" || network.connectingSsid === modelData.ssid) + + Spinner { + visible: network.connectingSsid === modelData.ssid + running: network.connectingSsid === modelData.ssid + color: Theme.accentPrimary + anchors.centerIn: parent + size: 22 + } + + Text { + visible: network.connectStatus === "success" && !network.connectingSsid + text: "check_circle" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: "#43a047" + anchors.centerIn: parent + } + + Text { + visible: network.connectStatus === "error" && !network.connectingSsid + text: "error" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: Theme.error + anchors.centerIn: parent + } + } + + Text { + visible: modelData.connected + text: "connected" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + font.pixelSize: 11 + } + } + + MouseArea { + id: networkMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (modelData.connected) { + network.disconnectNetwork(modelData.ssid); + } else if (network.isSecured(modelData.security) && !modelData.existing) { + passwordPromptSsid = modelData.ssid; + showPasswordPrompt = true; + passwordInput = ""; // Clear previous input + Qt.callLater(function() { + passwordInputField.forceActiveFocus(); + }); + } else { + network.connectNetwork(modelData.ssid, modelData.security); + } + } + } + } + + // Password prompt section + Rectangle { + id: passwordPromptSection + Layout.fillWidth: true + Layout.preferredHeight: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 60 : 0 + Layout.margins: 8 + visible: modelData.ssid === passwordPromptSsid && showPasswordPrompt + color: Theme.surfaceVariant + radius: 8 + + RowLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 10 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 36 + + Rectangle { + anchors.fill: parent + radius: 8 + color: "transparent" + border.color: passwordInputField.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: passwordInputField + anchors.fill: parent + anchors.margins: 12 + text: passwordInput + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + focus: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhNone + echoMode: TextInput.Password + onTextChanged: passwordInput = text + onAccepted: { + network.submitPassword(passwordPromptSsid, passwordInput); + showPasswordPrompt = false; + } + + MouseArea { + id: passwordInputMouseArea + anchors.fill: parent + onClicked: passwordInputField.forceActiveFocus() + } + } + } + } + + Rectangle { + Layout.preferredWidth: 80 + Layout.preferredHeight: 36 + radius: 18 + color: Theme.accentPrimary + border.color: Theme.accentPrimary + border.width: 0 + opacity: 1.0 + + Behavior on color { + ColorAnimation { + duration: 100 + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + network.submitPassword(passwordPromptSsid, passwordInput); + showPasswordPrompt = false; + } + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: parent.color = Qt.darker(Theme.accentPrimary, 1.1) + onExited: parent.color = Theme.accentPrimary + } + + Text { + anchors.centerIn: parent + text: "Connect" + color: Theme.backgroundPrimary + font.pixelSize: 14 + font.bold: true + } + } + } + } + } + } + } + } + } + } +} \ No newline at end of file diff --git a/Bar/Modules/Workspace.qml b/Bar/Modules/Workspace.qml index 40a4023..a658a6a 100644 --- a/Bar/Modules/Workspace.qml +++ b/Bar/Modules/Workspace.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import QtQuick.Window -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Io import qs.Settings @@ -124,13 +124,13 @@ Item { border.color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.1) border.width: 1 layer.enabled: true - layer.effect: DropShadow { - color: "black" - radius: 12 - samples: 24 - verticalOffset: 0 - horizontalOffset: 0 - opacity: 0.10 + layer.effect: MultiEffect { + shadowColor: "black" + // radius: 12 + + shadowVerticalOffset: 0 + shadowHorizontalOffset: 0 + shadowOpacity: 0.10 } } diff --git a/Components/Avatar.qml b/Components/Avatar.qml new file mode 100644 index 0000000..3a369bd --- /dev/null +++ b/Components/Avatar.qml @@ -0,0 +1,51 @@ +import QtQuick +import Quickshell +import Quickshell.Widgets +import qs.Settings +import QtQuick.Effects + +Item { + anchors.fill: parent + anchors.margins: 2 + + Image { + id: avatarImage + anchors.fill: parent + source: "file://" + Settings.settings.profileImage + visible: false + mipmap: true + smooth: true + asynchronous: true + fillMode: Image.PreserveAspectCrop + } + + MultiEffect { + anchors.fill: parent + source: avatarImage + maskEnabled: true + maskSource: mask + visible: Settings.settings.profileImage !== "" + } + + Item { + id: mask + anchors.fill: parent + layer.enabled: true + visible: false + Rectangle { + anchors.fill: parent + radius: avatarImage.width / 2 + } + } + + // Fallback icon + Text { + anchors.centerIn: parent + text: "person" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.onAccent + visible: Settings.settings.profileImage === undefined || Settings.settings.profileImage === "" + z: 0 + } +} diff --git a/Components/CircularProgressBar.qml b/Components/CircularProgressBar.qml index 075f201..da03bfa 100644 --- a/Components/CircularProgressBar.qml +++ b/Components/CircularProgressBar.qml @@ -5,8 +5,7 @@ Rectangle { id: circularProgressBar color: "transparent" - // Properties - property real progress: 0.0 // 0.0 to 1.0 + property real progress: 0.0 property int size: 80 property color backgroundColor: Theme.surfaceVariant property color progressColor: Theme.accentPrimary @@ -19,7 +18,7 @@ Rectangle { // Notch properties property bool hasNotch: false - property real notchSize: 0.25 // Size of the notch as a fraction of the circle + property real notchSize: 0.25 property string notchIcon: "" property int notchIconSize: 12 property color notchIconColor: Theme.accentPrimary @@ -32,6 +31,7 @@ Rectangle { anchors.fill: parent onPaint: { + // Setup canvas context and calculate dimensions var ctx = getContext("2d") var centerX = width / 2 var centerY = height / 2 @@ -41,25 +41,22 @@ Rectangle { var notchStartAngle = -notchAngle / 2 var notchEndAngle = notchAngle / 2 - // Clear canvas ctx.reset() - - // Background circle ctx.strokeStyle = backgroundColor ctx.lineWidth = strokeWidth ctx.lineCap = "round" ctx.beginPath() if (hasNotch) { - // Draw background circle with notch on the right side - // Draw the arc excluding the notch area (notch is at 0 radians, right side) + // Draw background arc with notch gap ctx.arc(centerX, centerY, radius, notchEndAngle, 2 * Math.PI + notchStartAngle) } else { + // Draw full background circle ctx.arc(centerX, centerY, radius, 0, 2 * Math.PI) } ctx.stroke() - // Progress arc + // Draw progress arc if (progress > 0) { ctx.strokeStyle = progressColor ctx.lineWidth = strokeWidth @@ -67,15 +64,11 @@ Rectangle { ctx.beginPath() if (hasNotch) { - // Calculate progress with notch consideration + // Calculate progress arc with notch gap var availableAngle = 2 * Math.PI - notchAngle var progressAngle = availableAngle * progress - - // Start from where the notch cutout begins (top-right) and go clockwise var adjustedStartAngle = notchEndAngle var adjustedEndAngle = adjustedStartAngle + progressAngle - - // Ensure we don't exceed the available space if (adjustedEndAngle > 2 * Math.PI + notchStartAngle) { adjustedEndAngle = 2 * Math.PI + notchStartAngle } @@ -84,6 +77,7 @@ Rectangle { ctx.arc(centerX, centerY, radius, adjustedStartAngle, adjustedEndAngle) } } else { + // Draw full progress arc ctx.arc(centerX, centerY, radius, startAngle, startAngle + (2 * Math.PI * progress)) } ctx.stroke() diff --git a/Components/StyledTooltip.qml b/Components/StyledTooltip.qml index 3c63e93..ae444e7 100644 --- a/Components/StyledTooltip.qml +++ b/Components/StyledTooltip.qml @@ -8,18 +8,22 @@ Window { property bool tooltipVisible: false property Item targetItem: null property int delay: 300 + + property bool positionAbove: true + flags: Qt.ToolTip | Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint color: "transparent" visible: false - minimumWidth: tooltipText.implicitWidth + 24 - minimumHeight: tooltipText.implicitHeight + 16 property var _timerObj: null + onTooltipVisibleChanged: { if (tooltipVisible) { if (delay > 0) { if (_timerObj) { _timerObj.destroy(); _timerObj = null; } - _timerObj = Qt.createQmlObject('import QtQuick 2.0; Timer { interval: ' + delay + '; running: true; repeat: false; onTriggered: tooltipWindow._showNow() }', tooltipWindow); + _timerObj = Qt.createQmlObject( + 'import QtQuick 2.0; Timer { interval: ' + delay + '; running: true; repeat: false; onTriggered: tooltipWindow._showNow() }', + tooltipWindow); } else { _showNow(); } @@ -27,30 +31,45 @@ Window { _hideNow(); } } + function _showNow() { + width = Math.max(50, tooltipText.implicitWidth + 24) + height = Math.max(50, tooltipText.implicitHeight + 16) + if (!targetItem) return; - var pos = targetItem.mapToGlobal(0, targetItem.height); - x = pos.x - width / 2 + targetItem.width / 2; - y = pos.y + 12; + + if (positionAbove) { + // Position tooltip above the target item + var pos = targetItem.mapToGlobal(0, 0); + x = pos.x - width / 2 + targetItem.width / 2; + y = pos.y - height - 12; // 12 px margin above + } else { + // Position tooltip below the target item + var pos = targetItem.mapToGlobal(0, targetItem.height); + x = pos.x - width / 2 + targetItem.width / 2; + y = pos.y + 12; // 12 px margin below + } visible = true; } + function _hideNow() { visible = false; if (_timerObj) { _timerObj.destroy(); _timerObj = null; } } + Connections { target: tooltipWindow.targetItem function onXChanged() { - if (tooltipWindow.visible) tooltipWindow._showNow() + if (tooltipWindow.visible) tooltipWindow._showNow(); } function onYChanged() { - if (tooltipWindow.visible) tooltipWindow._showNow() + if (tooltipWindow.visible) tooltipWindow._showNow(); } function onWidthChanged() { - if (tooltipWindow.visible) tooltipWindow._showNow() + if (tooltipWindow.visible) tooltipWindow._showNow(); } function onHeightChanged() { - if (tooltipWindow.visible) tooltipWindow._showNow() + if (tooltipWindow.visible) tooltipWindow._showNow(); } } @@ -63,6 +82,7 @@ Window { opacity: 0.97 z: 1 } + Text { id: tooltipText text: tooltipWindow.text @@ -76,15 +96,16 @@ Window { padding: 8 z: 2 } + MouseArea { anchors.fill: parent hoverEnabled: true onExited: tooltipWindow.tooltipVisible = false cursorShape: Qt.ArrowCursor } + onTextChanged: { width = Math.max(minimumWidth, tooltipText.implicitWidth + 24); height = Math.max(minimumHeight, tooltipText.implicitHeight + 16); } - -} \ No newline at end of file +} \ No newline at end of file diff --git a/Helpers/IPCHandlers.qml b/Helpers/IPCHandlers.qml index 6cc0aea..3da1fca 100644 --- a/Helpers/IPCHandlers.qml +++ b/Helpers/IPCHandlers.qml @@ -6,7 +6,7 @@ IpcHandler { property var appLauncherPanel property var lockScreen property IdleInhibitor idleInhibitor - property var notificationPopup + property var notificationPopupVariants target: "globalIPC" @@ -17,10 +17,18 @@ IpcHandler { function toggleNotificationPopup(): void { console.log("[IPC] NotificationPopup toggle() called") - notificationPopup.togglePopup(); + + if (notificationPopupVariants) { + for (let i = 0; i < notificationPopupVariants.count; i++) { + let popup = notificationPopupVariants.objectAt(i); + if (popup) { + popup.togglePopup(); + } + } + } } - // Toggle Applauncher visibility + function toggleLauncher(): void { if (!appLauncherPanel) { console.warn("AppLauncherIpcHandler: appLauncherPanel not set!"); @@ -34,7 +42,7 @@ IpcHandler { } } - // Toggle LockScreen + function toggleLock(): void { if (!lockScreen) { console.warn("LockScreenIpcHandler: lockScreen not set!"); diff --git a/Helpers/IdleInhibitor.qml b/Helpers/IdleInhibitor.qml index de78a99..d2d8166 100644 --- a/Helpers/IdleInhibitor.qml +++ b/Helpers/IdleInhibitor.qml @@ -3,10 +3,10 @@ import Quickshell.Io Process { id: idleRoot - // Example: systemd-inhibit to prevent idle/sleep + // Uses systemd-inhibit to prevent idle/sleep command: ["systemd-inhibit", "--what=idle:sleep", "--who=noctalia", "--why=User requested", "sleep", "infinity"] - // Keep process running in background + // Track background process state property bool isRunning: running onStarted: { @@ -17,7 +17,7 @@ Process { console.log("[IdleInhibitor] Process finished:", exitCode) } - // Control functions + function start() { if (!running) { console.log("[IdleInhibitor] Starting idle inhibitor...") diff --git a/Helpers/Time.js b/Helpers/Time.js new file mode 100644 index 0000000..7e1e316 --- /dev/null +++ b/Helpers/Time.js @@ -0,0 +1,18 @@ +function formatVagueHumanReadableTime(totalSeconds) { + const hours = Math.floor(totalSeconds / 3600); + const minutes = Math.floor((totalSeconds - (hours * 3600)) / 60); + const seconds = totalSeconds - (hours * 3600) - (minutes * 60); + + var str = ""; + if (hours) { + str += hours.toString() + "h"; + } + if (minutes) { + str += minutes.toString() + "m"; + } + if (!hours && !minutes) { + str += seconds.toString() + "s"; + } + return str; +} + diff --git a/README.md b/README.md index f682de1..9f9e6a6 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,14 @@ Contributions are welcome! Feel free to open issues or submit pull requests. While I actually didn't want to accept donations, more and more people are asking to donate so... I don't know, if you really feel like donating then I obviously highly appreciate it but **PLEASE** never feel forced to donate or anything. It won't change how I work on Noctalia, it's a project that I work on for fun in the end. [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/R6R01IX85B) + +--- + +#### Special Thanks + +Thank you to everyone who supports me and this project 💜! +* Gohma + --- ## License diff --git a/Services/MusicManager.qml b/Services/MusicManager.qml index 27f0226..81236b1 100644 --- a/Services/MusicManager.qml +++ b/Services/MusicManager.qml @@ -8,7 +8,7 @@ import qs.Components Singleton { id: manager - // Properties + property var currentPlayer: null property real currentPosition: 0 property int selectedPlayerIndex: 0 @@ -25,14 +25,14 @@ Singleton { property bool canSeek: currentPlayer ? currentPlayer.canSeek : false property bool hasPlayer: getAvailablePlayers().length > 0 - // Initialize + Item { Component.onCompleted: { updateCurrentPlayer() } } - // Returns available MPRIS players + function getAvailablePlayers() { if (!Mpris.players || !Mpris.players.values) { return [] @@ -51,14 +51,14 @@ Singleton { return controllablePlayers } - // Returns active player or first available + function findActivePlayer() { let availablePlayers = getAvailablePlayers() if (availablePlayers.length === 0) { return null } - // Use selected player if valid, otherwise use first available + if (selectedPlayerIndex < availablePlayers.length) { return availablePlayers[selectedPlayerIndex] } else { @@ -67,7 +67,8 @@ Singleton { } } - // Updates currentPlayer and currentPosition + + // Switch to the most recently active player function updateCurrentPlayer() { let newPlayer = findActivePlayer() if (newPlayer !== currentPlayer) { @@ -76,7 +77,7 @@ Singleton { } } - // Player control functions + function playPause() { if (currentPlayer) { if (currentPlayer.isPlaying) { @@ -118,6 +119,7 @@ Singleton { } } + // Seek to position based on ratio (0.0 to 1.0) function seekByRatio(ratio) { if (currentPlayer && currentPlayer.canSeek && currentPlayer.length > 0) { let seekPosition = ratio * currentPlayer.length @@ -126,20 +128,29 @@ Singleton { } } - // Updates progress bar every second + // Update progress bar every second while playing Timer { id: positionTimer interval: 1000 - running: currentPlayer && currentPlayer.isPlaying && currentPlayer.length > 0 + running: currentPlayer && currentPlayer.isPlaying && currentPlayer.length > 0 && currentPlayer.playbackState === MprisPlaybackState.Playing repeat: true onTriggered: { - if (currentPlayer && currentPlayer.isPlaying) { + if (currentPlayer && currentPlayer.isPlaying && currentPlayer.playbackState === MprisPlaybackState.Playing) { currentPosition = currentPlayer.position + } else { + running = false } } } - // Reacts to player list changes + // Reset position when switching to inactive player + onCurrentPlayerChanged: { + if (!currentPlayer || !currentPlayer.isPlaying || currentPlayer.playbackState !== MprisPlaybackState.Playing) { + currentPosition = 0 + } + } + + // Update current player when available players change Connections { target: Mpris.players function onValuesChanged() { diff --git a/Services/Network.qml b/Services/Network.qml new file mode 100644 index 0000000..c1dd53a --- /dev/null +++ b/Services/Network.qml @@ -0,0 +1,348 @@ +import QtQuick +import Quickshell.Io + +QtObject { + id: root + + property var networks: ({}) + property string connectingSsid: "" + property string connectStatus: "" + property string connectStatusSsid: "" + property string connectError: "" + property string detectedInterface: "" + + function signalIcon(signal) { + if (signal >= 80) + return "network_wifi"; + if (signal >= 60) + return "network_wifi_3_bar"; + if (signal >= 40) + return "network_wifi_2_bar"; + if (signal >= 20) + return "network_wifi_1_bar"; + return "wifi_0_bar"; + } + + function isSecured(security) { + return security && security.trim() !== "" && security.trim() !== "--"; + } + + function refreshNetworks() { + existingNetwork.running = true; + } + + function connectNetwork(ssid, security) { + pendingConnect = { + ssid: ssid, + security: security, + password: "" + }; + doConnect(); + } + + function submitPassword(ssid, password) { + pendingConnect = { + ssid: ssid, + security: networks[ssid].security, + password: password + }; + doConnect(); + } + + function disconnectNetwork(ssid) { + disconnectProfileProcess.connectionName = ssid; + disconnectProfileProcess.running = true; + } + + property var pendingConnect: null + + function doConnect() { + const params = pendingConnect; + if (!params) + return; + + connectingSsid = params.ssid; + connectStatus = ""; + connectStatusSsid = params.ssid; + + + const targetNetwork = networks[params.ssid]; + + if (targetNetwork && targetNetwork.existing) { + upConnectionProcess.profileName = params.ssid; + upConnectionProcess.running = true; + pendingConnect = null; + return; + } + + + if (params.security && params.security !== "--") { + getInterfaceProcess.running = true; + return; + } + connectProcess.security = params.security; + connectProcess.ssid = params.ssid; + connectProcess.password = params.password; + connectProcess.running = true; + pendingConnect = null; + } + + property int refreshInterval: 25000 + + // Only refresh when we have an active connection + property bool hasActiveConnection: { + for (const net in networks) { + if (networks[net].connected) { + return true; + } + } + return false; + } + + property Timer refreshTimer: Timer { + interval: root.refreshInterval + // Only run timer when we're connected to a network + running: root.hasActiveConnection + repeat: true + onTriggered: root.refreshNetworks() + } + + // Force a refresh when menu is opened + function onMenuOpened() { + refreshNetworks(); + } + + function onMenuClosed() { + // No need to do anything special on close + } + + property Process disconnectProfileProcess: Process { + id: disconnectProfileProcess + property string connectionName: "" + running: false + command: ["nmcli", "connection", "down", connectionName] + onRunningChanged: { + if (!running) { + root.refreshNetworks(); + } + } + } + + property Process existingNetwork: Process { + id: existingNetwork + running: false + command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"] + stdout: StdioCollector { + onStreamFinished: { + const lines = text.split("\n"); + const networksMap = {}; + + for (let i = 0; i < lines.length; ++i) { + const line = lines[i].trim(); + if (!line) + continue; + + const parts = line.split(":"); + if (parts.length < 2) { + console.warn("Malformed nmcli output line:", line); + continue; + } + + const ssid = parts[0]; + const type = parts[1]; + + if (ssid) { + networksMap[ssid] = { + ssid: ssid, + type: type + }; + } + } + scanProcess.existingNetwork = networksMap; + scanProcess.running = true; + } + } + } + + property Process scanProcess: Process { + id: scanProcess + running: false + command: ["nmcli", "-t", "-f", "SSID,SECURITY,SIGNAL,IN-USE", "device", "wifi", "list"] + + property var existingNetwork + + stdout: StdioCollector { + onStreamFinished: { + const lines = text.split("\n"); + const networksMap = {}; + + for (let i = 0; i < lines.length; ++i) { + const line = lines[i].trim(); + if (!line) + continue; + + const parts = line.split(":"); + if (parts.length < 4) { + console.warn("Malformed nmcli output line:", line); + continue; + } + const ssid = parts[0]; + const security = parts[1]; + const signal = parseInt(parts[2]); + const inUse = parts[3] === "*"; + + if (ssid) { + if (!networksMap[ssid]) { + networksMap[ssid] = { + ssid: ssid, + security: security, + signal: signal, + connected: inUse, + existing: ssid in scanProcess.existingNetwork + }; + } else { + const existingNet = networksMap[ssid]; + if (inUse) { + existingNet.connected = true; + } + if (signal > existingNet.signal) { + existingNet.signal = signal; + existingNet.security = security; + } + } + } + } + + root.networks = networksMap; + scanProcess.existingNetwork = {}; + } + } + } + + property Process connectProcess: Process { + id: connectProcess + property string ssid: "" + property string password: "" + property string security: "" + running: false + command: { + if (password) { + return ["nmcli", "device", "wifi", "connect", `'${ssid}'`, "password", password]; + } else { + return ["nmcli", "device", "wifi", "connect", `'${ssid}'`]; + } + } + stdout: StdioCollector { + onStreamFinished: { + root.connectingSsid = ""; + root.connectStatus = "success"; + root.connectStatusSsid = connectProcess.ssid; + root.connectError = ""; + root.refreshNetworks(); + } + } + stderr: StdioCollector { + onStreamFinished: { + root.connectingSsid = ""; + root.connectStatus = "error"; + root.connectStatusSsid = connectProcess.ssid; + root.connectError = text; + } + } + } + + property Process getInterfaceProcess: Process { + id: getInterfaceProcess + running: false + command: ["nmcli", "-t", "-f", "DEVICE,TYPE,STATE", "device"] + stdout: StdioCollector { + onStreamFinished: { + var lines = text.split("\n"); + for (var i = 0; i < lines.length; ++i) { + var parts = lines[i].split(":"); + if (parts[1] === "wifi" && parts[2] !== "unavailable") { + root.detectedInterface = parts[0]; + break; + } + } + if (root.detectedInterface) { + var params = root.pendingConnect; + addConnectionProcess.ifname = root.detectedInterface; + addConnectionProcess.ssid = params.ssid; + addConnectionProcess.password = params.password; + addConnectionProcess.profileName = params.ssid; + addConnectionProcess.security = params.security; + addConnectionProcess.running = true; + } else { + root.connectStatus = "error"; + root.connectStatusSsid = root.pendingConnect.ssid; + root.connectError = "No Wi-Fi interface found."; + root.connectingSsid = ""; + root.pendingConnect = null; + } + } + } + } + + property Process addConnectionProcess: Process { + id: addConnectionProcess + property string ifname: "" + property string ssid: "" + property string password: "" + property string profileName: "" + property string security: "" + running: false + command: { + var cmd = ["nmcli", "connection", "add", "type", "wifi", "ifname", ifname, "con-name", profileName, "ssid", ssid]; + if (security && security !== "--") { + cmd.push("wifi-sec.key-mgmt"); + cmd.push("wpa-psk"); + cmd.push("wifi-sec.psk"); + cmd.push(password); + } + return cmd; + } + stdout: StdioCollector { + onStreamFinished: { + upConnectionProcess.profileName = addConnectionProcess.profileName; + upConnectionProcess.running = true; + } + } + stderr: StdioCollector { + onStreamFinished: { + upConnectionProcess.profileName = addConnectionProcess.profileName; + upConnectionProcess.running = true; + } + } + } + + property Process upConnectionProcess: Process { + id: upConnectionProcess + property string profileName: "" + running: false + command: ["nmcli", "connection", "up", "id", profileName] + stdout: StdioCollector { + onStreamFinished: { + root.connectingSsid = ""; + root.connectStatus = "success"; + root.connectStatusSsid = root.pendingConnect ? root.pendingConnect.ssid : ""; + root.connectError = ""; + root.pendingConnect = null; + root.refreshNetworks(); + } + } + stderr: StdioCollector { + onStreamFinished: { + root.connectingSsid = ""; + root.connectStatus = "error"; + root.connectStatusSsid = root.pendingConnect ? root.pendingConnect.ssid : ""; + root.connectError = text; + root.pendingConnect = null; + } + } + } + + Component.onCompleted: { + refreshNetworks(); + } +} \ No newline at end of file diff --git a/Services/WallpaperManager.qml b/Services/WallpaperManager.qml index 64d1cad..f22f0fb 100644 --- a/Services/WallpaperManager.qml +++ b/Services/WallpaperManager.qml @@ -15,7 +15,7 @@ Singleton { toggleRandomWallpaper(); } } - property string wallpaperDirectory: Settings.settings.wallpaperFolder + property var wallpaperList: [] property string currentWallpaper: Settings.settings.currentWallpaper property bool scanning: false @@ -46,6 +46,11 @@ Singleton { } changeWallpaperProcess.running = true; } + + if (randomWallpaperTimer.running) { + randomWallpaperTimer.restart(); + } + generateTheme(); } @@ -91,15 +96,17 @@ Singleton { FolderListModel { id: folderModel + // Swww supports many images format but Quickshell only support a subset of those. nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.bmp"] showDirs: false sortField: FolderListModel.Name onStatusChanged: { if (status === FolderListModel.Ready) { var files = []; + var filesSwww = []; for (var i = 0; i < count; i++) { - var fileph = (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "") + "/" + get(i, "fileName"); - files.push(fileph); + var filepath = (Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "") + "/" + get(i, "fileName"); + files.push(filepath); } wallpaperList = files; scanning = false; diff --git a/Settings/Settings.qml b/Settings/Settings.qml index 879abd2..23bb5f2 100644 --- a/Settings/Settings.qml +++ b/Settings/Settings.qml @@ -45,6 +45,7 @@ Singleton { property string wallpaperFolder: "/usr/share/wallpapers" property string currentWallpaper: "" property string videoPath: "~/Videos/" + property bool showActiveWindow: true property bool showActiveWindowIcon: false property bool showSystemInfoInBar: false property bool showCorners: true @@ -65,6 +66,22 @@ Singleton { property real fontSizeMultiplier: 1.0 // Font size multiplier (1.0 = normal, 1.2 = 20% larger, 0.8 = 20% smaller) property int taskbarIconSize: 24 // Taskbar icon button size in pixels (default: 32, smaller: 24, larger: 40) property var pinnedExecs: [] // Added for AppLauncher pinned apps + + property bool showDock: true + property bool dockExclusive: false + property bool wifiEnabled: false + property bool bluetoothEnabled: false + property int recordingFrameRate: 60 + property string recordingQuality: "very_high" + property string recordingCodec: "h264" + property string audioCodec: "opus" + property bool showCursor: true + property string colorRange: "limited" + + // Monitor/Display Settings + property var barMonitors: [] // Array of monitor names to show the bar on + property var dockMonitors: [] // Array of monitor names to show the dock on + property var notificationMonitors: [] // Array of monitor names to show notifications on } } diff --git a/Widgets/Background.qml b/Widgets/Background.qml index b8eb602..b4d5dca 100644 --- a/Widgets/Background.qml +++ b/Widgets/Background.qml @@ -35,6 +35,7 @@ ShellRoot { visible: wallpaperSource !== "" cache: true smooth: true + mipmap: false } } } diff --git a/Widgets/Dock.qml b/Widgets/Dock.qml new file mode 100644 index 0000000..94b69f3 --- /dev/null +++ b/Widgets/Dock.qml @@ -0,0 +1,350 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import Quickshell.Widgets +import qs.Settings +import qs.Components + +PanelWindow { + id: taskbarWindow + visible: Settings.settings.showDock && + (Settings.settings.dockMonitors.includes(modelData.name) || + (Settings.settings.dockMonitors.length === 0)) + screen: (typeof modelData !== 'undefined' ? modelData : null) + exclusionMode: ExclusionMode.Ignore + anchors.bottom: true + anchors.left: true + anchors.right: true + focusable: false + color: "transparent" + implicitHeight: 43 + + // Auto-hide properties + property bool autoHide: true + property bool hidden: true + property int hideDelay: 500 + property int showDelay: 100 + property int hideAnimationDuration: 200 + property int showAnimationDuration: 150 + property int peekHeight: 2 + property int fullHeight: taskbarContainer.height + + // Track hover state + property bool dockHovered: false + property bool anyAppHovered: false + + // Context menu properties + property bool contextMenuVisible: false + property var contextMenuTarget: null + property var contextMenuToplevel: null + + // Timer for auto-hide delay + Timer { + id: hideTimer + interval: hideDelay + onTriggered: if (autoHide && !dockHovered && !anyAppHovered && !contextMenuVisible) hidden = true + } + + // Timer for show delay + Timer { + id: showTimer + interval: showDelay + onTriggered: hidden = false + } + + // Behavior for smooth hide/show animations + Behavior on margins.bottom { + NumberAnimation { + duration: hidden ? hideAnimationDuration : showAnimationDuration + easing.type: Easing.InOutQuad + } + } + + // Mouse area at screen bottom to detect entry and keep dock visible + MouseArea { + id: screenEdgeMouseArea + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + height: 10 + hoverEnabled: true + propagateComposedEvents: true + + onEntered: if (autoHide && hidden) showTimer.start() + onExited: if (autoHide && !hidden && !dockHovered && !anyAppHovered && !contextMenuVisible) hideTimer.start() + } + + margins.bottom: hidden ? -(fullHeight - peekHeight) : 0 + + Rectangle { + id: taskbarContainer + width: taskbar.width + 40 + height: Settings.settings.taskbarIconSize + 20 + topLeftRadius: 16 + topRightRadius: 16 + color: Theme.backgroundSecondary + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottom: parent.bottom + + MouseArea { + id: dockMouseArea + anchors.fill: parent + hoverEnabled: true + propagateComposedEvents: true + + onEntered: { + dockHovered = true + if (autoHide) { + showTimer.stop() + hideTimer.stop() + hidden = false + } + } + onExited: { + dockHovered = false + if (autoHide && !anyAppHovered && !contextMenuVisible) hideTimer.start() + } + } + + Item { + id: taskbar + width: runningAppsRow.width + height: parent.height - 10 + anchors.centerIn: parent + + StyledTooltip { id: styledTooltip } + + function getAppIcon(toplevel: Toplevel): string { + if (!toplevel) return ""; + let icon = Quickshell.iconPath(toplevel.appId?.toLowerCase(), true); + if (!icon) icon = Quickshell.iconPath(toplevel.appId, true); + if (!icon) icon = Quickshell.iconPath(toplevel.title?.toLowerCase(), true); + if (!icon) icon = Quickshell.iconPath(toplevel.title, true); + return icon || Quickshell.iconPath("application-x-executable", true); + } + + Row { + id: runningAppsRow + spacing: 12 + height: parent.height + anchors.centerIn: parent + + Repeater { + model: ToplevelManager ? ToplevelManager.toplevels : null + + delegate: Rectangle { + id: appButton + width: Settings.settings.taskbarIconSize + 8 + height: Settings.settings.taskbarIconSize + 8 + radius: Math.max(6, Settings.settings.taskbarIconSize * 0.3) + color: isActive ? Theme.accentPrimary : (hovered ? Theme.surfaceVariant : "transparent") + border.color: isActive ? Qt.darker(Theme.accentPrimary, 1.2) : "transparent" + border.width: 1 + + property bool isActive: ToplevelManager.activeToplevel && ToplevelManager.activeToplevel === modelData + property bool hovered: appMouseArea.containsMouse + property string appId: modelData ? modelData.appId : "" + property string appTitle: modelData ? modelData.title : "" + + Behavior on color { ColorAnimation { duration: 150 } } + Behavior on border.color { ColorAnimation { duration: 150 } } + + IconImage { + id: appIcon + width: Math.max(20, Settings.settings.taskbarIconSize * 0.75) + height: Math.max(20, Settings.settings.taskbarIconSize * 0.75) + anchors.centerIn: parent + source: taskbar.getAppIcon(modelData) + visible: source.toString() !== "" + } + + Text { + anchors.centerIn: parent + visible: !appIcon.visible + text: appButton.appId ? appButton.appId.charAt(0).toUpperCase() : "?" + font.family: Theme.fontFamily + font.pixelSize: Math.max(14, Settings.settings.taskbarIconSize * 0.5) + font.bold: true + color: appButton.isActive ? Theme.onAccent : Theme.textPrimary + } + + MouseArea { + id: appMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton + + onEntered: { + anyAppHovered = true + if (!contextMenuVisible) { + styledTooltip.text = appTitle || appId; + styledTooltip.targetItem = appButton; + styledTooltip.positionAbove = true; + styledTooltip.tooltipVisible = true; + } + if (autoHide) { + showTimer.stop() + hideTimer.stop() + hidden = false + } + } + onExited: { + anyAppHovered = false + if (!contextMenuVisible) { + styledTooltip.tooltipVisible = false; + } + if (autoHide && !dockHovered && !contextMenuVisible) hideTimer.start() + } + onClicked: function(mouse) { + if (mouse.button === Qt.MiddleButton && modelData?.close) { + modelData.close(); + } + if (mouse.button === Qt.LeftButton && modelData?.activate) { + modelData.activate(); + } + if (mouse.button === Qt.RightButton) { + styledTooltip.tooltipVisible = false; + contextMenuTarget = appButton; + contextMenuToplevel = modelData; + contextMenuVisible = true; + } + } + } + + Rectangle { + visible: isActive + width: 6 + height: 6 + radius: 3 + color: Theme.onAccent + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + anchors.bottomMargin: -8 + } + } + } + } + } + } + + // Context Menu + PanelWindow { + id: contextMenuWindow + visible: contextMenuVisible + screen: taskbarWindow.screen + exclusionMode: ExclusionMode.Ignore + anchors.bottom: true + anchors.left: true + anchors.right: true + color: "transparent" + focusable: false + + MouseArea { + anchors.fill: parent + onClicked: { + contextMenuVisible = false; + contextMenuTarget = null; + contextMenuToplevel = null; + hidden = true; // Hide dock when context menu closes + } + } + + Rectangle { + id: contextMenuContainer + width: 80 + height: contextMenuColumn.height + 0 + radius: 16 + color: Theme.backgroundPrimary + border.color: Theme.outline + border.width: 1 + + x: { + if (!contextMenuTarget) return 0; + // Get position relative to screen + const pos = contextMenuTarget.mapToItem(null, 0, 0); + // Center horizontally above the icon + let xPos = pos.x + (contextMenuTarget.width - width) / 2; + // Constrain to screen edges + return Math.max(0, Math.min(xPos, taskbarWindow.width - width)); + } + + y: { + if (!contextMenuTarget) return 0; + // Position above the dock + const pos = contextMenuTarget.mapToItem(null, 0, 0); + return pos.y - height + 32; + } + + Column { + id: contextMenuColumn + anchors.centerIn: parent + spacing: 4 + width: parent.width + + + Rectangle { + width: parent.width + height: 32 + radius: 16 + color: closeMouseArea.containsMouse ? Theme.surfaceVariant : "transparent" + border.color: Theme.outline + border.width: 1 + + Row { + anchors.left: parent.left + anchors.leftMargin: 12 + anchors.verticalCenter: parent.verticalCenter + spacing: 4 + + Text { + anchors.verticalCenter: parent.verticalCenter + text: "close" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 + color: Theme.textPrimary + } + + Text { + anchors.verticalCenter: parent.verticalCenter + text: "Close" + font.family: Theme.fontFamily + font.pixelSize: 14 + color: Theme.textPrimary + } + } + + MouseArea { + id: closeMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { + if (contextMenuToplevel?.close) contextMenuToplevel.close(); + contextMenuVisible = false; + hidden = true; + } + } + } + } + + // Animation + scale: contextMenuVisible ? 1 : 0.9 + opacity: contextMenuVisible ? 1 : 0 + transformOrigin: Item.Bottom + + Behavior on scale { + NumberAnimation { + duration: 150 + easing.type: Easing.OutBack + } + } + + Behavior on opacity { + NumberAnimation { duration: 100 } + } + } + } +} diff --git a/Widgets/LockScreen/BatteryCharge.qml b/Widgets/LockScreen/BatteryCharge.qml index 5e8b4c0..dd96c25 100644 --- a/Widgets/LockScreen/BatteryCharge.qml +++ b/Widgets/LockScreen/BatteryCharge.qml @@ -6,7 +6,7 @@ import qs.Components import qs.Settings Item { - // Test mode + property bool testMode: false property int testPercent: 49 property bool testCharging: true @@ -21,7 +21,7 @@ Item { height: row.height visible: testMode || (isReady && battery.isLaptopBattery) - // Choose icon based on charge and charging state + function batteryIcon() { if (!show) return ""; @@ -32,7 +32,7 @@ Item { if (percent >= 95) return "battery_android_full"; - // Hardcoded battery symbols + if (percent >= 85) return "battery_android_6"; if (percent >= 70) diff --git a/Widgets/LockScreen/LockScreen.qml b/Widgets/LockScreen/LockScreen.qml index 1097c29..2eaf479 100644 --- a/Widgets/LockScreen/LockScreen.qml +++ b/Widgets/LockScreen/LockScreen.qml @@ -2,11 +2,11 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls import QtQuick.Effects -import Qt5Compat.GraphicalEffects -import Quickshell.Wayland import Quickshell +import Quickshell.Wayland import Quickshell.Services.Pam import Quickshell.Io +import Quickshell.Widgets import qs.Components import qs.Settings import qs.Services @@ -32,7 +32,7 @@ WlSessionLock { Component.onCompleted: { Qt.callLater(function () { fetchWeatherData(); - }) + }); } function fetchWeatherData() { @@ -135,8 +135,8 @@ WlSessionLock { fillMode: Image.PreserveAspectCrop source: WallpaperManager.currentWallpaper !== "" ? WallpaperManager.currentWallpaper : "" cache: true - smooth: false - visible: true // source for MultiEffect + smooth: true + mipmap: false } MultiEffect { @@ -146,6 +146,7 @@ WlSessionLock { blurEnabled: true blur: 0.48 // controls blur strength (0 to 1) blurMax: 128 // max blur radius in pixels + // transparentBorder: true } ColumnLayout { @@ -160,39 +161,21 @@ WlSessionLock { radius: 40 color: Theme.accentPrimary - Image { - id: avatarImage + Rectangle { anchors.fill: parent - anchors.margins: 4 - source: Settings.settings.profileImage - fillMode: Image.PreserveAspectCrop - visible: false - asynchronous: true - } - OpacityMask { - anchors.fill: avatarImage - source: avatarImage - maskSource: Rectangle { - width: avatarImage.width - height: avatarImage.height - radius: avatarImage.width / 2 - visible: false - } - visible: Settings.settings.profileImage !== "" - } - Text { - anchors.centerIn: parent - text: "person" - font.family: "Material Symbols Outlined" - font.pixelSize: 32 - color: Theme.onAccent - visible: Settings.settings.profileImage === "" + color: "transparent" + radius: 40 + border.color: Theme.accentPrimary + border.width: 3 + z: 2 } + + Avatar {} + layer.enabled: true - layer.effect: Glow { - color: Theme.accentPrimary - radius: 8 - samples: 16 + layer.effect: MultiEffect { + shadowEnabled: true + shadowColor: Theme.accentPrimary } } @@ -257,7 +240,7 @@ WlSessionLock { width: parent.width * 0.8 height: 44 color: Theme.overlay - radius: 22 + radius: 20 visible: lock.errorMessage !== "" Text { @@ -275,7 +258,7 @@ WlSessionLock { Layout.alignment: Qt.AlignHCenter width: 120 height: 44 - radius: 22 + radius: 20 opacity: unlockButtonArea.containsMouse ? 0.8 : 0.5 color: unlockButtonArea.containsMouse ? Theme.accentPrimary : Theme.surface border.color: Theme.accentPrimary @@ -328,7 +311,7 @@ WlSessionLock { position: "bottomright" size: 1.3 fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" - offsetX: - Screen.width / 2 - 38 + offsetX: -Screen.width / 2 - 38 offsetY: 0 anchors.top: parent.top visible: Settings.settings.showCorners @@ -336,7 +319,7 @@ WlSessionLock { } Rectangle { - width: infoColumn.width + 16 + width: infoColumn.width + 32 height: infoColumn.height + 8 color: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" anchors.horizontalCenter: parent.horizontalCenter @@ -404,7 +387,6 @@ WlSessionLock { horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter } - } } @@ -431,13 +413,11 @@ WlSessionLock { anchors.left: parent.left anchors.bottom: parent.bottom anchors.margins: 32 - spacing: 12 + spacing: 12 - BatteryCharge { - } + BatteryCharge {} } - ColumnLayout { anchors.right: parent.right anchors.bottom: parent.bottom diff --git a/Widgets/Notification/NotificationHistory.qml b/Widgets/Notification/NotificationHistory.qml index be98dd0..7e6277a 100644 --- a/Widgets/Notification/NotificationHistory.qml +++ b/Widgets/Notification/NotificationHistory.qml @@ -5,7 +5,7 @@ import qs.Settings import QtQuick.Layouts import qs.Components -// The popup window + PanelWithOverlay { id: notificationHistoryWin property string historyFilePath: Settings.settingsDir + "notification_history.json" diff --git a/Widgets/Notification/NotificationIcon.qml b/Widgets/Notification/NotificationIcon.qml index 988483a..cfb7b6f 100644 --- a/Widgets/Notification/NotificationIcon.qml +++ b/Widgets/Notification/NotificationIcon.qml @@ -9,13 +9,13 @@ Item { width: 22; height: 22 property bool isSilence: false - // Process for executing CLI commands + Process { id: rightClickProcess command: ["qs","ipc", "call", "globalIPC", "toggleNotificationPopup"] } - // Bell icon/button + Item { id: bell width: 22; height: 22 @@ -34,7 +34,7 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: function(mouse): void { + onClicked: function(mouse) { if (mouse.button === Qt.RightButton) { root.isSilence = !root.isSilence; rightClickProcess.running = true; @@ -55,8 +55,9 @@ Item { StyledTooltip { id: notificationTooltip text: "Notification History" + positionAbove: false tooltipVisible: false targetItem: bell delay: 200 } -} \ No newline at end of file +} diff --git a/Widgets/Notification/NotificationManager.qml b/Widgets/Notification/NotificationManager.qml index 8ac1d5d..94294b9 100644 --- a/Widgets/Notification/NotificationManager.qml +++ b/Widgets/Notification/NotificationManager.qml @@ -14,7 +14,7 @@ PanelWindow { anchors.top: true anchors.right: true - margins.top: -20 // keep as you want + margins.top: -20 margins.right: 6 property var notifications: [] @@ -52,7 +52,7 @@ PanelWindow { anchors.right: parent.right spacing: window.spacing width: parent.width - clip: false // prevent clipping during animation + clip: false // Prevent clipping during animation Repeater { model: notifications diff --git a/Widgets/Notification/NotificationPopup.qml b/Widgets/Notification/NotificationPopup.qml index 42ad0ba..5b4fd5d 100644 --- a/Widgets/Notification/NotificationPopup.qml +++ b/Widgets/Notification/NotificationPopup.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Controls import Quickshell +import Quickshell.Widgets import qs.Settings PanelWindow { @@ -9,7 +10,7 @@ PanelWindow { implicitHeight: notificationColumn.implicitHeight color: "transparent" visible: notificationsVisible && notificationModel.count > 0 - screen: Quickshell.primaryScreen !== undefined ? Quickshell.primaryScreen : null + screen: (typeof modelData !== 'undefined' ? modelData : Quickshell.primaryScreen) focusable: false property bool barVisible: true @@ -114,38 +115,37 @@ PanelWindow { id: iconBackground width: 36 height: 36 - radius: width / 2 // Circular + radius: width / 2 color: Theme.accentPrimary anchors.verticalCenter: parent.verticalCenter border.color: Qt.darker(Theme.accentPrimary, 1.2) border.width: 1.5 - // Get all possible icon sources from notification + // Priority order for notification icons: image > appIcon > icon property var iconSources: [rawNotification?.image || "", rawNotification?.appIcon || "", rawNotification?.icon || ""] - // Try to load notification icon - Image { + // Load notification icon with fallback handling + IconImage { id: iconImage anchors.fill: parent anchors.margins: 4 - fillMode: Image.PreserveAspectFit - smooth: true - cache: false asynchronous: true - sourceSize.width: 36 - sourceSize.height: 36 + backer.fillMode: Image.PreserveAspectFit source: { + // Try each icon source in priority order for (var i = 0; i < iconBackground.iconSources.length; i++) { var icon = iconBackground.iconSources[i]; if (!icon) continue; + // Handle special path format from some notifications if (icon.includes("?path=")) { const [name, path] = icon.split("?path="); const fileName = name.substring(name.lastIndexOf("/") + 1); return `file://${path}/${fileName}`; } + // Handle absolute file paths if (icon.startsWith('/')) { return "file://" + icon; } @@ -157,7 +157,7 @@ PanelWindow { visible: status === Image.Ready && source.toString() !== "" } - // Fallback to first letter of app name + // Fallback: show first letter of app name when no icon available Text { anchors.centerIn: parent visible: !iconImage.visible diff --git a/Widgets/Overview.qml b/Widgets/Overview.qml index c23b79f..9702eba 100644 --- a/Widgets/Overview.qml +++ b/Widgets/Overview.qml @@ -2,7 +2,6 @@ import QtQuick import QtQuick.Effects import Quickshell import Quickshell.Wayland -import Qt5Compat.GraphicalEffects import qs.Services import qs.Settings @@ -34,15 +33,16 @@ ShellRoot { source: wallpaperSource cache: true smooth: true - visible: wallpaperSource !== "" // Show the original for FastBlur input + mipmap: false + visible: wallpaperSource !== "" } MultiEffect { id: overviewBgBlur anchors.fill: parent source: bgImage blurEnabled: true - blur: 0.48 // controls blur strength (0 to 1) - blurMax: 128 // max blur radius in pixels + blur: 0.48 + blurMax: 128 } Rectangle { anchors.fill: parent @@ -53,4 +53,4 @@ ShellRoot { } } } -} \ No newline at end of file +} diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml new file mode 100644 index 0000000..29a695d --- /dev/null +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -0,0 +1,369 @@ +import Quickshell +import Quickshell.Wayland +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Effects +import qs.Settings +import qs.Widgets.SettingsWindow.Tabs + +PanelWindow { + id: panelMain + implicitHeight: screen.height / 2 + implicitWidth: screen.width / 2 + color: "transparent" + + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + + + Component { + id: generalSettings + General {} + } + + Component { + id: barSettings + Bar {} + } + + Component { + id: timeWeatherSettings + TimeWeather {} + } + + Component { + id: recordingSettings + Recording {} + } + + Component { + id: networkSettings + Network {} + } + + Component { + id: miscSettings + Misc {} + } + + Component { + id: aboutSettings + About {} + } + + Component { + id: displaySettings + Display {} + } + + + Rectangle { + id: background + color: Theme.backgroundPrimary + anchors.fill: parent + radius: 20 + border.color: Theme.outline + border.width: 1 + + MultiEffect { + source: background + anchors.fill: background + shadowEnabled: true + shadowColor: Theme.shadow + shadowOpacity: 0.3 + shadowHorizontalOffset: 0 + shadowVerticalOffset: 2 + shadowBlur: 12 + } + } + + + Rectangle { + id: settings + color: Theme.backgroundTertiary + anchors { + left: tabs.right + top: parent.top + bottom: parent.bottom + right: parent.right + margins: 12 + } + topRightRadius: 20 + bottomRightRadius: 20 + + + Rectangle { + id: headerArea + anchors { + top: parent.top + left: parent.left + right: parent.right + margins: 16 + } + height: 48 + color: "transparent" + + RowLayout { + anchors.fill: parent + spacing: 12 + + + Text { + id: tabName + text: "General" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + + + Rectangle { + width: 32 + height: 32 + radius: 16 + color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 + + Text { + anchors.centerIn: parent + text: "close" + font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 18 + color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + } + + MouseArea { + id: closeButtonArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: panelMain.visible = false + } + } + } + } + + + Rectangle { + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + margins: 16 + } + height: 1 + color: Theme.outline + opacity: 0.3 + } + + Item { + id: settingsContainer + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + margins: 24 + topMargin: 32 + } + + + Loader { + id: settingsLoader + anchors.fill: parent + sourceComponent: generalSettings + opacity: 1 + visible: opacity > 0 + + Behavior on opacity { + NumberAnimation { + duration: 150 + easing.type: Easing.InOutQuad + } + } + } + + + Loader { + id: settingsLoader2 + anchors.fill: parent + opacity: 0 + visible: opacity > 0 + + Behavior on opacity { + NumberAnimation { + duration: 150 + easing.type: Easing.InOutQuad + } + } + } + } + } + + + Rectangle { + id: tabs + color: Theme.surface + width: screen.width / 9 + height: panelMain.height + topLeftRadius: 20 + bottomLeftRadius: 20 + border.color: Theme.outline + border.width: 1 + + Column { + width: parent.width + spacing: 0 + topPadding: 8 + + Repeater { + id: repeater + model: [ + { icon: "tune", text: "General" }, + { icon: "space_dashboard", text: "Bar" }, + { icon: "schedule", text: "Time & Weather" }, + { icon: "photo_camera", text: "Recording" }, + { icon: "wifi", text: "Network" }, + { icon: "monitor", text: "Display" }, + { icon: "settings_suggest", text: "Misc" }, + { icon: "info", text: "About" } + ] + + delegate: Column { + width: tabs.width + height: 40 + + Item { + width: parent.width + height: 39 + + RowLayout { + anchors.fill: parent + spacing: 8 + + + Rectangle { + id: activeIndicator + Layout.leftMargin: 8 + Layout.preferredWidth: 3 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignVCenter + radius: 2 + color: Theme.accentPrimary + opacity: index === 0 ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 200 } } + } + + + Label { + id: icon + text: modelData.icon + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: index === 0 ? Theme.accentPrimary : Theme.textPrimary + opacity: index === 0 ? 1 : 0.8 + Layout.leftMargin: 20 + Layout.preferredWidth: 24 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignVCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + + Label { + id: label + text: modelData.text + font.pixelSize: 12 + color: index === 0 ? Theme.accentPrimary : Theme.textSecondary + font.weight: index === 0 ? Font.DemiBold : Font.Normal + Layout.fillWidth: true + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.leftMargin: 4 + Layout.rightMargin: 16 + verticalAlignment: Text.AlignVCenter + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onClicked: { + + const newComponent = { + 0: generalSettings, + 1: barSettings, + 2: timeWeatherSettings, + 3: recordingSettings, + 4: networkSettings, + 5: displaySettings, + 6: miscSettings, + 7: aboutSettings + }[index]; + + + const tabNames = [ + "General", + "Bar", + "Time & Weather", + "Recording", + "Network", + "Display", + "Misc", + "About" + ]; + tabName.text = tabNames[index]; + + + if (settingsLoader.opacity === 1) { + + settingsLoader2.sourceComponent = newComponent; + settingsLoader.opacity = 0; + settingsLoader2.opacity = 1; + } else { + + settingsLoader.sourceComponent = newComponent; + settingsLoader2.opacity = 0; + settingsLoader.opacity = 1; + } + + + for (let i = 0; i < repeater.count; i++) { + let item = repeater.itemAt(i); + if (item) { + + let containerItem = item.children[0]; + + let rowLayout = containerItem.children[0]; + + let indicator = rowLayout.children[0]; + let icon = rowLayout.children[1]; + let label = rowLayout.children[2]; + + indicator.opacity = i === index ? 1 : 0; + icon.color = i === index ? Theme.accentPrimary : Theme.textPrimary; + icon.opacity = i === index ? 1 : 0.8; + label.color = i === index ? Theme.accentPrimary : Theme.textSecondary; + label.font.weight = i === index ? Font.Bold : Font.Normal; + } + } + } + } + } + + + Rectangle { + width: parent.width + height: 1 + color: Theme.outline + opacity: 0.6 + visible: index < (repeater.count - 1) + } + } + } + } + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/About.qml b/Widgets/SettingsWindow/Tabs/About.qml new file mode 100644 index 0000000..3ee1942 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/About.qml @@ -0,0 +1,405 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Effects +import Quickshell +import Quickshell.Io +import qs.Settings +import qs.Components + +Item { + id: root + + property string latestVersion: "Unknown" + property string currentVersion: "Unknown" + property var contributors: [] + property string githubDataPath: Settings.settingsDir + "github_data.json" + + Process { + id: currentVersionProcess + command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"] + stdout: StdioCollector { + onStreamFinished: { + const version = text.trim() + if (version && version !== "Unknown") { + root.currentVersion = version + } else { + + currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"] + currentVersionProcess.running = true + } + } + } + Component.onCompleted: { + running = true + } + } + + FileView { + id: githubDataFile + path: root.githubDataPath + blockLoading: true + printErrors: true + watchChanges: true + + JsonAdapter { + id: githubData + property string version: "Unknown" + property var contributors: [] + property double timestamp: 0 + } + + onFileChanged: githubDataFile.reload() + onLoaded: loadFromFile() + onLoadFailed: function(error) { + console.log("GitHub data file doesn't exist yet, creating it...") + githubData.version = "Unknown" + githubData.contributors = [] + githubData.timestamp = 0 + githubDataFile.writeAdapter() + fetchFromGitHub() + } + Component.onCompleted: if (path) reload() + } + + function loadFromFile() { + const now = Date.now() + const data = githubData + + if (!data.timestamp || (now - data.timestamp > 3600000)) { + console.log("[About] Cache expired or missing, fetching new data from GitHub...") + fetchFromGitHub() + return + } + console.log("[About] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)") + if (data.version) { + root.latestVersion = data.version + } + if (data.contributors) { + root.contributors = data.contributors + } + } + + Process { + id: versionProcess + command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/releases/latest"] + stdout: StdioCollector { + onStreamFinished: { + try { + const data = JSON.parse(text) + if (data.tag_name) { + const version = data.tag_name + githubData.version = version + root.latestVersion = version + console.log("[About] Latest version fetched from GitHub:", version) + } else { + console.log("No tag_name in GitHub response") + } + saveData() + } catch (e) { + console.error("Failed to parse version:", e) + } + } + } + } + + Process { + id: contributorsProcess + command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/contributors?per_page=100"] + stdout: StdioCollector { + onStreamFinished: { + try { + const data = JSON.parse(text) + githubData.contributors = data || [] + root.contributors = githubData.contributors + console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors") + saveData() + } catch (e) { + console.error("Failed to parse contributors:", e) + root.contributors = [] + } + } + } + } + + function fetchFromGitHub() { + versionProcess.running = true + contributorsProcess.running = true + } + + function saveData() { + githubData.timestamp = Date.now() + Qt.callLater(() => { + githubDataFile.writeAdapter() + }) + } + + Item { + anchors.fill: parent + + + ColumnLayout { + id: mainLayout + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: 8 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 32 + } + + Text { + text: "Noctalia" + font.pixelSize: 24 + font.bold: true + color: Theme.textPrimary + Layout.alignment: Qt.AlignCenter + } + + GridLayout { + Layout.alignment: Qt.AlignCenter + columns: 2 + rowSpacing: 4 + columnSpacing: 8 + + Text { + text: "Latest Version:" + font.pixelSize: 16 + color: Theme.textSecondary + Layout.alignment: Qt.AlignRight + } + + Text { + text: root.latestVersion + font.pixelSize: 16 + color: Theme.textPrimary + font.bold: true + } + + Text { + text: "Installed Version:" + font.pixelSize: 16 + color: Theme.textSecondary + Layout.alignment: Qt.AlignRight + } + + Text { + text: root.currentVersion + font.pixelSize: 16 + color: Theme.textPrimary + font.bold: true + } + } + + + Rectangle { + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 8 + Layout.preferredWidth: updateText.implicitWidth + 46 + Layout.preferredHeight: 32 + radius: 20 + color: updateArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 + visible: { + if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") { + return false + } + const latest = root.latestVersion.replace("v", "").split(".") + const current = root.currentVersion.replace("v", "").split(".") + + + for (let i = 0; i < Math.max(latest.length, current.length); i++) { + const l = parseInt(latest[i] || "0") + const c = parseInt(current[i] || "0") + if (l > c) return true + if (l < c) return false + } + return false + } + + RowLayout { + anchors.centerIn: parent + spacing: 8 + + Text { + text: "system_update" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + } + + Text { + id: updateText + text: "Download latest release" + font.pixelSize: 14 + color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + } + } + + MouseArea { + id: updateArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]) + } + } + } + + Text { + text: "Description something something <.< I hate writing text..." + font.pixelSize: 14 + color: Theme.textSecondary + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 16 + } + + + ColumnLayout { + Layout.fillWidth: true + Layout.topMargin: 32 + Layout.leftMargin: 32 + Layout.rightMargin: 32 + spacing: 16 + + RowLayout { + Layout.alignment: Qt.AlignCenter + spacing: 8 + + Text { + text: "Contributors" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "(" + root.contributors.length + ")" + font.pixelSize: 14 + color: Theme.textSecondary + } + } + + ScrollView { + Layout.fillWidth: true + Layout.preferredHeight: 300 + clip: true + + Item { + anchors.fill: parent + + GridView { + id: contributorsGrid + anchors.centerIn: parent + width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200) + height: parent.height + cellWidth: 200 + cellHeight: 110 + model: root.contributors + + delegate: Rectangle { + width: contributorsGrid.cellWidth - 4 + height: contributorsGrid.cellHeight - 10 + radius: 20 + color: contributorArea.containsMouse ? Theme.highlight : "transparent" + + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 12 + + + Item { + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 + + Image { + id: avatarImage + anchors.fill: parent + source: modelData.avatar_url || "" + sourceSize: Qt.size(80, 80) + visible: false + mipmap: true + smooth: true + asynchronous: true + fillMode: Image.PreserveAspectCrop + cache: true + } + + MultiEffect { + anchors.fill: parent + source: avatarImage + maskEnabled: true + maskSource: mask + } + + Item { + id: mask + anchors.fill: parent + layer.enabled: true + visible: false + Rectangle { + anchors.fill: parent + radius: avatarImage.width / 2 + } + } + + + Text { + anchors.centerIn: parent + text: "person" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary + visible: !avatarImage.source || avatarImage.status !== Image.Ready + } + } + + + ColumnLayout { + spacing: 4 + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + + Text { + text: modelData.login || "Unknown" + font.pixelSize: 13 + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: (modelData.contributions || 0) + " commits" + font.pixelSize: 11 + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary + } + } + } + + MouseArea { + id: contributorArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (modelData.html_url) { + Quickshell.execDetached(["xdg-open", modelData.html_url]) + } + } + } + } + } + } + } + } + + + } + } + } \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Bar.qml b/Widgets/SettingsWindow/Tabs/Bar.qml new file mode 100644 index 0000000..53e1cc7 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Bar.qml @@ -0,0 +1,380 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components + +ColumnLayout { + id: root + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Bar Elements" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Active Window Icon" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display the icon of the currently focused window in the bar" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: activeWindowIconSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: activeWindowIconThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Active Window" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display the title of the currently focused window below the bar" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: activeWindowSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: activeWindowThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showActiveWindow ? activeWindowSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showActiveWindow = !Settings.settings.showActiveWindow; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show System Info" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display system information (CPU, RAM, etc.) in the bar" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: systemInfoSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: systemInfoThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Taskbar" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display a taskbar showing currently open windows" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: taskbarSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: taskbarThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showTaskbar = !Settings.settings.showTaskbar; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Media" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display media controls and information in the bar" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: mediaSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: mediaThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml new file mode 100644 index 0000000..e8b9c8a --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml @@ -0,0 +1,97 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components + +Rectangle { + id: root + width: 64 + height: 32 + radius: 16 + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + + property bool useFahrenheit: Settings.settings.useFahrenheit + + + Rectangle { + id: slider + width: parent.width / 2 - 4 + height: parent.height - 4 + radius: 14 + color: Theme.accentPrimary + x: 2 + (useFahrenheit ? parent.width / 2 : 0) + y: 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + + Row { + anchors.fill: parent + spacing: 0 + + + Item { + width: parent.width / 2 + height: parent.height + + Text { + anchors.centerIn: parent + text: "°C" + font.pixelSize: 13 + font.bold: !useFahrenheit + color: !useFahrenheit ? Theme.onAccent : Theme.textPrimary + + Behavior on color { + ColorAnimation { duration: 200 } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (useFahrenheit) { + Settings.settings.useFahrenheit = false; + } + } + } + } + + + Item { + width: parent.width / 2 + height: parent.height + + Text { + anchors.centerIn: parent + text: "°F" + font.pixelSize: 13 + font.bold: useFahrenheit + color: useFahrenheit ? Theme.onAccent : Theme.textPrimary + + Behavior on color { + ColorAnimation { duration: 200 } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (!useFahrenheit) { + Settings.settings.useFahrenheit = true; + } + } + } + } + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Display.qml b/Widgets/SettingsWindow/Tabs/Display.qml new file mode 100644 index 0000000..ee4a159 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Display.qml @@ -0,0 +1,354 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs.Settings +import qs.Components + +ColumnLayout { + id: root + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + // Get list of available monitors/screens + property var monitors: Quickshell.screens || [] + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Monitor Selection" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Bar Monitors" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Select which monitors to display the top panel/bar on" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + } + + + Flow { + Layout.fillWidth: true + spacing: 8 + + Repeater { + model: root.monitors + delegate: Rectangle { + id: barCheckbox + property bool isChecked: false + + Component.onCompleted: { + // Initialize checkbox state from settings + let monitors = Settings.settings.barMonitors || []; + isChecked = monitors.includes(modelData.name); + } + + width: checkboxContent.implicitWidth + 16 + height: 32 + radius: 16 + color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: isChecked ? Theme.accentPrimary : Theme.outline + border.width: 1 + + RowLayout { + id: checkboxContent + anchors.centerIn: parent + spacing: 6 + + Text { + text: barCheckbox.isChecked ? "check" : "" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 + color: barCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary + visible: barCheckbox.isChecked + } + + Text { + text: modelData.name || "Unknown" + font.pixelSize: 12 + color: barCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + isChecked = !isChecked; + + // Update settings array when checkbox is toggled + let monitors = Settings.settings.barMonitors || []; + monitors = [...monitors]; // Create copy to trigger reactivity + + if (isChecked) { + if (!monitors.includes(modelData.name)) { + monitors.push(modelData.name); + } + } else { + monitors = monitors.filter(name => name !== modelData.name); + } + + Settings.settings.barMonitors = monitors; + console.log("Bar monitors updated:", JSON.stringify(monitors)); + } + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Dock Monitors" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Select which monitors to display the application dock on" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + } + + + Flow { + Layout.fillWidth: true + spacing: 8 + + Repeater { + model: root.monitors + delegate: Rectangle { + id: dockCheckbox + property bool isChecked: false + + Component.onCompleted: { + // Initialize with current settings + let monitors = Settings.settings.dockMonitors || []; + isChecked = monitors.includes(modelData.name); + } + + width: checkboxContent.implicitWidth + 16 + height: 32 + radius: 16 + color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: isChecked ? Theme.accentPrimary : Theme.outline + border.width: 1 + + RowLayout { + id: checkboxContent + anchors.centerIn: parent + spacing: 6 + + Text { + text: dockCheckbox.isChecked ? "check" : "" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 + color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary + visible: dockCheckbox.isChecked + } + + Text { + text: modelData.name || "Unknown" + font.pixelSize: 12 + color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // Toggle state immediately for UI responsiveness + isChecked = !isChecked; + + // Update settings + let monitors = Settings.settings.dockMonitors || []; + monitors = [...monitors]; // Copy array + + if (isChecked) { + // Add to array if not already there + if (!monitors.includes(modelData.name)) { + monitors.push(modelData.name); + } + } else { + // Remove from array + monitors = monitors.filter(name => name !== modelData.name); + } + + Settings.settings.dockMonitors = monitors; + console.log("Dock monitors updated:", JSON.stringify(monitors)); + } + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Notification Monitors" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Select which monitors to display system notifications on" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + } + + + Flow { + Layout.fillWidth: true + spacing: 8 + + Repeater { + model: root.monitors + delegate: Rectangle { + id: notificationCheckbox + property bool isChecked: false + + Component.onCompleted: { + // Initialize with current settings + let monitors = Settings.settings.notificationMonitors || []; + isChecked = monitors.includes(modelData.name); + } + + width: checkboxContent.implicitWidth + 16 + height: 32 + radius: 16 + color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: isChecked ? Theme.accentPrimary : Theme.outline + border.width: 1 + + RowLayout { + id: checkboxContent + anchors.centerIn: parent + spacing: 6 + + Text { + text: notificationCheckbox.isChecked ? "check" : "" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 + color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary + visible: notificationCheckbox.isChecked + } + + Text { + text: modelData.name || "Unknown" + font.pixelSize: 12 + color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // Toggle state immediately for UI responsiveness + isChecked = !isChecked; + + // Update settings + let monitors = Settings.settings.notificationMonitors || []; + monitors = [...monitors]; // Copy array + + if (isChecked) { + // Add to array if not already there + if (!monitors.includes(modelData.name)) { + monitors.push(modelData.name); + } + } else { + // Remove from array + monitors = monitors.filter(name => name !== modelData.name); + } + + Settings.settings.notificationMonitors = monitors; + console.log("Notification monitors updated:", JSON.stringify(monitors)); + } + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml new file mode 100644 index 0000000..4d0ffda --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -0,0 +1,339 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components + +ColumnLayout { + id: root + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Profile" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 2 + Layout.fillWidth: true + + Text { + text: "Profile Image" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Your profile picture displayed in various places throughout the shell" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + Layout.bottomMargin: 4 + } + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + Rectangle { + width: 48 + height: 48 + radius: 24 + + Rectangle { + anchors.fill: parent + color: "transparent" + radius: 24 + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 2 + z: 2 + } + + Avatar {} + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: profileImageInput + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.profileImage + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.profileImage = text; + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: profileImageInput.forceActiveFocus() + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 16 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "User Interface" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Corners" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display rounded corners on screen edges" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: cornersSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: cornersThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showCorners = !Settings.settings.showCorners; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 4 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Dock" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display a dock at the bottom of the screen for quick access to applications" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: dockSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: dockThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showDock ? dockSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showDock = !Settings.settings.showDock; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 4 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Dim Desktop" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Dim the desktop when panels or menus are open" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: dimSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: dimThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.dimPanels = !Settings.settings.dimPanels; + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Misc.qml b/Widgets/SettingsWindow/Tabs/Misc.qml new file mode 100644 index 0000000..19d0e74 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Misc.qml @@ -0,0 +1,137 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components + +ColumnLayout { + id: root + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Media" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Visualizer Type" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Choose the style of the audio visualizer" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + Layout.bottomMargin: 4 + } + + ComboBox { + id: visualizerTypeComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["radial", "fire", "diamond"] + currentIndex: model.indexOf(Settings.settings.visualizerType) + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing + text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1) + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: visualizerTypeComboBox.width - width - 12 + y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: visualizerTypeComboBox.height + width: visualizerTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null + currentIndex: visualizerTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: visualizerTypeComboBox.width + contentItem: Text { + text: modelData.charAt(0).toUpperCase() + modelData.slice(1) + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: visualizerTypeComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.visualizerType = model[index]; + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml new file mode 100644 index 0000000..3f79e32 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -0,0 +1,193 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Bluetooth +import qs.Settings +import qs.Components + +ColumnLayout { + id: root + spacing: 24 + + Component.onCompleted: { + + Quickshell.execDetached(["nmcli", "-t", "-f", "WIFI", "radio"]) + } + + + ColumnLayout { + spacing: 16 + Layout.fillWidth: true + + Text { + text: "Wi-Fi" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Enable Wi-Fi" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Turn Wi-Fi radio on or off" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: wifiSwitch + width: 52 + height: 32 + radius: 16 + property bool checked: Settings.settings.wifiEnabled + color: checked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: checked ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wifiThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: wifiSwitch.checked ? wifiSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled + Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"]) + } + } + } + } + } + } + + + ColumnLayout { + spacing: 16 + Layout.fillWidth: true + Layout.topMargin: 16 + + Text { + text: "Bluetooth" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Enable Bluetooth" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Turn Bluetooth radio on or off" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: bluetoothSwitch + width: 52 + height: 32 + radius: 16 + property bool checked: Settings.settings.bluetoothEnabled + color: checked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: checked ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: bluetoothThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: bluetoothSwitch.checked ? bluetoothSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (Bluetooth.defaultAdapter) { + Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled + Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled + if (Bluetooth.defaultAdapter.enabled) { + Bluetooth.defaultAdapter.discovering = true + } + } + } + } + } + } + } + } + + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Record.qml b/Widgets/SettingsWindow/Tabs/Record.qml new file mode 100644 index 0000000..b1faa8b --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Record.qml @@ -0,0 +1,19 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components + +ColumnLayout { + id: root + spacing: 24 + + Text { + text: "Coming soon..." + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 32 + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Recording.qml b/Widgets/SettingsWindow/Tabs/Recording.qml new file mode 100644 index 0000000..1d5677f --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Recording.qml @@ -0,0 +1,812 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components + +ColumnLayout { + id: root + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + ScrollView { + id: scrollView + Layout.fillWidth: true + Layout.fillHeight: true + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + ColumnLayout { + width: scrollView.availableWidth + spacing: 0 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Screen Recording" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Output Directory" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Directory where screen recordings will be saved" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: videoPathInput + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.videoPath = text; + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: videoPathInput.forceActiveFocus() + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Frame Rate" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Target frame rate for screen recordings (default: 60)" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + SpinBox { + id: frameRateSpinBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + from: 24 + to: 144 + value: Settings.settings.recordingFrameRate || 60 + stepSize: 1 + + onValueChanged: { + Settings.settings.recordingFrameRate = value; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: frameRateSpinBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: TextInput { + text: frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale) + font.pixelSize: 13 + color: Theme.textPrimary + selectionColor: Theme.accentPrimary + selectedTextColor: Theme.onAccent + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + readOnly: false + selectByMouse: true + validator: IntValidator { + bottom: frameRateSpinBox.from + top: frameRateSpinBox.to + } + inputMethodHints: Qt.ImhDigitsOnly + + onTextChanged: { + var newValue = parseInt(text); + if (!isNaN(newValue) && newValue >= frameRateSpinBox.from && newValue <= frameRateSpinBox.to) { + frameRateSpinBox.value = newValue; + } + } + + onEditingFinished: { + var newValue = parseInt(text); + if (isNaN(newValue) || newValue < frameRateSpinBox.from || newValue > frameRateSpinBox.to) { + text = frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale); + } + } + } + + up.indicator: Rectangle { + x: parent.width - width + height: parent.height + width: height + color: "transparent" + radius: 16 + + Text { + text: "add" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.textPrimary + anchors.centerIn: parent + } + } + + down.indicator: Rectangle { + x: 0 + height: parent.height + width: height + color: "transparent" + radius: 16 + + Text { + text: "remove" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.textPrimary + anchors.centerIn: parent + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Audio Source" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Audio source to capture during recording" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: audioSourceComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["default_output", "default_input", "both"] + currentIndex: model.indexOf(Settings.settings.recordingAudioSource || "default_output") + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: audioSourceComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: audioSourceComboBox.indicator.width + audioSourceComboBox.spacing + text: { + switch(audioSourceComboBox.currentText) { + case "default_output": return "System Audio"; + case "default_input": return "Microphone"; + case "both": return "System Audio + Microphone"; + default: return audioSourceComboBox.currentText; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: audioSourceComboBox.width - width - 12 + y: audioSourceComboBox.topPadding + (audioSourceComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: audioSourceComboBox.height + width: audioSourceComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: audioSourceComboBox.popup.visible ? audioSourceComboBox.delegateModel : null + currentIndex: audioSourceComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: audioSourceComboBox.width + contentItem: Text { + text: { + switch(modelData) { + case "default_output": return "System Audio"; + case "default_input": return "Microphone"; + case "both": return "System Audio + Microphone"; + default: return modelData; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: audioSourceComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.recordingAudioSource = model[index]; + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Video Quality" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Higher quality results in larger file sizes" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: qualityComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["medium", "high", "very_high", "ultra"] + currentIndex: model.indexOf(Settings.settings.recordingQuality || "very_high") + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: qualityComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: qualityComboBox.indicator.width + qualityComboBox.spacing + text: { + switch(qualityComboBox.currentText) { + case "medium": return "Medium"; + case "high": return "High"; + case "very_high": return "Very High"; + case "ultra": return "Ultra"; + default: return qualityComboBox.currentText; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: qualityComboBox.width - width - 12 + y: qualityComboBox.topPadding + (qualityComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: qualityComboBox.height + width: qualityComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: qualityComboBox.popup.visible ? qualityComboBox.delegateModel : null + currentIndex: qualityComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: qualityComboBox.width + contentItem: Text { + text: { + switch(modelData) { + case "medium": return "Medium"; + case "high": return "High"; + case "very_high": return "Very High"; + case "ultra": return "Ultra"; + default: return modelData; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: qualityComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.recordingQuality = model[index]; + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Video Codec" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Different codecs offer different compression and compatibility" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: codecComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["h264", "hevc", "av1", "vp8", "vp9"] + currentIndex: model.indexOf(Settings.settings.recordingCodec || "h264") + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: codecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: codecComboBox.indicator.width + codecComboBox.spacing + text: codecComboBox.currentText.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: codecComboBox.width - width - 12 + y: codecComboBox.topPadding + (codecComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: codecComboBox.height + width: codecComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: codecComboBox.popup.visible ? codecComboBox.delegateModel : null + currentIndex: codecComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: codecComboBox.width + contentItem: Text { + text: modelData.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: codecComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.recordingCodec = model[index]; + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Audio Codec" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Opus is recommended for best performance and smallest audio size" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: audioCodecComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["opus", "aac"] + currentIndex: model.indexOf(Settings.settings.audioCodec || "opus") + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: audioCodecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: audioCodecComboBox.indicator.width + audioCodecComboBox.spacing + text: audioCodecComboBox.currentText.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: audioCodecComboBox.width - width - 12 + y: audioCodecComboBox.topPadding + (audioCodecComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: audioCodecComboBox.height + width: audioCodecComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: audioCodecComboBox.popup.visible ? audioCodecComboBox.delegateModel : null + currentIndex: audioCodecComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: audioCodecComboBox.width + contentItem: Text { + text: modelData.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: audioCodecComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.audioCodec = model[index]; + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Color Range" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Limited is recommended for better compatibility" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: colorRangeComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["limited", "full"] + currentIndex: model.indexOf(Settings.settings.colorRange || "limited") + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: colorRangeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: colorRangeComboBox.indicator.width + colorRangeComboBox.spacing + text: colorRangeComboBox.currentText.charAt(0).toUpperCase() + colorRangeComboBox.currentText.slice(1) + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: colorRangeComboBox.width - width - 12 + y: colorRangeComboBox.topPadding + (colorRangeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: colorRangeComboBox.height + width: colorRangeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: colorRangeComboBox.popup.visible ? colorRangeComboBox.delegateModel : null + currentIndex: colorRangeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: colorRangeComboBox.width + contentItem: Text { + text: modelData.charAt(0).toUpperCase() + modelData.slice(1) + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: colorRangeComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.colorRange = model[index]; + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Cursor" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Record mouse cursor in the video" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: cursorSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: cursorThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showCursor ? cursorSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showCursor = !Settings.settings.showCursor; + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 24 + } + } + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml new file mode 100644 index 0000000..7ee1c75 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -0,0 +1,283 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components +import qs.Widgets.SettingsWindow.Tabs.Components + +ColumnLayout { + id: root + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Time" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use 12 Hour Clock" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: use12HourClockSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: use12HourClockThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.use12HourClock = !Settings.settings.use12HourClock; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "US Style Date" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: reverseDayMonthSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: reverseDayMonthThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; + } + } + } + } + } + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + Layout.topMargin: 16 + + Text { + text: "Weather" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "City" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Your city name for weather information" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: cityInput + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.weatherCity + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + focus: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhNone + + onTextChanged: { + Settings.settings.weatherCity = text; + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: { + cityInput.forceActiveFocus(); + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Temperature Unit" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Choose between Celsius and Fahrenheit" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + UnitSelector {} + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } +} \ No newline at end of file diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index 0c74453..3451e25 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -1,13 +1,15 @@ import QtQuick import QtQuick.Layouts +import QtQuick.Effects import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import Quickshell.Widgets +import qs.Components import qs.Settings Rectangle { id: profileSettingsCard Layout.fillWidth: true - Layout.preferredHeight: 650 + Layout.preferredHeight: 690 color: Theme.surface radius: 18 @@ -53,47 +55,23 @@ Rectangle { spacing: 8 Layout.fillWidth: true + // Profile image Rectangle { - width: 40 - height: 40 - radius: 20 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 + width: 48 + height: 48 + radius: 24 - Image { - id: avatarImage + // Border + Rectangle { 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 !== "" + color: "transparent" + radius: 24 + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 2 + z: 2 } - Text { - anchors.centerIn: parent - text: "person" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - visible: Settings.settings.profileImage === "" - } + Avatar {} } Rectangle { @@ -121,7 +99,7 @@ Rectangle { activeFocusOnTab: true inputMethodHints: Qt.ImhUrlCharactersOnly onTextChanged: { - Settings.settings.profileImage = text + Settings.settings.profileImage = text; } MouseArea { anchors.fill: parent @@ -182,7 +160,7 @@ Rectangle { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon + Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; } } } @@ -237,7 +215,7 @@ Rectangle { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar + Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; } } } @@ -292,7 +270,7 @@ Rectangle { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - Settings.settings.showCorners = !Settings.settings.showCorners + Settings.settings.showCorners = !Settings.settings.showCorners; } } } @@ -347,7 +325,62 @@ Rectangle { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - Settings.settings.showTaskbar = !Settings.settings.showTaskbar + Settings.settings.showTaskbar = !Settings.settings.showTaskbar; + } + } + } + } + + // Show Dock Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Show Dock" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: dockSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: dockThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showDock ? taskbarSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showDock = !Settings.settings.showDock; } } } @@ -402,7 +435,7 @@ Rectangle { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar + Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; } } } @@ -457,7 +490,7 @@ Rectangle { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - Settings.settings.dimPanels = !Settings.settings.dimPanels + Settings.settings.dimPanels = !Settings.settings.dimPanels; } } } @@ -596,7 +629,7 @@ Rectangle { activeFocusOnTab: true inputMethodHints: Qt.ImhUrlCharactersOnly onTextChanged: { - Settings.settings.videoPath = text + Settings.settings.videoPath = text; } MouseArea { anchors.fill: parent @@ -607,4 +640,4 @@ Rectangle { } } } -} \ No newline at end of file +} diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index d4d4ccf..056f6f5 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -22,7 +22,7 @@ PanelWindow { Rectangle { anchors.fill: parent color: Theme.backgroundPrimary - radius: 24 + radius: 20 z: 0 ColumnLayout { @@ -31,7 +31,6 @@ PanelWindow { anchors.leftMargin: 32 anchors.rightMargin: 32 anchors.topMargin: 32 - spacing: 24 // Header @@ -85,14 +84,14 @@ PanelWindow { } } - // Tabs bar (moved here) + // Tabs bar (reordered) Tabs { id: settingsTabs Layout.fillWidth: true tabsModel: [ - { icon: "cloud", label: "Weather" }, { icon: "settings", label: "System" }, - { icon: "wallpaper", label: "Wallpaper" } + { icon: "wallpaper", label: "Wallpaper" }, + { icon: "cloud", label: "Weather" } ] } @@ -115,7 +114,32 @@ PanelWindow { id: tabContentLoader anchors.top: parent.top width: parent.width - sourceComponent: settingsTabs.currentIndex === 0 ? weatherTab : settingsTabs.currentIndex === 1 ? systemTab : wallpaperTab + sourceComponent: settingsTabs.currentIndex === 0 ? systemTab : settingsTabs.currentIndex === 1 ? wallpaperTab : weatherTab + } + } + + Component { + id: systemTab + ColumnLayout { + anchors.fill: parent + ProfileSettings { + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + anchors.margins: 16 + } + } + } + + Component { + id: wallpaperTab + ColumnLayout { + anchors.fill: parent + WallpaperSettings { + id: wallpaperSettings + Layout.fillWidth: true + Layout.alignment: Qt.AlignTop + anchors.margins: 16 + } } } @@ -130,29 +154,6 @@ PanelWindow { } } } - Component { - id: systemTab - ColumnLayout { - anchors.fill: parent - ProfileSettings { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } - Component { - id: wallpaperTab - ColumnLayout { - anchors.fill: parent - WallpaperSettings { - id: wallpaperSettings - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } } } } @@ -160,7 +161,6 @@ PanelWindow { // Function to open the modal and initialize temp values function openSettings() { visible = true; - // Force focus on the text input after a short delay focusTimer.start(); } @@ -174,20 +174,16 @@ PanelWindow { interval: 100 repeat: false onTriggered: { - if (visible) - // Focus will be handled by the individual components - {} - } - } - - // Release focus when modal becomes invisible - onVisibleChanged: { - if (!visible) { - // Focus will be handled by the individual components - if (typeof weather !== 'undefined' && weather !== null && weather.fetchCityWeather) { - weather.fetchCityWeather(); + if (visible) { + // Focus logic can go here if needed } } } -} + // Refresh weather data when hidden + onVisibleChanged: { + if (!visible && typeof weather !== 'undefined' && weather !== null && weather.fetchCityWeather) { + weather.fetchCityWeather(); + } + } +} diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml index c824721..730a935 100644 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ b/Widgets/Sidebar/Config/WallpaperSettings.qml @@ -1,12 +1,13 @@ import QtQuick -import QtQuick.Layouts import QtQuick.Controls +import QtQuick.Layouts import qs.Settings Rectangle { id: wallpaperSettingsCard + Layout.fillWidth: true - Layout.preferredHeight: 720 + Layout.preferredHeight: Settings.settings.useSWWW ? 720 : 360 color: Theme.surface radius: 18 @@ -15,25 +16,28 @@ Rectangle { anchors.margins: 18 spacing: 12 - // Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - Text { - text: "image" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - Text { - text: "Wallpaper Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary + + RowLayout { Layout.fillWidth: true + spacing: 12 + + Text { + text: "image" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.accentPrimary + } + + Text { + text: "Wallpaper Settings" + font.family: Theme.fontFamily + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } } - } + ColumnLayout { spacing: 8 @@ -47,7 +51,7 @@ Rectangle { color: Theme.textPrimary } - // Folder Path Input + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 40 @@ -55,8 +59,10 @@ Rectangle { 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 @@ -77,72 +83,22 @@ Rectangle { onTextChanged: { Settings.settings.wallpaperFolder = text; } + MouseArea { anchors.fill: parent cursorShape: Qt.IBeamCursor onClicked: folderInput.forceActiveFocus() } + } + } + } - // Use SWWW Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - Item { - Layout.fillWidth: true - } - // Custom Material 3 Switch - Rectangle { - id: swwwSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: swwwThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - } - } - - // Random Wallpaper Setting RowLayout { spacing: 8 Layout.fillWidth: true @@ -162,6 +118,7 @@ Rectangle { // Custom Material 3 Switch Rectangle { id: randomWallpaperSwitch + width: 52 height: 32 radius: 16 @@ -171,6 +128,7 @@ Rectangle { Rectangle { id: randomWallpaperThumb + width: 28 height: 28 radius: 14 @@ -185,7 +143,9 @@ Rectangle { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -195,10 +155,12 @@ Rectangle { Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; } } + } + } - // Use Wallpaper Theme Setting + RowLayout { spacing: 8 Layout.fillWidth: true @@ -218,6 +180,7 @@ Rectangle { // Custom Material 3 Switch Rectangle { id: wallpaperThemeSwitch + width: 52 height: 32 radius: 16 @@ -227,6 +190,7 @@ Rectangle { Rectangle { id: wallpaperThemeThumb + width: 28 height: 28 radius: 14 @@ -241,7 +205,9 @@ Rectangle { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -251,10 +217,12 @@ Rectangle { Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; } } + } + } - // Wallpaper Interval Setting + ColumnLayout { spacing: 12 Layout.fillWidth: true @@ -262,6 +230,7 @@ Rectangle { RowLayout { Layout.fillWidth: true + Text { text: "Wallpaper Interval (seconds)" font.pixelSize: 13 @@ -278,16 +247,21 @@ Rectangle { font.pixelSize: 13 color: Theme.textPrimary } + } Slider { id: intervalSlider + Layout.fillWidth: true from: 10 to: 900 stepSize: 10 value: Settings.settings.wallpaperInterval snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.wallpaperInterval = Math.round(value); + } background: Rectangle { x: intervalSlider.leftPadding @@ -305,6 +279,7 @@ Rectangle { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -318,17 +293,78 @@ Rectangle { border.width: 2 } - onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); - } } + } - // Resize Mode Setting + + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Use SWWW" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: swwwSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: swwwThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useSWWW = !Settings.settings.useSWWW; + } + } + + } + + } + + ColumnLayout { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 + visible: Settings.settings.useSWWW Text { text: "Resize Mode" @@ -339,10 +375,14 @@ Rectangle { ComboBox { id: resizeComboBox + Layout.fillWidth: true Layout.preferredHeight: 40 model: ["no", "crop", "fit", "stretch"] currentIndex: model.indexOf(Settings.settings.wallpaperResize) + onActivated: { + Settings.settings.wallpaperResize = model[index]; + } background: Rectangle { implicitWidth: 120 @@ -385,7 +425,9 @@ Rectangle { model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null currentIndex: resizeComboBox.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator {} + ScrollIndicator.vertical: ScrollIndicator { + } + } background: Rectangle { @@ -394,10 +436,13 @@ Rectangle { border.width: 1 radius: 16 } + } delegate: ItemDelegate { width: resizeComboBox.width + highlighted: resizeComboBox.highlightedIndex === index + contentItem: Text { text: modelData font.family: Theme.fontFamily @@ -406,24 +451,23 @@ Rectangle { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - highlighted: resizeComboBox.highlightedIndex === index background: Rectangle { color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } + } - onActivated: { - Settings.settings.wallpaperResize = model[index]; - } } + } - // Transition Type Setting + ColumnLayout { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 + visible: Settings.settings.useSWWW Text { text: "Transition Type" @@ -434,10 +478,14 @@ Rectangle { ComboBox { id: transitionTypeComboBox + Layout.fillWidth: true Layout.preferredHeight: 40 model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] currentIndex: model.indexOf(Settings.settings.transitionType) + onActivated: { + Settings.settings.transitionType = model[index]; + } background: Rectangle { implicitWidth: 120 @@ -480,7 +528,9 @@ Rectangle { model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null currentIndex: transitionTypeComboBox.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator {} + ScrollIndicator.vertical: ScrollIndicator { + } + } background: Rectangle { @@ -489,10 +539,13 @@ Rectangle { border.width: 1 radius: 16 } + } delegate: ItemDelegate { width: transitionTypeComboBox.width + highlighted: transitionTypeComboBox.highlightedIndex === index + contentItem: Text { text: modelData font.family: Theme.fontFamily @@ -501,27 +554,27 @@ Rectangle { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - highlighted: transitionTypeComboBox.highlightedIndex === index background: Rectangle { color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } + } - onActivated: { - Settings.settings.transitionType = model[index]; - } } + } - // Transition FPS Setting + ColumnLayout { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 + visible: Settings.settings.useSWWW RowLayout { Layout.fillWidth: true + Text { text: "Transition FPS" font.pixelSize: 13 @@ -538,16 +591,21 @@ Rectangle { font.pixelSize: 13 color: Theme.textPrimary } + } Slider { id: fpsSlider + Layout.fillWidth: true from: 30 to: 500 stepSize: 5 value: Settings.settings.transitionFps snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionFps = Math.round(value); + } background: Rectangle { x: fpsSlider.leftPadding @@ -565,6 +623,7 @@ Rectangle { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -578,20 +637,20 @@ Rectangle { border.width: 2 } - onMoved: { - Settings.settings.transitionFps = Math.round(value); - } } + } - // Transition Duration Setting + ColumnLayout { spacing: 12 Layout.fillWidth: true Layout.topMargin: 16 - + visible: Settings.settings.useSWWW + RowLayout { Layout.fillWidth: true + Text { text: "Transition Duration (seconds)" font.pixelSize: 13 @@ -608,16 +667,21 @@ Rectangle { font.pixelSize: 13 color: Theme.textPrimary } + } Slider { id: durationSlider + Layout.fillWidth: true - from: 0.250 - to: 10.0 - stepSize: 0.050 + from: 0.25 + to: 10 + stepSize: 0.05 value: Settings.settings.transitionDuration snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionDuration = value; + } background: Rectangle { x: durationSlider.leftPadding @@ -635,6 +699,7 @@ Rectangle { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -648,10 +713,10 @@ Rectangle { border.width: 2 } - onMoved: { - Settings.settings.transitionDuration = value; - } } + } + } + } diff --git a/Widgets/Sidebar/Config/WeatherSettings.qml b/Widgets/Sidebar/Config/WeatherSettings.qml index d7689dc..511e79a 100644 --- a/Widgets/Sidebar/Config/WeatherSettings.qml +++ b/Widgets/Sidebar/Config/WeatherSettings.qml @@ -14,7 +14,7 @@ Rectangle { anchors.margins: 18 spacing: 12 - // Weather Settings Header + RowLayout { Layout.fillWidth: true spacing: 12 @@ -36,7 +36,7 @@ Rectangle { } } - // Weather City Setting + ColumnLayout { spacing: 8 Layout.fillWidth: true @@ -93,7 +93,7 @@ Rectangle { } } - // Temperature Unit Setting + RowLayout { spacing: 12 Layout.fillWidth: true @@ -160,7 +160,7 @@ Rectangle { } - // Random Wallpaper Setting + RowLayout { spacing: 8 Layout.fillWidth: true @@ -216,7 +216,7 @@ Rectangle { } } - // Reverse Day Month Setting + RowLayout { spacing: 8 Layout.fillWidth: true diff --git a/Widgets/Sidebar/Panel/BluetoothPanel.qml b/Widgets/Sidebar/Panel/BluetoothPanel.qml index f87aa8f..3837d5c 100644 --- a/Widgets/Sidebar/Panel/BluetoothPanel.qml +++ b/Widgets/Sidebar/Panel/BluetoothPanel.qml @@ -12,7 +12,7 @@ Item { id: root property alias panel: bluetoothPanelModal - // For showing error/status messages + property string statusMessage: "" property bool statusPopupVisible: false @@ -90,7 +90,7 @@ Item { Rectangle { anchors.fill: parent color: Theme.backgroundPrimary - radius: 24 + radius: 20 ColumnLayout { anchors.fill: parent @@ -145,7 +145,7 @@ Item { opacity: 0.12 } - // Content area (centered, in a card) + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 640 diff --git a/Widgets/Sidebar/Panel/Music.qml b/Widgets/Sidebar/Panel/Music.qml index 553f3b3..6d24310 100644 --- a/Widgets/Sidebar/Panel/Music.qml +++ b/Widgets/Sidebar/Panel/Music.qml @@ -1,7 +1,7 @@ -import QtQuick +import QtQuick import QtQuick.Controls import QtQuick.Layouts -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import qs.Settings import qs.Components import qs.Services @@ -53,24 +53,108 @@ Rectangle { spacing: 12 visible: !!MusicManager.currentPlayer - // Album art and spectrum + // Player selector + ComboBox { + id: playerSelector + Layout.fillWidth: true + Layout.preferredHeight: 40 + visible: MusicManager.getAvailablePlayers().length > 1 + model: MusicManager.getAvailablePlayers() + textRole: "identity" + currentIndex: MusicManager.selectedPlayerIndex + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: playerSelector.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: playerSelector.indicator.width + playerSelector.spacing + text: playerSelector.displayText + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: playerSelector.width - width - 12 + y: playerSelector.topPadding + (playerSelector.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: playerSelector.height + width: playerSelector.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: playerSelector.popup.visible ? playerSelector.delegateModel : null + currentIndex: playerSelector.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: playerSelector.width + contentItem: Text { + text: modelData.identity + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: playerSelector.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + MusicManager.selectedPlayerIndex = index; + MusicManager.updateCurrentPlayer(); + } + } + + // Album art with spectrum visualizer RowLayout { spacing: 12 Layout.fillWidth: true - // Album art with spectrum + // Album art container with circular spectrum overlay Item { id: albumArtContainer - width: 96; height: 96 // enough for spectrum and art (will adjust if needed) + width: 96 + height: 96 // enough for spectrum and art (will adjust if needed) Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - // Spectrum visualizer + // Circular spectrum visualizer around album art CircularSpectrum { id: spectrum values: MusicManager.cavaValues anchors.centerIn: parent - innerRadius: 30 // just outside 60x60 album art - outerRadius: 48 // how far bars extend + innerRadius: 30 // Position just outside 60x60 album art + outerRadius: 48 // Extend bars outward from album art fillColor: Theme.accentPrimary strokeColor: Theme.accentPrimary strokeWidth: 0 @@ -80,7 +164,8 @@ Rectangle { // Album art image Rectangle { id: albumArtwork - width: 60; height: 60 + width: 60 + height: 60 anchors.centerIn: parent radius: 30 // circle color: Qt.darker(Theme.surface, 1.1) @@ -93,6 +178,7 @@ Rectangle { anchors.margins: 2 fillMode: Image.PreserveAspectCrop smooth: true + mipmap: true cache: false asynchronous: true sourceSize.width: 60 @@ -100,20 +186,29 @@ Rectangle { source: MusicManager.trackArtUrl visible: source.toString() !== "" - // Rounded corners using layer + // Apply circular mask for rounded corners layer.enabled: true - layer.effect: OpacityMask { - cached: true - maskSource: Rectangle { - width: albumArt.width - height: albumArt.height - radius: albumArt.width / 2 // circle - visible: false - } + layer.effect: MultiEffect { + maskEnabled: true + maskSource: mask } } - // Fallback icon + Item { + id: mask + + anchors.fill: albumArt + layer.enabled: true + visible: false + + Rectangle { + width: albumArt.width + height: albumArt.height + radius: albumArt.width / 2 // circle + } + } + + // Fallback icon when no album art available Text { anchors.centerIn: parent text: "album" @@ -171,8 +266,12 @@ Rectangle { color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.15) Layout.fillWidth: true - property real progressRatio: Math.min(1, MusicManager.trackLength > 0 ? - (MusicManager.currentPosition / MusicManager.trackLength) : 0) + property real progressRatio: { + if (!MusicManager.currentPlayer || !MusicManager.isPlaying || MusicManager.trackLength <= 0) { + return 0; + } + return Math.min(1, MusicManager.currentPosition / MusicManager.trackLength); + } Rectangle { id: progressFill @@ -182,7 +281,9 @@ Rectangle { color: Theme.accentPrimary Behavior on width { - NumberAnimation { duration: 200 } + NumberAnimation { + duration: 200 + } } } @@ -196,14 +297,16 @@ Rectangle { border.color: Qt.lighter(Theme.accentPrimary, 1.3) border.width: 1 - x: Math.max(0, Math.min(parent.width - width, progressFill.width - width/2)) + x: Math.max(0, Math.min(parent.width - width, progressFill.width - width / 2)) anchors.verticalCenter: parent.verticalCenter visible: MusicManager.trackLength > 0 scale: progressMouseArea.containsMouse || progressMouseArea.pressed ? 1.2 : 1.0 Behavior on scale { - NumberAnimation { duration: 150 } + NumberAnimation { + duration: 150 + } } } @@ -215,15 +318,15 @@ Rectangle { cursorShape: Qt.PointingHandCursor enabled: MusicManager.trackLength > 0 && MusicManager.canSeek - onClicked: function(mouse) { - let ratio = mouse.x / width - MusicManager.seekByRatio(ratio) + onClicked: function (mouse) { + let ratio = mouse.x / width; + MusicManager.seekByRatio(ratio); } - onPositionChanged: function(mouse) { + onPositionChanged: function (mouse) { if (pressed) { - let ratio = Math.max(0, Math.min(1, mouse.x / width)) - MusicManager.seekByRatio(ratio) + let ratio = Math.max(0, Math.min(1, mouse.x / width)); + MusicManager.seekByRatio(ratio); } } } @@ -318,4 +421,4 @@ Rectangle { } } } -} \ No newline at end of file +} diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index a656072..0ddc9c0 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -35,7 +35,7 @@ PanelWithOverlay { anchors.top: parent.top anchors.right: parent.right - // Animation properties + property real slideOffset: width property bool isAnimating: false @@ -59,15 +59,15 @@ PanelWithOverlay { if (sidebarPopupRect.settingsModal && sidebarPopupRect.settingsModal.visible) { sidebarPopupRect.settingsModal.visible = false; } - if (sidebarPopupRect.wallpaperPanelModal && sidebarPopupRect.wallpaperPanelModal.visible) { - sidebarPopupRect.wallpaperPanelModal.visible = false; + if (wallpaperPanel && wallpaperPanel.visible) { + wallpaperPanel.visible = false; + } + if (sidebarPopupRect.wifiPanelModal && sidebarPopupRect.wifiPanelModal.visible) { + sidebarPopupRect.wifiPanelModal.visible = false; + } + if (sidebarPopupRect.bluetoothPanelModal && sidebarPopupRect.bluetoothPanelModal.visible) { + sidebarPopupRect.bluetoothPanelModal.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; @@ -85,7 +85,7 @@ PanelWithOverlay { onStopped: { if (sidebarPopupRect.slideOffset === sidebarPopupRect.width) { sidebarPopup.visible = false; - // Stop monitoring and background tasks when hidden + if (weather) weather.stopWeatherFetch(); if (systemWidget) @@ -125,7 +125,6 @@ PanelWithOverlay { } property alias settingsModal: settingsModal - property alias wallpaperPanelModal: wallpaperPanelModal property alias wifiPanelModal: wifiPanel.panel property alias bluetoothPanelModal: bluetoothPanel.panel SettingsModal { @@ -314,7 +313,7 @@ PanelWithOverlay { settingsModal.visible = true; } onWallpaperRequested: { - wallpaperPanelModal.visible = true; + wallpaperPanel.visible = true; } } } @@ -339,7 +338,15 @@ PanelWithOverlay { videoPath += "/"; } var outputPath = videoPath + filename; - var command = "gpu-screen-recorder -w portal -f 60 -a default_output -o " + outputPath; + var command = "gpu-screen-recorder -w portal" + + " -f " + Settings.settings.recordingFrameRate + + " -a default_output" + + " -k " + Settings.settings.recordingCodec + + " -ac " + Settings.settings.audioCodec + + " -q " + Settings.settings.recordingQuality + + " -cursor " + (Settings.settings.showCursor ? "yes" : "no") + + " -cr " + Settings.settings.colorRange + + " -o " + outputPath; Quickshell.execDetached(["sh", "-c", command]); isRecording = true; quickAccessWidget.isRecording = true; @@ -403,15 +410,13 @@ PanelWithOverlay { } WallpaperPanel { - id: wallpaperPanelModal - visible: false + id: wallpaperPanel Component.onCompleted: { if (parent) { - wallpaperPanelModal.anchors.top = parent.top; - wallpaperPanelModal.anchors.right = parent.right; + anchors.top = parent.top; + 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 c68fa4f..bdd9c49 100644 --- a/Widgets/Sidebar/Panel/PowerProfile.qml +++ b/Widgets/Sidebar/Panel/PowerProfile.qml @@ -17,7 +17,7 @@ Rectangle { anchors.verticalCenter: parent.verticalCenter spacing: 20 - // Performance + Rectangle { width: 36; height: 36 radius: 18 @@ -63,7 +63,7 @@ Rectangle { } } - // Balanced + Rectangle { width: 36; height: 36 radius: 18 @@ -109,7 +109,7 @@ Rectangle { } } - // Power Saver + Rectangle { width: 36; height: 36 radius: 18 diff --git a/Widgets/Sidebar/Panel/QuickAccess.qml b/Widgets/Sidebar/Panel/QuickAccess.qml index 93975f1..ac33aec 100644 --- a/Widgets/Sidebar/Panel/QuickAccess.qml +++ b/Widgets/Sidebar/Panel/QuickAccess.qml @@ -1,7 +1,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Io import qs.Settings @@ -32,7 +32,7 @@ Rectangle { anchors.margins: 18 spacing: 12 - // Settings Button + Rectangle { id: settingsButton Layout.fillWidth: true @@ -75,7 +75,7 @@ Rectangle { } } - // Screen Recorder Button + Rectangle { id: recorderButton Layout.fillWidth: true @@ -123,7 +123,7 @@ Rectangle { } } - // Wallpaper Button + Rectangle { id: wallpaperButton Layout.fillWidth: true @@ -168,10 +168,10 @@ Rectangle { } } - // Properties + property bool panelVisible: false - // Timer to check if recording is active + Timer { interval: 2000 repeat: true @@ -185,7 +185,7 @@ Rectangle { } } - // Process to check if gpu-screen-recorder is running + Process { id: checkRecordingProcess command: ["pgrep", "-f", "gpu-screen-recorder.*portal"] diff --git a/Widgets/Sidebar/Panel/System.qml b/Widgets/Sidebar/Panel/System.qml index f2a6a21..1ffe456 100644 --- a/Widgets/Sidebar/Panel/System.qml +++ b/Widgets/Sidebar/Panel/System.qml @@ -1,9 +1,10 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls -import Qt5Compat.GraphicalEffects +import QtQuick.Effects import Quickshell import Quickshell.Io +import Quickshell.Widgets import qs.Settings import qs.Widgets import qs.Widgets.LockScreen @@ -29,19 +30,19 @@ Rectangle { anchors.margins: 18 spacing: 12 - // User info row + RowLayout { Layout.fillWidth: true spacing: 12 - // Profile image + Rectangle { width: 48 height: 48 radius: 24 color: Theme.accentPrimary - // Border + Rectangle { anchors.fill: parent color: "transparent" @@ -51,41 +52,10 @@ Rectangle { z: 2 } - OpacityMask { - anchors.fill: parent - source: Image { - id: avatarImage - anchors.fill: parent - source: Settings.settings.profileImage !== undefined ? Settings.settings.profileImage : "" - fillMode: Image.PreserveAspectCrop - asynchronous: true - cache: false - sourceSize.width: 44 - sourceSize.height: 44 - } - maskSource: Rectangle { - width: 44 - height: 44 - radius: 22 - visible: false - } - visible: Settings.settings.profileImage !== undefined && Settings.settings.profileImage !== "" - z: 1 - } - - // Fallback icon - Text { - anchors.centerIn: parent - text: "person" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.onAccent - visible: Settings.settings.profileImage === undefined || Settings.settings.profileImage === "" - z: 0 - } + Avatar {} } - // User info text + ColumnLayout { spacing: 4 Layout.fillWidth: true @@ -106,12 +76,12 @@ Rectangle { } } - // Spacer + Item { Layout.fillWidth: true } - // System menu button + Rectangle { id: systemButton width: 32 @@ -153,7 +123,7 @@ Rectangle { id: systemMenu anchors.top: systemButton.bottom anchors.right: systemButton.right - // System menu popup + Rectangle { width: 160 @@ -167,7 +137,7 @@ Rectangle { anchors.top: parent.top anchors.right: parent.right - // Position below system button + anchors.rightMargin: 32 anchors.topMargin: systemButton.y + systemButton.height + 48 @@ -176,7 +146,7 @@ Rectangle { anchors.margins: 8 spacing: 4 - // Lock button + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -216,7 +186,7 @@ Rectangle { } } - // Suspend button + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -255,7 +225,7 @@ Rectangle { } } - // Reboot button + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -295,7 +265,7 @@ Rectangle { } } - // Logout button + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -334,7 +304,7 @@ Rectangle { } } - // Shutdown button + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -376,10 +346,10 @@ Rectangle { } } - // Properties + property string uptimeText: "--:--" - // Process to get uptime + Process { id: uptimeProcess command: ["sh", "-c", "uptime | awk -F 'up ' '{print $2}' | awk -F ',' '{print $1}' | xargs"] @@ -410,7 +380,7 @@ Rectangle { running: false } - Process { + Process { id: logoutProcessNiri command: ["niri", "msg", "action", "quit", "--skip-confirmation"] running: false @@ -422,13 +392,19 @@ Rectangle { running: false } + Process { + id: logoutProcess + command: ["loginctl", "terminate-user", Quickshell.env("USER")] + running: false + } + function logout() { if (WorkspaceManager.isNiri) { logoutProcessNiri.running = true; } else if (WorkspaceManager.isHyprland) { logoutProcessHyprland.running = true; } else { - // fallback or error + console.warn("No supported compositor detected for logout"); } } @@ -445,19 +421,18 @@ Rectangle { rebootProcess.running = true; } - property bool panelVisible: false - // Trigger initial update when panel becomes visible + onPanelVisibleChanged: { if (panelVisible) { updateSystemInfo(); } } - // Timer to update uptime - only runs when panel is visible + Timer { - interval: 60000 // Update every minute + interval: 60000 repeat: true running: panelVisible onTriggered: updateSystemInfo() @@ -471,8 +446,8 @@ Rectangle { uptimeProcess.running = true; } - // Add lockscreen instance (hidden by default) + LockScreen { id: lockScreen } -} \ No newline at end of file +} diff --git a/Widgets/Sidebar/Panel/SystemMonitor.qml b/Widgets/Sidebar/Panel/SystemMonitor.qml index b9380e4..8b0e3ee 100644 --- a/Widgets/Sidebar/Panel/SystemMonitor.qml +++ b/Widgets/Sidebar/Panel/SystemMonitor.qml @@ -12,6 +12,7 @@ Rectangle { height: 250 color: "transparent" + // Track visibility state for panel integration property bool isVisible: false Rectangle { @@ -26,7 +27,8 @@ Rectangle { spacing: 12 Layout.alignment: Qt.AlignVCenter - // CPU Usage + + // CPU usage indicator with circular progress bar Item { width: 50; height: 50 CircularProgressBar { @@ -55,7 +57,8 @@ Rectangle { } } - // Cpu Temp + + // CPU temperature indicator with circular progress bar Item { width: 50; height: 50 CircularProgressBar { @@ -85,7 +88,8 @@ Rectangle { } } - // Memory Usage + + // Memory usage indicator with circular progress bar Item { width: 50; height: 50 CircularProgressBar { @@ -114,7 +118,8 @@ Rectangle { } } - // Disk Usage + + // Disk usage indicator with circular progress bar Item { width: 50; height: 50 CircularProgressBar { diff --git a/Widgets/Sidebar/Panel/WallpaperPanel.qml b/Widgets/Sidebar/Panel/WallpaperPanel.qml index aa444a5..be4c95f 100644 --- a/Widgets/Sidebar/Panel/WallpaperPanel.qml +++ b/Widgets/Sidebar/Panel/WallpaperPanel.qml @@ -30,7 +30,7 @@ PanelWindow { } onVisibleChanged: { - if (wallpaperPanelModal.visible) { + if (wallpaperPanel.visible) { wallpapers = WallpaperManager.wallpaperList } else { wallpapers = [] @@ -40,7 +40,7 @@ PanelWindow { Rectangle { anchors.fill: parent color: Theme.backgroundPrimary - radius: 24 + radius: 20 ColumnLayout { anchors.fill: parent anchors.margins: 32 @@ -81,7 +81,9 @@ PanelWindow { id: closeButtonArea anchors.fill: parent hoverEnabled: true - onClicked: wallpaperPanelModal.visible = false + onClicked: { + wallpaperPanel.visible = false; + } cursorShape: Qt.PointingHandCursor } } @@ -92,7 +94,7 @@ PanelWindow { color: Theme.outline opacity: 0.12 } - // Wallpaper grid area + Item { Layout.fillWidth: true Layout.fillHeight: true @@ -114,7 +116,7 @@ PanelWindow { cellWidth: Math.max(120, (scrollView.width / 3) - 12) cellHeight: cellWidth * 0.6 model: wallpapers - cacheBuffer: 32 + cacheBuffer: 64 leftMargin: 8 rightMargin: 8 topMargin: 8 @@ -129,7 +131,7 @@ PanelWindow { color: Qt.darker(Theme.backgroundPrimary, 1.1) radius: 12 border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline - border.width: Settings.settings.currentWallpaper === modelData ? 3 : 1 + border.width: 2 Image { id: wallpaperImage anchors.fill: parent @@ -137,8 +139,19 @@ PanelWindow { fillMode: Image.PreserveAspectCrop asynchronous: true cache: true - sourceSize.width: Math.min(width, 150) - sourceSize.height: Math.min(height, 90) + smooth: true + mipmap: true + + sourceSize.width: Math.min(width, 480) + sourceSize.height: Math.min(height, 270) + + opacity: (wallpaperImage.status == Image.Ready) ? 1.0 : 0.0 + Behavior on opacity { + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } + } } MouseArea { anchors.fill: parent diff --git a/Widgets/Sidebar/Panel/Weather.qml b/Widgets/Sidebar/Panel/Weather.qml index f9feb53..c6d0fd8 100644 --- a/Widgets/Sidebar/Panel/Weather.qml +++ b/Widgets/Sidebar/Panel/Weather.qml @@ -54,17 +54,17 @@ Rectangle { anchors.margins: 18 spacing: 12 - // Current weather row + RowLayout { spacing: 12 Layout.fillWidth: true - // Weather icon and basic info section + RowLayout { spacing: 12 Layout.preferredWidth: 140 - // Weather icon + Text { id: weatherIcon text: weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud" @@ -103,13 +103,13 @@ Rectangle { } } } - // Spacer to push content to the right + Item { Layout.fillWidth: true } } - // Separator line + Rectangle { width: parent.width height: 1 @@ -119,7 +119,7 @@ Rectangle { Layout.bottomMargin: 2 } - // 5-day forecast row + RowLayout { spacing: 12 Layout.fillWidth: true @@ -132,7 +132,7 @@ Rectangle { spacing: 2 Layout.alignment: Qt.AlignHCenter Text { - // Day of the week (e.g., Mon) + text: Qt.formatDateTime(new Date(weatherData.daily.time[index]), "ddd") font.family: Theme.fontFamily font.pixelSize: 12 @@ -141,7 +141,7 @@ Rectangle { Layout.alignment: Qt.AlignHCenter } Text { - // Material Symbol icon + text: materialSymbolForCode(weatherData.daily.weathercode[index]) font.family: "Material Symbols Outlined" font.pixelSize: 22 @@ -150,7 +150,7 @@ Rectangle { Layout.alignment: Qt.AlignHCenter } Text { - // High/low temp + 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 @@ -162,7 +162,7 @@ Rectangle { } } - // Error message + Text { text: errorString color: Theme.error @@ -175,16 +175,16 @@ Rectangle { } } - // Weather code to Material Symbol ligature mapping + function materialSymbolForCode(code) { - if (code === 0) return "sunny"; // Clear - if (code === 1 || code === 2) return "partly_cloudy_day"; // Mainly clear/partly cloudy - if (code === 3) return "cloud"; // Overcast - if (code >= 45 && code <= 48) return "foggy"; // Fog - if (code >= 51 && code <= 67) return "rainy"; // Drizzle - if (code >= 71 && code <= 77) return "weather_snowy"; // Snow - if (code >= 80 && code <= 82) return "rainy"; // Rain showers - if (code >= 95 && code <= 99) return "thunderstorm"; // Thunderstorm + if (code === 0) return "sunny"; + if (code === 1 || code === 2) return "partly_cloudy_day"; + if (code === 3) return "cloud"; + if (code >= 45 && code <= 48) return "foggy"; + if (code >= 51 && code <= 67) return "rainy"; + if (code >= 71 && code <= 77) return "weather_snowy"; + if (code >= 80 && code <= 82) return "rainy"; + if (code >= 95 && code <= 99) return "thunderstorm"; return "cloud"; } function weatherDescriptionForCode(code) { diff --git a/Widgets/Sidebar/Panel/WifiPanel.qml b/Widgets/Sidebar/Panel/WifiPanel.qml index 008a3c1..e70ff7f 100644 --- a/Widgets/Sidebar/Panel/WifiPanel.qml +++ b/Widgets/Sidebar/Panel/WifiPanel.qml @@ -11,72 +11,128 @@ import qs.Helpers Item { property alias panel: wifiPanelModal - + function showAt() { wifiPanelModal.visible = true; wifiLogic.refreshNetworks(); } + Component.onCompleted: { + existingNetwork.running = true; + } + function signalIcon(signal) { - if (signal >= 80) return "network_wifi"; - if (signal >= 60) return "network_wifi_3_bar"; - if (signal >= 40) return "network_wifi_2_bar"; - if (signal >= 20) return "network_wifi_1_bar"; + if (signal >= 80) + return "network_wifi"; + if (signal >= 60) + return "network_wifi_3_bar"; + if (signal >= 40) + return "network_wifi_2_bar"; + if (signal >= 20) + return "network_wifi_1_bar"; return "wifi_0_bar"; } + Process { + id: existingNetwork + running: false + command: ["nmcli", "-t", "-f", "NAME,TYPE", "connection", "show"] + stdout: StdioCollector { + onStreamFinished: { + const lines = text.split("\n"); + const networksMap = {}; + + refreshIndicator.running = true; + refreshIndicator.visible = true; + + for (let i = 0; i < lines.length; ++i) { + const line = lines[i].trim(); + if (!line) + continue; + + const parts = line.split(":"); + if (parts.length < 2) { + console.warn("Malformed nmcli output line:", line); + continue; + } + + const ssid = wifiLogic.replaceQuickshell(parts[0]); + const type = parts[1]; + + if (ssid) { + networksMap[ssid] = { + ssid: ssid, + type: type + }; + } + } + scanProcess.existingNetwork = networksMap; + scanProcess.running = true; + } + } + } + Process { id: scanProcess running: false command: ["nmcli", "-t", "-f", "SSID,SECURITY,SIGNAL,IN-USE", "device", "wifi", "list"] - onRunningChanged: { - // Removed debug log - } + + property var existingNetwork + stdout: StdioCollector { onStreamFinished: { - var lines = text.split("\n"); - var nets = []; - var seen = {}; - for (var i = 0; i < lines.length; ++i) { - var line = lines[i].trim(); - if (!line) continue; - var parts = line.split(":"); - var ssid = parts[0]; - var security = parts[1]; - var signal = parseInt(parts[2]); - var inUse = parts[3] === "*"; + const lines = text.split("\n"); + const networksMap = {}; + + for (let i = 0; i < lines.length; ++i) { + const line = lines[i].trim(); + if (!line) + continue; + + const parts = line.split(":"); + if (parts.length < 4) { + console.warn("Malformed nmcli output line:", line); + continue; + } + const ssid = parts[0]; + const security = parts[1]; + const signal = parseInt(parts[2]); + const inUse = parts[3] === "*"; + if (ssid) { - if (!seen[ssid]) { - // First time seeing this SSID - nets.push({ ssid: ssid, security: security, signal: signal, connected: inUse }); - seen[ssid] = true; + if (!networksMap[ssid]) { + networksMap[ssid] = { + ssid: ssid, + security: security, + signal: signal, + connected: inUse, + existing: ssid in scanProcess.existingNetwork + }; } else { - // SSID already exists, update if this entry has better signal or is connected - for (var j = 0; j < nets.length; ++j) { - if (nets[j].ssid === ssid) { - // Update connection status if this entry is connected - if (inUse) { - nets[j].connected = true; - } - // Update signal if this entry has better signal - if (signal > nets[j].signal) { - nets[j].signal = signal; - nets[j].security = security; - } - break; - } + const existingNet = networksMap[ssid]; + if (inUse) { + existingNet.connected = true; + } + if (signal > existingNet.signal) { + existingNet.signal = signal; + existingNet.security = security; } } } } - wifiLogic.networks = nets; + + + wifiLogic.networks = networksMap; + scanProcess.existingNetwork = {}; + refreshIndicator.running = false; + refreshIndicator.visible = false; } } } QtObject { id: wifiLogic - property var networks: [] + property var networks: {} property var anchorItem: null property real anchorX property real anchorY @@ -90,54 +146,98 @@ Item { property string connectSecurity: "" property var pendingConnect: null property string detectedInterface: "" + property string actionPanelSsid: "" - function profileNameForSsid(ssid) { - return "quickshell-" + ssid.replace(/[^a-zA-Z0-9]/g, "_"); + function replaceQuickshell(ssid: string): string { + const newName = ssid.replace("quickshell-", ""); + + if (!ssid.startsWith("quickshell-")) { + return newName; + } + + if (wifiLogic.networks && newName in wifiLogic.networks) { + console.log(`Quickshell ${newName} already exists, deleting old profile`) + deleteProfileProcess.connName = ssid; + deleteProfileProcess.running = true; + } + + console.log(`Changing from ${ssid} to ${newName}`) + renameConnectionProcess.oldName = ssid; + renameConnectionProcess.newName = newName; + renameConnectionProcess.running = true; + + return newName; } + function disconnectNetwork(ssid) { - var profileName = wifiLogic.profileNameForSsid(ssid); + const profileName = ssid; disconnectProfileProcess.connectionName = profileName; disconnectProfileProcess.running = true; } function refreshNetworks() { - scanProcess.running = true; + existingNetwork.running = true; } function showAt() { wifiPanelModal.visible = true; wifiLogic.refreshNetworks(); } function connectNetwork(ssid, security) { - wifiLogic.pendingConnect = {ssid: ssid, security: security, password: ""}; - listConnectionsProcess.running = true; + wifiLogic.pendingConnect = { + ssid: ssid, + security: security, + password: "" + }; + wifiLogic.doConnect(); } function submitPassword() { - wifiLogic.pendingConnect = {ssid: wifiLogic.passwordPromptSsid, security: wifiLogic.connectSecurity, password: wifiLogic.passwordInput}; - listConnectionsProcess.running = true; + wifiLogic.pendingConnect = { + ssid: wifiLogic.passwordPromptSsid, + security: wifiLogic.connectSecurity, + password: wifiLogic.passwordInput + }; + wifiLogic.doConnect(); } function doConnect() { - var params = wifiLogic.pendingConnect; + const params = wifiLogic.pendingConnect; + if (!params) + return; + wifiLogic.connectingSsid = params.ssid; + + + const targetNetwork = wifiLogic.networks[params.ssid]; + + + if (targetNetwork && targetNetwork.existing) { + + upConnectionProcess.profileName = params.ssid; + upConnectionProcess.running = true; + wifiLogic.pendingConnect = null; + return; + } + + if (params.security && params.security !== "--") { getInterfaceProcess.running = true; - } else { - connectProcess.security = params.security; - connectProcess.ssid = params.ssid; - connectProcess.password = params.password; - connectProcess.running = true; - wifiLogic.pendingConnect = null; + return; } + connectProcess.security = params.security; + connectProcess.ssid = params.ssid; + connectProcess.password = params.password; + connectProcess.running = true; + wifiLogic.pendingConnect = null; } function isSecured(security) { return security && security.trim() !== "" && security.trim() !== "--"; } } - // Disconnect, delete profile, refresh + Process { id: disconnectProfileProcess property string connectionName: "" running: false - command: ["nmcli", "connection", "down", "id", connectionName] + command: ["nmcli", "connection", "down", connectionName] onRunningChanged: { if (!running) { wifiLogic.refreshNetworks(); @@ -145,63 +245,70 @@ Item { } } + // Process to rename a connection Process { - id: listConnectionsProcess + id: renameConnectionProcess running: false - command: ["nmcli", "-t", "-f", "NAME", "connection", "show"] + property string oldName: "" + property string newName: "" + command: ["nmcli", "connection", "modify", oldName, "connection.id", newName] + stdout: StdioCollector { onStreamFinished: { - var params = wifiLogic.pendingConnect; - var lines = text.split("\n"); - var expectedProfile = wifiLogic.profileNameForSsid(params.ssid); - var foundProfile = null; - for (var i = 0; i < lines.length; ++i) { - if (lines[i] === expectedProfile) { - foundProfile = lines[i]; - break; - } - } - if (foundProfile) { - // Profile exists, just bring it up (no password prompt) - upConnectionProcess.profileName = foundProfile; - upConnectionProcess.running = true; - } else { - // No profile: check if secured - if (wifiLogic.isSecured(params.security)) { - if (params.password && params.password.length > 0) { - // Password provided, proceed to connect - wifiLogic.doConnect(); - } else { - // No password yet, prompt for it - wifiLogic.passwordPromptSsid = params.ssid; - wifiLogic.passwordInput = ""; - wifiLogic.showPasswordPrompt = true; - wifiLogic.connectStatus = ""; - wifiLogic.connectStatusSsid = ""; - wifiLogic.connectError = ""; - wifiLogic.connectSecurity = params.security; - } - } else { - // Open, connect directly - wifiLogic.doConnect(); - } + console.log("Successfully renamed connection '" + + renameConnectionProcess.oldName + "' to '" + + renameConnectionProcess.newName + "'"); + } + } + stderr: StdioCollector { + onStreamFinished: { + if (text.trim() !== "" && !text.toLowerCase().includes("warning")) { + console.error("Error renaming connection:", text); } } } } - // Handles connecting to a Wi-Fi network, with or without password + + + // Process to rename a connection + Process { + id: deleteProfileProcess + running: false + property string connName: "" + command: ["nmcli", "connection", "delete", `'${connName}'`] + + stdout: StdioCollector { + onStreamFinished: { + console.log("Deleted connection '" + deleteProfileProcess.connName + "'"); + } + } + stderr: StdioCollector { + onStreamFinished: { + console.error("Error deleting connection '" + deleteProfileProcess.connName + "':", text); + } + } + } + + + Process { id: connectProcess property string ssid: "" property string password: "" property string security: "" running: false + onStarted: { + refreshIndicator.running = true; + } + onExited: (exitCode, exitStatus) => { + refreshIndicator.running = false; + } command: { if (password) { - return ["nmcli", "device", "wifi", "connect", ssid, "password", password] + return ["nmcli", "device", "wifi", "connect", `'${ssid}'`, "password", password]; } else { - return ["nmcli", "device", "wifi", "connect", ssid] + return ["nmcli", "device", "wifi", "connect", `'${ssid}'`]; } } stdout: StdioCollector { @@ -229,7 +336,7 @@ Item { } } - // Finds the correct Wi-Fi interface for connection + Process { id: getInterfaceProcess running: false @@ -249,7 +356,7 @@ Item { addConnectionProcess.ifname = wifiLogic.detectedInterface; addConnectionProcess.ssid = params.ssid; addConnectionProcess.password = params.password; - addConnectionProcess.profileName = wifiLogic.profileNameForSsid(params.ssid); + addConnectionProcess.profileName = params.ssid; addConnectionProcess.security = params.security; addConnectionProcess.running = true; } else { @@ -263,7 +370,7 @@ Item { } } - // Adds a new Wi-Fi connection profile + Process { id: addConnectionProcess property string ifname: "" @@ -296,7 +403,7 @@ Item { } } - // Brings up the new connection profile and finalizes connection state + Process { id: upConnectionProcess property string profileName: "" @@ -329,10 +436,11 @@ Item { } } - // Wifi button (no background card) + Rectangle { id: wifiButton - width: 36; height: 36 + width: 36 + height: 36 radius: 18 border.color: Theme.accentPrimary border.width: 1 @@ -343,9 +451,7 @@ Item { text: "wifi" font.family: "Material Symbols Outlined" font.pixelSize: 22 - color: wifiButtonArea.containsMouse - ? Theme.backgroundPrimary - : Theme.accentPrimary + color: wifiButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } @@ -371,12 +477,12 @@ Item { margins.top: 0 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand Component.onCompleted: { - wifiLogic.refreshNetworks() + wifiLogic.refreshNetworks(); } Rectangle { anchors.fill: parent color: Theme.backgroundPrimary - radius: 24 + radius: 20 ColumnLayout { anchors.fill: parent anchors.margins: 32 @@ -400,8 +506,29 @@ Item { color: Theme.textPrimary Layout.fillWidth: true } + Item { + Layout.fillWidth: true + } + Spinner { + id: refreshIndicator + Layout.preferredWidth: 24 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignVCenter + visible: false + running: false + color: Theme.accentPrimary + size: 22 + } + IconButton { + id: refreshButton + icon: "refresh" + onClicked: wifiLogic.refreshNetworks() + } + Rectangle { - width: 36; height: 36; radius: 18 + implicitWidth: 36 + implicitHeight: 36 + radius: 18 color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary border.width: 1 @@ -463,11 +590,15 @@ Item { anchors.fill: parent spacing: 4 boundsBehavior: Flickable.StopAtBounds - model: wifiLogic.networks + model: wifiLogic.networks ? Object.values(wifiLogic.networks) : null delegate: Item { id: networkEntry + + required property var modelData + property var signalIcon: wifiPanel.signalIcon + width: parent.width - height: modelData.ssid === wifiLogic.passwordPromptSsid && wifiLogic.showPasswordPrompt ? 102 : 42 + height: (modelData.ssid === wifiLogic.passwordPromptSsid && wifiLogic.showPasswordPrompt ? 102 : 42) + (modelData.ssid === wifiLogic.actionPanelSsid ? 60 : 0) ColumnLayout { anchors.fill: parent spacing: 0 @@ -504,7 +635,8 @@ Item { Layout.alignment: Qt.AlignVCenter } Item { - width: 22; height: 22 + width: 22 + height: 22 visible: wifiLogic.connectStatusSsid === modelData.ssid && wifiLogic.connectStatus !== "" RowLayout { anchors.fill: parent @@ -554,28 +686,29 @@ Item { verticalAlignment: Text.AlignVCenter Layout.alignment: Qt.AlignVCenter } - Item { - Layout.alignment: Qt.AlignVCenter - Layout.preferredHeight: 22 - Layout.preferredWidth: 22 - Spinner { - visible: wifiLogic.connectingSsid === modelData.ssid - running: wifiLogic.connectingSsid === modelData.ssid - color: Theme.accentPrimary - anchors.centerIn: parent - size: 22 - } - } + Item { + Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: 22 + Layout.preferredWidth: 22 + Spinner { + visible: wifiLogic.connectingSsid === modelData.ssid + running: wifiLogic.connectingSsid === modelData.ssid + color: Theme.accentPrimary + anchors.centerIn: parent + size: 22 + } + } } MouseArea { id: networkMouseArea anchors.fill: parent hoverEnabled: true onClicked: { - if (modelData.connected) { - wifiLogic.disconnectNetwork(modelData.ssid); + + if (wifiLogic.actionPanelSsid === modelData.ssid) { + wifiLogic.actionPanelSsid = ""; // Close if already open } else { - wifiLogic.connectNetwork(modelData.ssid, modelData.security); + wifiLogic.actionPanelSsid = modelData.ssid; // Open for this network } } } @@ -586,8 +719,9 @@ Item { Layout.preferredHeight: 60 radius: 8 color: "transparent" - anchors.leftMargin: 32 - anchors.rightMargin: 32 + Layout.alignment: Qt.AlignLeft + Layout.leftMargin: 32 + Layout.rightMargin: 32 z: 2 RowLayout { anchors.fill: parent @@ -627,14 +761,18 @@ Item { } } Rectangle { - width: 80 - height: 36 + Layout.preferredWidth: 80 + Layout.preferredHeight: 36 radius: 18 color: Theme.accentPrimary border.color: Theme.accentPrimary border.width: 0 opacity: 1.0 - Behavior on color { ColorAnimation { duration: 100 } } + Behavior on color { + ColorAnimation { + duration: 100 + } + } MouseArea { anchors.fill: parent onClicked: wifiLogic.submitPassword() @@ -653,6 +791,113 @@ Item { } } } + + Rectangle { + visible: modelData.ssid === wifiLogic.actionPanelSsid + Layout.fillWidth: true + Layout.preferredHeight: 60 + radius: 8 + color: "transparent" + Layout.alignment: Qt.AlignLeft + Layout.leftMargin: 32 + Layout.rightMargin: 32 + z: 2 + RowLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 10 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 36 + visible: wifiLogic.isSecured(modelData.security) && !modelData.connected && !modelData.existing + Rectangle { + anchors.fill: parent + radius: 8 + color: "transparent" + border.color: actionPanelPasswordField.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + TextInput { + id: actionPanelPasswordField + anchors.fill: parent + anchors.margins: 12 + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhNone + echoMode: TextInput.Password + onAccepted: { + + wifiLogic.pendingConnect = { + ssid: modelData.ssid, + security: modelData.security, + password: text + }; + wifiLogic.doConnect(); + + wifiLogic.actionPanelSsid = ""; // Close the panel + } + } + } + } + + Rectangle { + Layout.preferredWidth: 80 + Layout.preferredHeight: 36 + radius: 18 + color: modelData.connected ? Theme.error : Theme.accentPrimary + border.color: modelData.connected ? Theme.error : Theme.accentPrimary + border.width: 0 + opacity: 1.0 + Behavior on color { + ColorAnimation { + duration: 100 + } + } + MouseArea { + anchors.fill: parent + onClicked: { + if (modelData.connected) { + + wifiLogic.disconnectNetwork(modelData.ssid); + } else { + + if (wifiLogic.isSecured(modelData.security) && !modelData.existing) { + + if (actionPanelPasswordField.text.length > 0) { + wifiLogic.pendingConnect = { + ssid: modelData.ssid, + security: modelData.security, + password: actionPanelPasswordField.text + }; + wifiLogic.doConnect(); + } + + } else { + + wifiLogic.connectNetwork(modelData.ssid, modelData.security); + } + } + wifiLogic.actionPanelSsid = ""; // Close the panel + } + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: parent.color = modelData.connected ? Qt.darker(Theme.error, 1.1) : Qt.darker(Theme.accentPrimary, 1.1) + onExited: parent.color = modelData.connected ? Theme.error : Theme.accentPrimary + } + Text { + anchors.centerIn: parent + text: modelData.connected ? "wifi_off" : "check" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.backgroundPrimary + } + } + } + } } } } diff --git a/qmlls.ini b/qmlls.ini new file mode 100644 index 0000000..e69de29 diff --git a/shell.qml b/shell.qml index 737badc..5bd4e33 100644 --- a/shell.qml +++ b/shell.qml @@ -22,18 +22,17 @@ Scope { property var notificationHistoryWin: notificationHistoryWin property bool pendingReload: false - // Helper function to round value to nearest step + // Round volume to nearest 5% increment for consistent control function roundToStep(value, step) { return Math.round(value / step) * step; } - // Volume property reflecting current audio volume in 0-100 - // Will be kept in sync dynamically below + // Current audio volume (0-100), synced with system property int volume: (defaultAudioSink && defaultAudioSink.audio && !defaultAudioSink.audio.muted) ? Math.round(defaultAudioSink.audio.volume * 100) : 0 - // Function to update volume with clamping, stepping, and applying to audio sink + // Update volume with 5-step increments and apply to audio sink function updateVolume(vol) { var clamped = Math.max(0, Math.min(100, vol)); var stepped = roundToStep(clamped, 5); @@ -53,6 +52,15 @@ Scope { property var notificationHistoryWin: notificationHistoryWin } + // Create dock for each monitor (respects dockMonitors setting) + Variants { + model: Quickshell.screens + + Dock { + property var modelData + } + } + Applauncher { id: appLauncherPanel visible: false @@ -77,9 +85,15 @@ Scope { onNotification: function (notification) { console.log("Notification received:", notification.appName); notification.tracked = true; - if (notificationPopup.notificationsVisible) { - notificationPopup.addNotification(notification); + + // Distribute notification to all visible notification popups + for (let i = 0; i < notificationPopupVariants.count; i++) { + let popup = notificationPopupVariants.objectAt(i); + if (popup && popup.notificationsVisible) { + popup.addNotification(notification); + } } + if (notificationHistoryWin) { notificationHistoryWin.addToHistory({ id: notification.id, @@ -93,9 +107,19 @@ Scope { } } - NotificationPopup { - id: notificationPopup - barVisible: bar.visible + // Create notification popups for each selected monitor + Variants { + id: notificationPopupVariants + model: Quickshell.screens + + NotificationPopup { + property var modelData + barVisible: bar.visible + screen: modelData + visible: notificationsVisible && notificationModel.count > 0 && + (Settings.settings.notificationMonitors.includes(modelData.name) || + (Settings.settings.notificationMonitors.length === 0)) // Show on all if none selected + } } NotificationHistory { @@ -113,7 +137,7 @@ Scope { appLauncherPanel: appLauncherPanel lockScreen: lockScreen idleInhibitor: idleInhibitor - notificationPopup: notificationPopup + notificationPopupVariants: notificationPopupVariants } Connections { @@ -130,11 +154,12 @@ Scope { Timer { id: reloadTimer - interval: 500 // ms + interval: 500 repeat: false onTriggered: Quickshell.reload(true) } + // Handle screen configuration changes (delay reload if locked) Connections { target: Quickshell function onScreensChanged() { @@ -146,17 +171,15 @@ Scope { } } - // --- NEW: Keep volume property in sync with actual Pipewire audio sink volume --- - Connections { - target: defaultAudioSink.audio - onVolumeChanged: { + target: defaultAudioSink ? defaultAudioSink.audio : null + function onVolumeChanged() { if (defaultAudioSink.audio && !defaultAudioSink.audio.muted) { volume = Math.round(defaultAudioSink.audio.volume * 100); console.log("Volume changed externally to:", volume); } } - onMutedChanged: { + function onMutedChanged() { if (defaultAudioSink.audio) { if (defaultAudioSink.audio.muted) { volume = 0; From 69d84752f3dd8d27a6fb3feaf670f8f5839f40ee Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Tue, 5 Aug 2025 20:15:11 +0200 Subject: [PATCH 02/43] Fix notification and other small fixes --- Bar/Modules/Applauncher.qml | 6 +- Helpers/IPCHandlers.qml | 19 +- Settings/Settings.qml | 4 +- Widgets/Notification/NotificationPopup.qml | 593 +++++++++++--------- Widgets/SettingsWindow/Tabs/About.qml | 2 +- Widgets/SettingsWindow/Tabs/Display.qml | 17 +- Widgets/SettingsWindow/Tabs/General.qml | 6 +- Widgets/SettingsWindow/Tabs/Network.qml | 2 +- Widgets/SettingsWindow/Tabs/TimeWeather.qml | 2 +- shell.qml | 53 +- 10 files changed, 366 insertions(+), 338 deletions(-) diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index a02ee37..f98509c 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -296,9 +296,9 @@ PanelWithOverlay { const searchTerm = query.slice(5).trim(); clipboardHistory.forEach(function(clip, index) { - let searchContent = clip.type === 'image' ? - clip.mimeType : - clip.content || clip; // Support both new object format and old string format + let searchContent = clip.type === 'image' ? + clip.mimeType : + clip.content || clip; // Support both new object format and old string format if (!searchTerm || searchContent.toLowerCase().includes(searchTerm)) { let entry; diff --git a/Helpers/IPCHandlers.qml b/Helpers/IPCHandlers.qml index 3da1fca..aa8ef69 100644 --- a/Helpers/IPCHandlers.qml +++ b/Helpers/IPCHandlers.qml @@ -6,7 +6,7 @@ IpcHandler { property var appLauncherPanel property var lockScreen property IdleInhibitor idleInhibitor - property var notificationPopupVariants + property var notificationPopup target: "globalIPC" @@ -17,18 +17,11 @@ IpcHandler { function toggleNotificationPopup(): void { console.log("[IPC] NotificationPopup toggle() called") - - if (notificationPopupVariants) { - for (let i = 0; i < notificationPopupVariants.count; i++) { - let popup = notificationPopupVariants.objectAt(i); - if (popup) { - popup.togglePopup(); - } - } - } + // Use the global toggle function from the notification manager + notificationPopup.togglePopup(); } - + // Toggle Applauncher visibility function toggleLauncher(): void { if (!appLauncherPanel) { console.warn("AppLauncherIpcHandler: appLauncherPanel not set!"); @@ -42,7 +35,7 @@ IpcHandler { } } - + // Toggle LockScreen function toggleLock(): void { if (!lockScreen) { console.warn("LockScreenIpcHandler: lockScreen not set!"); @@ -51,4 +44,4 @@ IpcHandler { console.log("[IPC] LockScreen show() called"); lockScreen.locked = true; } -} +} \ No newline at end of file diff --git a/Settings/Settings.qml b/Settings/Settings.qml index 23bb5f2..0a09b94 100644 --- a/Settings/Settings.qml +++ b/Settings/Settings.qml @@ -81,7 +81,7 @@ Singleton { // Monitor/Display Settings property var barMonitors: [] // Array of monitor names to show the bar on property var dockMonitors: [] // Array of monitor names to show the dock on - property var notificationMonitors: [] // Array of monitor names to show notifications on + property var notificationMonitors: [] // Array of monitor names to show notifications on, "*" means all monitors } } @@ -90,5 +90,7 @@ Singleton { function onRandomWallpaperChanged() { WallpaperManager.toggleRandomWallpaper() } function onWallpaperIntervalChanged() { WallpaperManager.restartRandomWallpaperTimer() } function onWallpaperFolderChanged() { WallpaperManager.loadWallpapers() } + function onNotificationMonitorsChanged() { + } } } \ No newline at end of file diff --git a/Widgets/Notification/NotificationPopup.qml b/Widgets/Notification/NotificationPopup.qml index 5b4fd5d..afaddd7 100644 --- a/Widgets/Notification/NotificationPopup.qml +++ b/Widgets/Notification/NotificationPopup.qml @@ -4,313 +4,356 @@ import Quickshell import Quickshell.Widgets import qs.Settings -PanelWindow { - id: window - implicitWidth: 350 - implicitHeight: notificationColumn.implicitHeight - color: "transparent" - visible: notificationsVisible && notificationModel.count > 0 - screen: (typeof modelData !== 'undefined' ? modelData : Quickshell.primaryScreen) - focusable: false +// Main container that manages multiple notification popups for different monitors +Item { + id: notificationManager + anchors.fill: parent - property bool barVisible: true + // Get list of available monitors/screens + property var monitors: Quickshell.screens || [] + + // Global visibility state for all notification popups property bool notificationsVisible: true - - anchors.top: true - anchors.right: true - margins.top: 6 - margins.right: 6 - - ListModel { - id: notificationModel - } - - property int maxVisible: 5 - property int spacing: 5 - + function togglePopup(): void { - console.log("[NotificationPopup] Current state: " + notificationsVisible); + console.log("[NotificationManager] Current state: " + notificationsVisible); notificationsVisible = !notificationsVisible; - console.log("[NotificationPopup] New state: " + notificationsVisible); + console.log("[NotificationManager] New state: " + notificationsVisible); } - function addNotification(notification) { - notificationModel.insert(0, { - id: notification.id, - appName: notification.appName || "Notification", - summary: notification.summary || "", - body: notification.body || "", - urgency: notification.urgency || 0, - rawNotification: notification, - appeared: false, - dismissed: false - }); - - while (notificationModel.count > maxVisible) { - notificationModel.remove(notificationModel.count - 1); - } - } - - function dismissNotificationById(id) { - for (var i = 0; i < notificationModel.count; i++) { - if (notificationModel.get(i).id === id) { - dismissNotificationByIndex(i); - break; + // Create a notification popup for each monitor + Repeater { + model: notificationManager.monitors + delegate: Item { + id: delegateItem + + // Make addNotification accessible from the Item level + function addNotification(notification) { + if (panelWindow) { + panelWindow.addNotification(notification); + } } - } - } + + PanelWindow { + id: panelWindow + implicitWidth: 350 + implicitHeight: Math.max(notificationColumn.height, 0) + color: "transparent" + visible: notificationManager.notificationsVisible && notificationModel.count > 0 && shouldShowOnThisMonitor + screen: modelData + focusable: false - function dismissNotificationByIndex(index) { - if (index >= 0 && index < notificationModel.count) { - var notif = notificationModel.get(index); - if (!notif.dismissed) { - notificationModel.set(index, { - id: notif.id, - appName: notif.appName, - summary: notif.summary, - body: notif.body, - rawNotification: notif.rawNotification, - appeared: notif.appeared, - dismissed: true - }); - } - } - } + property bool barVisible: true + property bool notificationsVisible: notificationManager.notificationsVisible + + // Check if this monitor should show notifications - make it reactive to settings changes + property bool shouldShowOnThisMonitor: { + let notificationMonitors = Settings.settings.notificationMonitors || []; + let currentScreenName = modelData ? modelData.name : ""; + return notificationMonitors.includes("*") || + notificationMonitors.includes(currentScreenName); + } - Column { - id: notificationColumn - anchors.right: parent.right - spacing: window.spacing - width: parent.width - clip: false + // Watch for changes in notification monitors setting + Connections { + target: Settings.settings + function onNotificationMonitorsChanged() { + // Settings changed, visibility will update automatically + } + } - Repeater { - id: notificationRepeater - model: notificationModel + anchors.top: true + anchors.right: true + margins.top: 6 + margins.right: 6 - delegate: Rectangle { - id: notificationDelegate - width: parent.width - color: Theme.backgroundPrimary - radius: 20 - border.color: model.urgency == 2 ? Theme.warning : Theme.outline - border.width: 1 + ListModel { + id: notificationModel + } - property bool appeared: model.appeared - property bool dismissed: model.dismissed - property var rawNotification: model.rawNotification + property int maxVisible: 5 + property int spacing: 5 - x: appeared ? 0 : width - opacity: dismissed ? 0 : 1 - height: dismissed ? 0 : contentRow.height + 20 + function addNotification(notification) { + notificationModel.insert(0, { + id: notification.id, + appName: notification.appName || "Notification", + summary: notification.summary || "", + body: notification.body || "", + urgency: notification.urgency || 0, + rawNotification: notification, + appeared: false, + dismissed: false + }); - Row { - id: contentRow - anchors.centerIn: parent - spacing: 10 - width: parent.width - 20 + while (notificationModel.count > maxVisible) { + notificationModel.remove(notificationModel.count - 1); + } + } - // Circular Icon container with border - Rectangle { - id: iconBackground - width: 36 - height: 36 - radius: width / 2 - color: Theme.accentPrimary - anchors.verticalCenter: parent.verticalCenter - border.color: Qt.darker(Theme.accentPrimary, 1.2) - border.width: 1.5 - - // Priority order for notification icons: image > appIcon > icon - property var iconSources: [rawNotification?.image || "", rawNotification?.appIcon || "", rawNotification?.icon || ""] - - // Load notification icon with fallback handling - IconImage { - id: iconImage - anchors.fill: parent - anchors.margins: 4 - asynchronous: true - backer.fillMode: Image.PreserveAspectFit - source: { - // Try each icon source in priority order - for (var i = 0; i < iconBackground.iconSources.length; i++) { - var icon = iconBackground.iconSources[i]; - if (!icon) - continue; - - // Handle special path format from some notifications - if (icon.includes("?path=")) { - const [name, path] = icon.split("?path="); - const fileName = name.substring(name.lastIndexOf("/") + 1); - return `file://${path}/${fileName}`; - } - - // Handle absolute file paths - if (icon.startsWith('/')) { - return "file://" + icon; - } - - return icon; - } - return ""; - } - visible: status === Image.Ready && source.toString() !== "" + function dismissNotificationById(id) { + for (var i = 0; i < notificationModel.count; i++) { + if (notificationModel.get(i).id === id) { + dismissNotificationByIndex(i); + break; } + } + } - // Fallback: show first letter of app name when no icon available - Text { - anchors.centerIn: parent - visible: !iconImage.visible - text: model.appName ? model.appName.charAt(0).toUpperCase() : "?" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody - font.bold: true + function dismissNotificationByIndex(index) { + if (index >= 0 && index < notificationModel.count) { + var notif = notificationModel.get(index); + if (!notif.dismissed) { + notificationModel.set(index, { + id: notif.id, + appName: notif.appName, + summary: notif.summary, + body: notif.body, + rawNotification: notif.rawNotification, + appeared: notif.appeared, + dismissed: true + }); + } + } + } + + Column { + id: notificationColumn + anchors.right: parent.right + spacing: window.spacing + width: parent.width + clip: false + + Repeater { + id: notificationRepeater + model: notificationModel + + delegate: Rectangle { + id: notificationDelegate + width: parent.width color: Theme.backgroundPrimary - } - } + radius: 20 + border.color: model.urgency == 2 ? Theme.warning : Theme.outline + border.width: 1 - Column { - width: contentRow.width - iconBackground.width - 10 - spacing: 5 + property bool appeared: model.appeared + property bool dismissed: model.dismissed + property var rawNotification: model.rawNotification - Text { - text: model.appName - width: parent.width - color: Theme.textPrimary - font.family: Theme.fontFamily - font.bold: true - font.pixelSize: Theme.fontSizeSmall - elide: Text.ElideRight - } - Text { - text: model.summary - width: parent.width - color: "#eeeeee" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall - wrapMode: Text.Wrap - visible: text !== "" - } - Text { - text: model.body - width: parent.width - color: "#cccccc" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption - wrapMode: Text.Wrap - visible: text !== "" - } - } - } + x: appeared ? 0 : width + opacity: dismissed ? 0 : 1 + height: dismissed ? 0 : contentRow.height + 20 - Timer { - interval: 4000 - running: !dismissed - repeat: false - onTriggered: { - dismissAnimation.start(); - if (rawNotification) - rawNotification.expire(); - } - } + Row { + id: contentRow + anchors.centerIn: parent + spacing: 10 + width: parent.width - 20 - MouseArea { - anchors.fill: parent - onClicked: { - dismissAnimation.start(); - if (rawNotification) - rawNotification.dismiss(); - } - } + // Circular Icon container with border + Rectangle { + id: iconBackground + width: 36 + height: 36 + radius: width / 2 + color: Theme.accentPrimary + anchors.verticalCenter: parent.verticalCenter + border.color: Qt.darker(Theme.accentPrimary, 1.2) + border.width: 1.5 - 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 - } - onFinished: { - for (let i = 0; i < notificationModel.count; i++) { - if (notificationModel.get(i).id === notificationDelegate.id) { - notificationModel.remove(i); - break; + // Priority order for notification icons: image > appIcon > icon + property var iconSources: [rawNotification?.image || "", rawNotification?.appIcon || "", rawNotification?.icon || ""] + + // Load notification icon with fallback handling + IconImage { + id: iconImage + anchors.fill: parent + anchors.margins: 4 + asynchronous: true + backer.fillMode: Image.PreserveAspectFit + source: { + // Try each icon source in priority order + for (var i = 0; i < iconBackground.iconSources.length; i++) { + var icon = iconBackground.iconSources[i]; + if (!icon) + continue; + + // Handle special path format from some notifications + if (icon.includes("?path=")) { + const [name, path] = icon.split("?path="); + const fileName = name.substring(name.lastIndexOf("/") + 1); + return `file://${path}/${fileName}`; + } + + // Handle absolute file paths + if (icon.startsWith('/')) { + return "file://" + icon; + } + + return icon; + } + return ""; + } + visible: status === Image.Ready && source.toString() !== "" + } + + // Fallback: show first letter of app name when no icon available + Text { + anchors.centerIn: parent + visible: !iconImage.visible + text: model.appName ? model.appName.charAt(0).toUpperCase() : "?" + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeBody + font.bold: true + color: Theme.backgroundPrimary + } + } + + Column { + width: contentRow.width - iconBackground.width - 10 + spacing: 5 + + Text { + text: model.appName + width: parent.width + color: Theme.textPrimary + font.family: Theme.fontFamily + font.bold: true + font.pixelSize: Theme.fontSizeSmall + elide: Text.ElideRight + } + Text { + text: model.summary + width: parent.width + color: "#eeeeee" + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeSmall + wrapMode: Text.Wrap + visible: text !== "" + } + Text { + text: model.body + width: parent.width + color: "#cccccc" + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeCaption + wrapMode: Text.Wrap + visible: text !== "" + } + } + } + + Timer { + interval: 4000 + running: !dismissed + repeat: false + onTriggered: { + dismissAnimation.start(); + if (rawNotification) + rawNotification.expire(); + } + } + + MouseArea { + anchors.fill: parent + onClicked: { + dismissAnimation.start(); + 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 + } + onFinished: { + 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 + } + } + + Component.onCompleted: { + if (!appeared) { + opacity = 0; + height = 0; + x = width; + appearAnimation.start(); + 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; + } + } + } } } } } - 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 - } - } - - Component.onCompleted: { - if (!appeared) { - opacity = 0; - height = 0; - x = width; - appearAnimation.start(); - 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; - } + Connections { + target: Quickshell + function onScreensChanged() { + if (panelWindow.screen) { + x = panelWindow.screen.width - panelWindow.width - 20; } } } } } } - - Connections { - target: Quickshell - function onScreensChanged() { - if (window.screen) { - x = window.screen.width - width - 20; - } - } - } } diff --git a/Widgets/SettingsWindow/Tabs/About.qml b/Widgets/SettingsWindow/Tabs/About.qml index 3ee1942..14d3bc0 100644 --- a/Widgets/SettingsWindow/Tabs/About.qml +++ b/Widgets/SettingsWindow/Tabs/About.qml @@ -255,7 +255,7 @@ Item { font.pixelSize: 14 color: Theme.textSecondary Layout.alignment: Qt.AlignCenter - Layout.topMargin: 16 + Layout.topMargin: 24 } diff --git a/Widgets/SettingsWindow/Tabs/Display.qml b/Widgets/SettingsWindow/Tabs/Display.qml index ee4a159..bf450a3 100644 --- a/Widgets/SettingsWindow/Tabs/Display.qml +++ b/Widgets/SettingsWindow/Tabs/Display.qml @@ -13,6 +13,17 @@ ColumnLayout { // Get list of available monitors/screens property var monitors: Quickshell.screens || [] + + // Sorted monitors by name + property var sortedMonitors: { + let sorted = [...monitors]; + sorted.sort((a, b) => { + let nameA = a.name || "Unknown"; + let nameB = b.name || "Unknown"; + return nameA.localeCompare(nameB); + }); + return sorted; + } Item { Layout.fillWidth: true @@ -68,7 +79,7 @@ ColumnLayout { spacing: 8 Repeater { - model: root.monitors + model: root.sortedMonitors delegate: Rectangle { id: barCheckbox property bool isChecked: false @@ -171,7 +182,7 @@ ColumnLayout { spacing: 8 Repeater { - model: root.monitors + model: root.sortedMonitors delegate: Rectangle { id: dockCheckbox property bool isChecked: false @@ -277,7 +288,7 @@ ColumnLayout { spacing: 8 Repeater { - model: root.monitors + model: root.sortedMonitors delegate: Rectangle { id: notificationCheckbox property bool isChecked: false diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index 4d0ffda..0f3d5b0 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -107,15 +107,11 @@ ColumnLayout { } } - Item { - Layout.fillWidth: true - Layout.preferredHeight: 16 - } - ColumnLayout { spacing: 4 Layout.fillWidth: true + Layout.topMargin: 58 Text { text: "User Interface" diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index 3f79e32..0223f56 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -101,7 +101,7 @@ ColumnLayout { ColumnLayout { spacing: 16 Layout.fillWidth: true - Layout.topMargin: 16 + Layout.topMargin: 58 Text { text: "Bluetooth" diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml index 7ee1c75..72aa261 100644 --- a/Widgets/SettingsWindow/Tabs/TimeWeather.qml +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -172,7 +172,7 @@ ColumnLayout { ColumnLayout { spacing: 4 Layout.fillWidth: true - Layout.topMargin: 16 + Layout.topMargin: 58 Text { text: "Weather" diff --git a/shell.qml b/shell.qml index 5bd4e33..a06df9c 100644 --- a/shell.qml +++ b/shell.qml @@ -22,17 +22,18 @@ Scope { property var notificationHistoryWin: notificationHistoryWin property bool pendingReload: false - // Round volume to nearest 5% increment for consistent control + // Helper function to round value to nearest step function roundToStep(value, step) { return Math.round(value / step) * step; } - // Current audio volume (0-100), synced with system + // Volume property reflecting current audio volume in 0-100 + // Will be kept in sync dynamically below property int volume: (defaultAudioSink && defaultAudioSink.audio && !defaultAudioSink.audio.muted) ? Math.round(defaultAudioSink.audio.volume * 100) : 0 - // Update volume with 5-step increments and apply to audio sink + // Function to update volume with clamping, stepping, and applying to audio sink function updateVolume(vol) { var clamped = Math.max(0, Math.min(100, vol)); var stepped = roundToStep(clamped, 5); @@ -52,13 +53,8 @@ Scope { property var notificationHistoryWin: notificationHistoryWin } - // Create dock for each monitor (respects dockMonitors setting) - Variants { - model: Quickshell.screens - - Dock { - property var modelData - } + Dock { + id: dock } Applauncher { @@ -83,17 +79,16 @@ Scope { NotificationServer { id: notificationServer onNotification: function (notification) { - console.log("Notification received:", notification.appName); notification.tracked = true; - - // Distribute notification to all visible notification popups - for (let i = 0; i < notificationPopupVariants.count; i++) { - let popup = notificationPopupVariants.objectAt(i); - if (popup && popup.notificationsVisible) { - popup.addNotification(notification); + if (notificationPopup.notificationsVisible) { + // Add notification to all popup instances + for (let i = 0; i < notificationPopup.children.length; i++) { + let child = notificationPopup.children[i]; + if (child.addNotification) { + child.addNotification(notification); + } } } - if (notificationHistoryWin) { notificationHistoryWin.addToHistory({ id: notification.id, @@ -107,19 +102,8 @@ Scope { } } - // Create notification popups for each selected monitor - Variants { - id: notificationPopupVariants - model: Quickshell.screens - - NotificationPopup { - property var modelData - barVisible: bar.visible - screen: modelData - visible: notificationsVisible && notificationModel.count > 0 && - (Settings.settings.notificationMonitors.includes(modelData.name) || - (Settings.settings.notificationMonitors.length === 0)) // Show on all if none selected - } + NotificationPopup { + id: notificationPopup } NotificationHistory { @@ -137,7 +121,7 @@ Scope { appLauncherPanel: appLauncherPanel lockScreen: lockScreen idleInhibitor: idleInhibitor - notificationPopupVariants: notificationPopupVariants + notificationPopup: notificationPopup } Connections { @@ -154,12 +138,11 @@ Scope { Timer { id: reloadTimer - interval: 500 + interval: 500 // ms repeat: false onTriggered: Quickshell.reload(true) } - // Handle screen configuration changes (delay reload if locked) Connections { target: Quickshell function onScreensChanged() { @@ -191,4 +174,4 @@ Scope { } } } -} +} \ No newline at end of file From 61e852ed517b37708f18e0e73a5a2f748b71c376 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Tue, 5 Aug 2025 22:17:47 +0200 Subject: [PATCH 03/43] Add wallpaper tab --- Widgets/Notification/NotificationPopup.qml | 2 +- Widgets/SettingsWindow/SettingsWindow.qml | 270 +++--- .../Tabs/Components/WallpaperSelector.qml | 118 +++ Widgets/SettingsWindow/Tabs/Wallpaper.qml | 802 ++++++++++++++++++ Widgets/Sidebar/Config/ProfileSettings.qml | 2 +- Widgets/Sidebar/Panel/Weather.qml | 10 + shell.qml | 9 +- 7 files changed, 1091 insertions(+), 122 deletions(-) create mode 100644 Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml create mode 100644 Widgets/SettingsWindow/Tabs/Wallpaper.qml diff --git a/Widgets/Notification/NotificationPopup.qml b/Widgets/Notification/NotificationPopup.qml index afaddd7..556e7eb 100644 --- a/Widgets/Notification/NotificationPopup.qml +++ b/Widgets/Notification/NotificationPopup.qml @@ -120,7 +120,7 @@ Item { Column { id: notificationColumn anchors.right: parent.right - spacing: window.spacing + spacing: panelWindow.spacing width: parent.width clip: false diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 29a695d..8f4f93b 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -6,6 +6,7 @@ import QtQuick.Layouts import QtQuick.Effects import qs.Settings import qs.Widgets.SettingsWindow.Tabs +import qs.Widgets.SettingsWindow.Tabs.Components PanelWindow { id: panelMain @@ -13,6 +14,8 @@ PanelWindow { implicitWidth: screen.width / 2 color: "transparent" + property int activeTabIndex: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand @@ -56,6 +59,10 @@ PanelWindow { Display {} } + Component { + id: wallpaperSettings + Wallpaper {} + } Rectangle { id: background @@ -80,7 +87,7 @@ PanelWindow { Rectangle { id: settings - color: Theme.backgroundTertiary + color: Theme.backgroundPrimary anchors { left: tabs.right top: parent.top @@ -110,13 +117,50 @@ PanelWindow { Text { id: tabName - text: "General" + text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : + activeTabIndex === 1 ? "Bar" : + activeTabIndex === 2 ? "Time & Weather" : + activeTabIndex === 3 ? "Recording" : + activeTabIndex === 4 ? "Network" : + activeTabIndex === 5 ? "Display" : + activeTabIndex === 6 ? "Wallpaper" : + activeTabIndex === 7 ? "Misc" : + activeTabIndex === 8 ? "About" : "General") font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.fillWidth: true } + // Wallpaper Selection Button (only visible on Wallpaper tab) + Rectangle { + width: 32 + height: 32 + radius: 16 + color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 + visible: activeTabIndex === 6 // Wallpaper tab index + + Text { + anchors.centerIn: parent + text: "image" + font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 18 + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + } + + MouseArea { + id: wallpaperButtonArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // Show the wallpaper selector + wallpaperSelector.show(); + } + } + } Rectangle { width: 32 @@ -199,6 +243,12 @@ PanelWindow { } } } + + // Wallpaper Selector Component + WallpaperSelector { + id: wallpaperSelector + anchors.fill: parent + } } } @@ -217,6 +267,7 @@ PanelWindow { width: parent.width spacing: 0 topPadding: 8 + bottomPadding: 8 Repeater { id: repeater @@ -227,140 +278,123 @@ PanelWindow { { icon: "photo_camera", text: "Recording" }, { icon: "wifi", text: "Network" }, { icon: "monitor", text: "Display" }, + { icon: "wallpaper", text: "Wallpaper" }, { icon: "settings_suggest", text: "Misc" }, { icon: "info", text: "About" } ] - delegate: Column { + delegate: Rectangle { width: tabs.width - height: 40 + height: 48 + color: "transparent" - Item { - width: parent.width - height: 39 + RowLayout { + anchors.fill: parent + spacing: 8 - RowLayout { - anchors.fill: parent - spacing: 8 - - - Rectangle { - id: activeIndicator - Layout.leftMargin: 8 - Layout.preferredWidth: 3 - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignVCenter - radius: 2 - color: Theme.accentPrimary - opacity: index === 0 ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 200 } } - } - - - Label { - id: icon - text: modelData.icon - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: index === 0 ? Theme.accentPrimary : Theme.textPrimary - opacity: index === 0 ? 1 : 0.8 - Layout.leftMargin: 20 - Layout.preferredWidth: 24 - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignVCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - - - Label { - id: label - text: modelData.text - font.pixelSize: 12 - color: index === 0 ? Theme.accentPrimary : Theme.textSecondary - font.weight: index === 0 ? Font.DemiBold : Font.Normal - Layout.fillWidth: true - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: 4 - Layout.rightMargin: 16 - verticalAlignment: Text.AlignVCenter - } + Rectangle { + id: activeIndicator + Layout.leftMargin: 8 + Layout.preferredWidth: 3 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignVCenter + radius: 2 + color: Theme.accentPrimary + opacity: index === activeTabIndex ? 1 : 0 + Behavior on opacity { NumberAnimation { duration: 200 } } } - MouseArea { - anchors.fill: parent - hoverEnabled: true - onClicked: { - - const newComponent = { - 0: generalSettings, - 1: barSettings, - 2: timeWeatherSettings, - 3: recordingSettings, - 4: networkSettings, - 5: displaySettings, - 6: miscSettings, - 7: aboutSettings - }[index]; - - - const tabNames = [ - "General", - "Bar", - "Time & Weather", - "Recording", - "Network", - "Display", - "Misc", - "About" - ]; - tabName.text = tabNames[index]; - - - if (settingsLoader.opacity === 1) { - - settingsLoader2.sourceComponent = newComponent; - settingsLoader.opacity = 0; - settingsLoader2.opacity = 1; - } else { - - settingsLoader.sourceComponent = newComponent; - settingsLoader2.opacity = 0; - settingsLoader.opacity = 1; - } - - - for (let i = 0; i < repeater.count; i++) { - let item = repeater.itemAt(i); - if (item) { - - let containerItem = item.children[0]; - - let rowLayout = containerItem.children[0]; - - let indicator = rowLayout.children[0]; - let icon = rowLayout.children[1]; - let label = rowLayout.children[2]; - - indicator.opacity = i === index ? 1 : 0; - icon.color = i === index ? Theme.accentPrimary : Theme.textPrimary; - icon.opacity = i === index ? 1 : 0.8; - label.color = i === index ? Theme.accentPrimary : Theme.textSecondary; - label.font.weight = i === index ? Font.Bold : Font.Normal; - } - } - } - } + Label { + id: icon + text: modelData.icon + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary + opacity: index === activeTabIndex ? 1 : 0.8 + Layout.leftMargin: 20 + Layout.preferredWidth: 24 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignVCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter } + Label { + id: label + text: modelData.text + font.pixelSize: 16 + color: index === activeTabIndex ? Theme.accentPrimary : + (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary) + font.weight: index === activeTabIndex ? Font.DemiBold : + (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal) + Layout.fillWidth: true + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.leftMargin: 4 + Layout.rightMargin: 16 + verticalAlignment: Text.AlignVCenter + } + } + + MouseArea { + id: tabMouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + activeTabIndex = index; + const newComponent = { + 0: generalSettings, + 1: barSettings, + 2: timeWeatherSettings, + 3: recordingSettings, + 4: networkSettings, + 5: displaySettings, + 6: wallpaperSettings, + 7: miscSettings, + 8: aboutSettings + }[index]; + + + const tabNames = [ + "General", + "Bar", + "Time & Weather", + "Recording", + "Network", + "Display", + "Wallpaper", + "Misc", + "About" + ]; + tabName.text = tabNames[index]; + + + if (settingsLoader.opacity === 1) { + + settingsLoader2.sourceComponent = newComponent; + settingsLoader.opacity = 0; + settingsLoader2.opacity = 1; + } else { + + settingsLoader.sourceComponent = newComponent; + settingsLoader2.opacity = 0; + settingsLoader.opacity = 1; + } + + + // Tab colors and indicators are now handled by property bindings + } + } + Rectangle { width: parent.width height: 1 color: Theme.outline opacity: 0.6 visible: index < (repeater.count - 1) + anchors.bottom: parent.bottom } } } diff --git a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml new file mode 100644 index 0000000..ba96960 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml @@ -0,0 +1,118 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components +import qs.Services + +Rectangle { + id: wallpaperOverlay + anchors.fill: parent + color: Theme.backgroundPrimary + visible: false + z: 1000 + + // Click outside to close + MouseArea { + anchors.fill: parent + onClicked: { + wallpaperOverlay.visible = false; + } + } + + // Content area that stops event propagation + MouseArea { + anchors.fill: parent + anchors.margins: 24 + onClicked: { + // Stop event propagation + } + + ColumnLayout { + anchors.fill: parent + spacing: 0 + + // Wallpaper Grid + Item { + Layout.fillWidth: true + Layout.fillHeight: true + clip: true + + ScrollView { + anchors.fill: parent + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + GridView { + id: wallpaperGrid + anchors.fill: parent + cellWidth: Math.max(120, (parent.width / 3) - 12) + cellHeight: cellWidth * 0.6 + model: WallpaperManager.wallpaperList + cacheBuffer: 64 + leftMargin: 8 + rightMargin: 8 + topMargin: 8 + bottomMargin: 8 + + delegate: Item { + width: wallpaperGrid.cellWidth - 8 + height: wallpaperGrid.cellHeight - 8 + + Rectangle { + id: wallpaperItem + anchors.fill: parent + anchors.margins: 4 + color: Theme.surface + radius: 12 + border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Image { + id: wallpaperImage + anchors.fill: parent + anchors.margins: 4 + source: modelData + fillMode: Image.PreserveAspectCrop + asynchronous: true + cache: true + smooth: true + mipmap: true + + sourceSize.width: Math.min(width, 480) + sourceSize.height: Math.min(height, 270) + + opacity: (wallpaperImage.status == Image.Ready) ? 1.0 : 0.0 + Behavior on opacity { + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + WallpaperManager.changeWallpaper(modelData); + wallpaperOverlay.visible = false; + } + } + } + } + } + } + } + } + } + + // Function to show the overlay and load wallpapers + function show() { + // Ensure wallpapers are loaded + WallpaperManager.loadWallpapers(); + wallpaperOverlay.visible = true; + } +} \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml new file mode 100644 index 0000000..950d4f4 --- /dev/null +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -0,0 +1,802 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings +import qs.Components +import qs.Services + +ColumnLayout { + id: root + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + + + ScrollView { + id: scrollView + Layout.fillWidth: true + Layout.fillHeight: true + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + + ColumnLayout { + width: scrollView.availableWidth + spacing: 0 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Wallpaper" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + + // Wallpaper Settings Category + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 16 + + Text { + text: "Wallpaper Settings" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + // Wallpaper Folder + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Wallpaper Folder" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Path to your wallpaper folder" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: folderInput + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "" + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.wallpaperFolder = text; + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: folderInput.forceActiveFocus() + } + } + } + } + + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + Layout.topMargin: 58 + + Text { + text: "Automation" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Random Wallpaper + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Random Wallpaper" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Automatically select random wallpapers from the folder" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: randomWallpaperSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: randomWallpaperThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; + } + } + } + } + } + + // Use Wallpaper Theme + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use Wallpaper Theme" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Automatically adjust theme colors based on wallpaper" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: wallpaperThemeSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wallpaperThemeThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; + } + } + } + } + } + + // Wallpaper Interval + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Wallpaper Interval" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "How often to change wallpapers automatically (in seconds)" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.wallpaperInterval + " seconds" + font.pixelSize: 13 + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + } + + Slider { + id: intervalSlider + Layout.fillWidth: true + from: 10 + to: 900 + stepSize: 10 + value: Settings.settings.wallpaperInterval + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.wallpaperInterval = Math.round(value); + } + + background: Rectangle { + x: intervalSlider.leftPadding + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: intervalSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: intervalSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + } + + handle: Rectangle { + x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + } + } + } + + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + Layout.topMargin: 58 + + Text { + text: "SWWW" + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Use SWWW + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use SWWW" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Use SWWW daemon for advanced wallpaper management" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: swwwSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: swwwThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useSWWW = !Settings.settings.useSWWW; + } + } + } + } + } + + // SWWW Settings (only visible when useSWWW is enabled) + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + visible: Settings.settings.useSWWW + + // Resize Mode + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Resize Mode" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "How SWWW should resize wallpapers to fit the screen" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + ComboBox { + id: resizeComboBox + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + model: ["no", "crop", "fit", "stretch"] + currentIndex: model.indexOf(Settings.settings.wallpaperResize) + onActivated: { + Settings.settings.wallpaperResize = model[index]; + } + + background: Rectangle { + color: "transparent" + } + + contentItem: Text { + text: resizeComboBox.displayText + font: resizeComboBox.font + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + popup: Popup { + y: resizeComboBox.height + width: resizeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null + currentIndex: resizeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: Theme.surface + border.color: Theme.outline + border.width: 1 + radius: 8 + } + } + + delegate: ItemDelegate { + width: resizeComboBox.width + contentItem: Text { + text: modelData + color: Theme.textPrimary + font: resizeComboBox.font + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + highlighted: resizeComboBox.highlightedIndex === index + background: Rectangle { + color: parent.highlighted ? Theme.accentPrimary : "transparent" + } + } + } + } + } + + // Transition Type + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Transition Type" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Animation type when switching between wallpapers" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + ComboBox { + id: transitionTypeComboBox + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] + currentIndex: model.indexOf(Settings.settings.transitionType) + onActivated: { + Settings.settings.transitionType = model[index]; + } + + background: Rectangle { + color: "transparent" + } + + contentItem: Text { + text: transitionTypeComboBox.displayText + font: transitionTypeComboBox.font + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + popup: Popup { + y: transitionTypeComboBox.height + width: transitionTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null + currentIndex: transitionTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { } + } + + background: Rectangle { + color: Theme.surface + border.color: Theme.outline + border.width: 1 + radius: 8 + } + } + + delegate: ItemDelegate { + width: transitionTypeComboBox.width + contentItem: Text { + text: modelData + color: Theme.textPrimary + font: transitionTypeComboBox.font + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + highlighted: transitionTypeComboBox.highlightedIndex === index + background: Rectangle { + color: parent.highlighted ? Theme.accentPrimary : "transparent" + } + } + } + } + } + + // Transition FPS + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Transition FPS" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Frames per second for transition animations" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.transitionFps + " FPS" + font.pixelSize: 13 + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + } + + Slider { + id: fpsSlider + Layout.fillWidth: true + from: 30 + to: 500 + stepSize: 5 + value: Settings.settings.transitionFps + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionFps = Math.round(value); + } + + background: Rectangle { + x: fpsSlider.leftPadding + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: fpsSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: fpsSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + } + + handle: Rectangle { + x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + } + } + + // Transition Duration + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Transition Duration" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Duration of transition animations in seconds" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.transitionDuration.toFixed(3) + " seconds" + font.pixelSize: 13 + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + } + + Slider { + id: durationSlider + Layout.fillWidth: true + from: 0.25 + to: 10 + stepSize: 0.05 + value: Settings.settings.transitionDuration + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionDuration = value; + } + + background: Rectangle { + x: durationSlider.leftPadding + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: durationSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: durationSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + } + + handle: Rectangle { + x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } +} \ No newline at end of file diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml index 3451e25..5cedebe 100644 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -89,7 +89,7 @@ Rectangle { anchors.rightMargin: 12 anchors.topMargin: 6 anchors.bottomMargin: 6 - text: Settings.settings.profileImage + text: Settings.settings.profileImage !== undefined ? Settings.settings.profileImage : "" font.family: Theme.fontFamily font.pixelSize: 13 color: Theme.textPrimary diff --git a/Widgets/Sidebar/Panel/Weather.qml b/Widgets/Sidebar/Panel/Weather.qml index c6d0fd8..664f3fa 100644 --- a/Widgets/Sidebar/Panel/Weather.qml +++ b/Widgets/Sidebar/Panel/Weather.qml @@ -16,6 +16,16 @@ Rectangle { property string errorString: "" property bool isVisible: false + // Auto-refetch weather when city changes + Connections { + target: Settings.settings + function onWeatherCityChanged() { + if (isVisible && city !== "") { + fetchCityWeather() + } + } + } + Component.onCompleted: { if (isVisible) { fetchCityWeather() diff --git a/shell.qml b/shell.qml index a06df9c..844f8ab 100644 --- a/shell.qml +++ b/shell.qml @@ -53,8 +53,13 @@ Scope { property var notificationHistoryWin: notificationHistoryWin } - Dock { - id: dock + Variants { + model: Quickshell.screens + + Dock { + id: dock + property var modelData + } } Applauncher { From 3f6bc3414d577e7206528cbe5f9299ff37820918 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Wed, 6 Aug 2025 16:02:33 +0200 Subject: [PATCH 04/43] Possible ram improvement, move settings into sidebar --- Bar/Bar.qml | 9 +- Bar/Modules/Bluetooth.qml | 330 ++++---- Bar/Modules/SettingsButton.qml | 80 -- Bar/Modules/Wifi.qml | 488 ++++++------ Components/Corners.qml | 45 +- Helpers/IPCHandlers.qml | 1 - Widgets/Notification/NotificationIcon.qml | 46 +- Widgets/SettingsWindow/SettingsWindow.qml | 71 +- .../Sidebar/Config/CollapsibleCategory.qml | 56 -- Widgets/Sidebar/Config/ProfileSettings.qml | 643 ---------------- Widgets/Sidebar/Config/SettingsModal.qml | 190 +---- Widgets/Sidebar/Config/WallpaperSettings.qml | 722 ------------------ Widgets/Sidebar/Config/WeatherSettings.qml | 275 ------- Widgets/Sidebar/Panel/PanelPopup.qml | 120 ++- Widgets/Sidebar/Panel/SettingsIcon.qml | 80 ++ Widgets/Sidebar/Panel/Weather.qml | 43 +- shell.qml | 50 +- 17 files changed, 795 insertions(+), 2454 deletions(-) delete mode 100644 Bar/Modules/SettingsButton.qml delete mode 100644 Widgets/Sidebar/Config/CollapsibleCategory.qml delete mode 100644 Widgets/Sidebar/Config/ProfileSettings.qml delete mode 100644 Widgets/Sidebar/Config/WallpaperSettings.qml delete mode 100644 Widgets/Sidebar/Config/WeatherSettings.qml create mode 100644 Widgets/Sidebar/Panel/SettingsIcon.qml diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 8d355ba..730c61b 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -102,6 +102,7 @@ Scope { } NotificationIcon { + shell: rootScope.shell anchors.verticalCenter: parent.verticalCenter } @@ -135,12 +136,9 @@ Scope { anchors.verticalCenter: parent.verticalCenter } - SettingsButton { - anchors.verticalCenter: parent.verticalCenter - } - PanelPopup { id: sidebarPopup + shell: rootScope.shell } Button { @@ -151,8 +149,7 @@ Scope { } } - Background {} - Overview {} + } PanelWindow { diff --git a/Bar/Modules/Bluetooth.qml b/Bar/Modules/Bluetooth.qml index 80beb61..e8ae996 100644 --- a/Bar/Modules/Bluetooth.qml +++ b/Bar/Modules/Bluetooth.qml @@ -55,14 +55,19 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - bluetoothMenu.visible = !bluetoothMenu.visible; - // Enable adapter and start discovery when menu opens - if (bluetoothMenu.visible && Bluetooth.defaultAdapter) { - if (!Bluetooth.defaultAdapter.enabled) { - Bluetooth.defaultAdapter.enabled = true; - } - if (!Bluetooth.defaultAdapter.discovering) { - Bluetooth.defaultAdapter.discovering = true; + if (!bluetoothMenuLoader.active) { + bluetoothMenuLoader.loading = true; + } + if (bluetoothMenuLoader.item) { + bluetoothMenuLoader.item.visible = !bluetoothMenuLoader.item.visible; + // Enable adapter and start discovery when menu opens + if (bluetoothMenuLoader.item.visible && Bluetooth.defaultAdapter) { + if (!Bluetooth.defaultAdapter.enabled) { + Bluetooth.defaultAdapter.enabled = true; + } + if (!Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = true; + } } } } @@ -80,190 +85,195 @@ Item { delay: 200 } - PanelWindow { - id: bluetoothMenu - implicitWidth: 320 - implicitHeight: 480 - visible: false - color: "transparent" - anchors.top: true - anchors.right: true - margins.right: 0 - margins.top: 0 - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // LazyLoader for Bluetooth menu + LazyLoader { + id: bluetoothMenuLoader + loading: false + component: PanelWindow { + id: bluetoothMenu + implicitWidth: 320 + implicitHeight: 480 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - onVisibleChanged: { - // Stop discovery when menu closes to save battery - if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { - Bluetooth.defaultAdapter.discovering = false; + onVisibleChanged: { + // Stop discovery when menu closes to save battery + if (!visible && Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = false; + } } - } - Rectangle { - anchors.fill: parent - color: Theme.backgroundPrimary - radius: 12 - - ColumnLayout { + Rectangle { anchors.fill: parent - anchors.margins: 16 - spacing: 16 + color: Theme.backgroundPrimary + radius: 12 - RowLayout { - Layout.fillWidth: true - spacing: 12 + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 16 - Text { - text: "bluetooth" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.accentPrimary - } - - Text { - text: "Bluetooth Devices" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary + RowLayout { Layout.fillWidth: true - } + spacing: 12 - IconButton { - icon: "close" - onClicked: { - bluetoothMenu.visible = false; - if (Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { - Bluetooth.defaultAdapter.discovering = false; + Text { + text: "bluetooth" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.accentPrimary + } + + Text { + text: "Bluetooth Devices" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + + IconButton { + icon: "close" + onClicked: { + bluetoothMenu.visible = false; + if (Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering) { + Bluetooth.defaultAdapter.discovering = false; + } } } } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.outline - opacity: 0.12 - } + Rectangle { + Layout.fillWidth: true + height: 1 + color: Theme.outline + opacity: 0.12 + } - ListView { - id: deviceList - Layout.fillWidth: true - Layout.fillHeight: true - model: Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.devices : [] - spacing: 8 - clip: true + ListView { + id: deviceList + Layout.fillWidth: true + Layout.fillHeight: true + model: Bluetooth.defaultAdapter ? Bluetooth.defaultAdapter.devices : [] + spacing: 8 + clip: true - delegate: Item { - width: parent.width - height: 48 + delegate: Item { + width: parent.width + height: 48 - Rectangle { - anchors.fill: parent - radius: 8 - color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (deviceMouseArea.containsMouse ? Theme.highlight : "transparent") - - RowLayout { + Rectangle { anchors.fill: parent - anchors.margins: 8 - spacing: 8 + radius: 8 + color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (deviceMouseArea.containsMouse ? Theme.highlight : "transparent") - Text { - text: modelData.connected ? "bluetooth" : "bluetooth_disabled" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 Text { - text: { - let deviceName = modelData.name || modelData.deviceName || "Unknown Device"; - // Hide MAC addresses and show "Unknown Device" instead - let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; - if (macPattern.test(deviceName)) { - return "Unknown Device"; - } - return deviceName; - } - color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 - elide: Text.ElideRight - Layout.fillWidth: true - } - - Text { - text: { - let deviceName = modelData.name || modelData.deviceName || ""; - let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; - if (macPattern.test(deviceName)) { - // Show MAC address in subtitle for unnamed devices - return modelData.address + " • " + (modelData.paired ? "Paired" : "Available"); - } else { - // Show only status for named devices - return modelData.paired ? "Paired" : "Available"; - } - } + text: modelData.connected ? "bluetooth" : "bluetooth_disabled" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 - elide: Text.ElideRight + } + + ColumnLayout { Layout.fillWidth: true + spacing: 2 + + Text { + text: { + let deviceName = modelData.name || modelData.deviceName || "Unknown Device"; + // Hide MAC addresses and show "Unknown Device" instead + let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; + if (macPattern.test(deviceName)) { + return "Unknown Device"; + } + return deviceName; + } + color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) + font.pixelSize: 14 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: { + let deviceName = modelData.name || modelData.deviceName || ""; + let macPattern = /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/; + if (macPattern.test(deviceName)) { + // Show MAC address in subtitle for unnamed devices + return modelData.address + " • " + (modelData.paired ? "Paired" : "Available"); + } else { + // Show only status for named devices + return modelData.paired ? "Paired" : "Available"; + } + } + color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + Item { + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + visible: modelData.pairing || modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting + + Spinner { + visible: parent.visible + running: parent.visible + color: Theme.accentPrimary + anchors.centerIn: parent + size: 22 + } } } - Item { - Layout.preferredWidth: 22 - Layout.preferredHeight: 22 - visible: modelData.pairing || modelData.state === BluetoothDeviceState.Connecting || modelData.state === BluetoothDeviceState.Disconnecting - - Spinner { - visible: parent.visible - running: parent.visible - color: Theme.accentPrimary - anchors.centerIn: parent - size: 22 - } - } - } - - MouseArea { - id: deviceMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - // Handle device actions: disconnect, pair, or connect - if (modelData.connected) { - modelData.disconnect(); - } else if (!modelData.paired) { - modelData.pair(); - } else { - modelData.connect(); + MouseArea { + id: deviceMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + // Handle device actions: disconnect, pair, or connect + if (modelData.connected) { + modelData.disconnect(); + } else if (!modelData.paired) { + modelData.pair(); + } else { + modelData.connect(); + } } } } } } - } - // Discovering indicator - RowLayout { - Layout.fillWidth: true - spacing: 8 - visible: Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering + // Discovering indicator + RowLayout { + Layout.fillWidth: true + spacing: 8 + visible: Bluetooth.defaultAdapter && Bluetooth.defaultAdapter.discovering - Text { - text: "Scanning for devices..." - font.pixelSize: 12 - color: Theme.textSecondary - } + Text { + text: "Scanning for devices..." + font.pixelSize: 12 + color: Theme.textSecondary + } - Spinner { - running: true - color: Theme.accentPrimary - size: 16 + Spinner { + running: true + color: Theme.accentPrimary + size: 16 + } } } } diff --git a/Bar/Modules/SettingsButton.qml b/Bar/Modules/SettingsButton.qml deleted file mode 100644 index 7a9bede..0000000 --- a/Bar/Modules/SettingsButton.qml +++ /dev/null @@ -1,80 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import qs.Settings -import qs.Components -import qs.Widgets.SettingsWindow - -Item { - id: root - width: 22 - height: 22 - - property var settingsWindow: null - - Rectangle { - id: button - anchors.fill: parent - color: "transparent" - radius: width / 2 - - Text { - anchors.centerIn: parent - text: "settings" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 - color: mouseArea.containsMouse ? Theme.accentPrimary : Theme.textPrimary - } - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - - onClicked: { - if (!settingsWindow) { - // Create new window - settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues - if (settingsWindow) { - settingsWindow.visible = true; - // Handle window closure - settingsWindow.visibleChanged.connect(function() { - if (settingsWindow && !settingsWindow.visible) { - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.destroy(); - } - }); - } - } else if (settingsWindow.visible) { - // Close and destroy window - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.visible = false; - windowToDestroy.destroy(); - } - } - } - - StyledTooltip { - text: "Settings" - targetItem: mouseArea - tooltipVisible: mouseArea.containsMouse - } - } - - Component { - id: settingsComponent - SettingsWindow {} - } - - // Clean up on destruction - Component.onDestruction: { - if (settingsWindow) { - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.destroy(); - } - } -} \ No newline at end of file diff --git a/Bar/Modules/Wifi.qml b/Bar/Modules/Wifi.qml index 5c75ba1..13918f5 100644 --- a/Bar/Modules/Wifi.qml +++ b/Bar/Modules/Wifi.qml @@ -61,11 +61,16 @@ Item { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - wifiMenu.visible = !wifiMenu.visible; - if (wifiMenu.visible) { - network.onMenuOpened(); - } else { - network.onMenuClosed(); + if (!wifiMenuLoader.active) { + wifiMenuLoader.loading = true; + } + if (wifiMenuLoader.item) { + wifiMenuLoader.item.visible = !wifiMenuLoader.item.visible; + if (wifiMenuLoader.item.visible) { + network.onMenuOpened(); + } else { + network.onMenuClosed(); + } } } onEntered: wifiTooltip.tooltipVisible = true @@ -82,282 +87,287 @@ Item { delay: 200 } - PanelWindow { - id: wifiMenu - implicitWidth: 320 - implicitHeight: 480 - visible: false - color: "transparent" - anchors.top: true - anchors.right: true - margins.right: 0 - margins.top: 0 - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // LazyLoader for WiFi menu + LazyLoader { + id: wifiMenuLoader + loading: false + component: PanelWindow { + id: wifiMenu + implicitWidth: 320 + implicitHeight: 480 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - Rectangle { - anchors.fill: parent - color: Theme.backgroundPrimary - radius: 12 - - ColumnLayout { + Rectangle { anchors.fill: parent - anchors.margins: 16 - spacing: 16 + color: Theme.backgroundPrimary + radius: 12 - RowLayout { - Layout.fillWidth: true - spacing: 12 + ColumnLayout { + anchors.fill: parent + anchors.margins: 16 + spacing: 16 - Text { - text: "wifi" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.accentPrimary - } - - Text { - text: "WiFi Networks" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary + RowLayout { Layout.fillWidth: true - } + spacing: 12 - IconButton { - icon: "refresh" - onClicked: network.refreshNetworks() - } + Text { + text: "wifi" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.accentPrimary + } - IconButton { - icon: "close" - onClicked: { - wifiMenu.visible = false; - network.onMenuClosed(); + Text { + text: "WiFi Networks" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + + IconButton { + icon: "refresh" + onClicked: network.refreshNetworks() + } + + IconButton { + icon: "close" + onClicked: { + wifiMenu.visible = false; + network.onMenuClosed(); + } } } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.outline - opacity: 0.12 - } + Rectangle { + Layout.fillWidth: true + height: 1 + color: Theme.outline + opacity: 0.12 + } - ListView { - id: networkList - Layout.fillWidth: true - Layout.fillHeight: true - model: Object.values(network.networks) - spacing: 8 - clip: true + ListView { + id: networkList + Layout.fillWidth: true + Layout.fillHeight: true + model: Object.values(network.networks) + spacing: 8 + clip: true - delegate: Item { - width: parent.width - height: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 108 : 48 // 48 for network + 60 for password prompt + delegate: Item { + width: parent.width + height: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 108 : 48 // 48 for network + 60 for password prompt - ColumnLayout { - anchors.fill: parent - spacing: 0 + ColumnLayout { + anchors.fill: parent + spacing: 0 - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 48 - radius: 8 - color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (networkMouseArea.containsMouse ? Theme.highlight : "transparent") + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 48 + radius: 8 + color: modelData.connected ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.44) : (networkMouseArea.containsMouse ? Theme.highlight : "transparent") - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 8 - - Text { - text: network.signalIcon(modelData.signal) - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 Text { - text: modelData.ssid || "Unknown Network" - color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 - elide: Text.ElideRight - Layout.fillWidth: true - } - - Text { - text: modelData.security && modelData.security !== "--" ? modelData.security : "Open" + text: network.signalIcon(modelData.signal) + font.family: "Material Symbols Outlined" + font.pixelSize: 18 color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 - elide: Text.ElideRight + } + + ColumnLayout { Layout.fillWidth: true + spacing: 2 + + Text { + text: modelData.ssid || "Unknown Network" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) + font.pixelSize: 14 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: modelData.security && modelData.security !== "--" ? modelData.security : "Open" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + visible: network.connectStatusSsid === modelData.ssid && network.connectStatus === "error" && network.connectError.length > 0 + text: network.connectError + color: Theme.error + font.pixelSize: 11 + elide: Text.ElideRight + Layout.fillWidth: true + } + } + + Item { + Layout.preferredWidth: 22 + Layout.preferredHeight: 22 + visible: network.connectStatusSsid === modelData.ssid && (network.connectStatus !== "" || network.connectingSsid === modelData.ssid) + + Spinner { + visible: network.connectingSsid === modelData.ssid + running: network.connectingSsid === modelData.ssid + color: Theme.accentPrimary + anchors.centerIn: parent + size: 22 + } + + Text { + visible: network.connectStatus === "success" && !network.connectingSsid + text: "check_circle" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: "#43a047" + anchors.centerIn: parent + } + + Text { + visible: network.connectStatus === "error" && !network.connectingSsid + text: "error" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: Theme.error + anchors.centerIn: parent + } } Text { - visible: network.connectStatusSsid === modelData.ssid && network.connectStatus === "error" && network.connectError.length > 0 - text: network.connectError - color: Theme.error + visible: modelData.connected + text: "connected" + color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary font.pixelSize: 11 - elide: Text.ElideRight - Layout.fillWidth: true } } - Item { - Layout.preferredWidth: 22 - Layout.preferredHeight: 22 - visible: network.connectStatusSsid === modelData.ssid && (network.connectStatus !== "" || network.connectingSsid === modelData.ssid) - - Spinner { - visible: network.connectingSsid === modelData.ssid - running: network.connectingSsid === modelData.ssid - color: Theme.accentPrimary - anchors.centerIn: parent - size: 22 + MouseArea { + id: networkMouseArea + anchors.fill: parent + hoverEnabled: true + onClicked: { + if (modelData.connected) { + network.disconnectNetwork(modelData.ssid); + } else if (network.isSecured(modelData.security) && !modelData.existing) { + passwordPromptSsid = modelData.ssid; + showPasswordPrompt = true; + passwordInput = ""; // Clear previous input + Qt.callLater(function() { + passwordInputField.forceActiveFocus(); + }); + } else { + network.connectNetwork(modelData.ssid, modelData.security); + } } - - Text { - visible: network.connectStatus === "success" && !network.connectingSsid - text: "check_circle" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: "#43a047" - anchors.centerIn: parent - } - - Text { - visible: network.connectStatus === "error" && !network.connectingSsid - text: "error" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: Theme.error - anchors.centerIn: parent - } - } - - Text { - visible: modelData.connected - text: "connected" - color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary - font.pixelSize: 11 } } - MouseArea { - id: networkMouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - if (modelData.connected) { - network.disconnectNetwork(modelData.ssid); - } else if (network.isSecured(modelData.security) && !modelData.existing) { - passwordPromptSsid = modelData.ssid; - showPasswordPrompt = true; - passwordInput = ""; // Clear previous input - Qt.callLater(function() { - passwordInputField.forceActiveFocus(); - }); - } else { - network.connectNetwork(modelData.ssid, modelData.security); + // Password prompt section + Rectangle { + id: passwordPromptSection + Layout.fillWidth: true + Layout.preferredHeight: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 60 : 0 + Layout.margins: 8 + visible: modelData.ssid === passwordPromptSsid && showPasswordPrompt + color: Theme.surfaceVariant + radius: 8 + + RowLayout { + anchors.fill: parent + anchors.margins: 12 + spacing: 10 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 36 + + Rectangle { + anchors.fill: parent + radius: 8 + color: "transparent" + border.color: passwordInputField.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: passwordInputField + anchors.fill: parent + anchors.margins: 12 + text: passwordInput + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + focus: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhNone + echoMode: TextInput.Password + onTextChanged: passwordInput = text + onAccepted: { + network.submitPassword(passwordPromptSsid, passwordInput); + showPasswordPrompt = false; + } + + MouseArea { + id: passwordInputMouseArea + anchors.fill: parent + onClicked: passwordInputField.forceActiveFocus() + } + } + } } - } - } - } - - // Password prompt section - Rectangle { - id: passwordPromptSection - Layout.fillWidth: true - Layout.preferredHeight: modelData.ssid === passwordPromptSsid && showPasswordPrompt ? 60 : 0 - Layout.margins: 8 - visible: modelData.ssid === passwordPromptSsid && showPasswordPrompt - color: Theme.surfaceVariant - radius: 8 - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 10 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 36 Rectangle { - anchors.fill: parent - radius: 8 - color: "transparent" - border.color: passwordInputField.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 + Layout.preferredWidth: 80 + Layout.preferredHeight: 36 + radius: 18 + color: Theme.accentPrimary + border.color: Theme.accentPrimary + border.width: 0 + opacity: 1.0 - TextInput { - id: passwordInputField + Behavior on color { + ColorAnimation { + duration: 100 + } + } + + MouseArea { anchors.fill: parent - anchors.margins: 12 - text: passwordInput - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - focus: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhNone - echoMode: TextInput.Password - onTextChanged: passwordInput = text - onAccepted: { + onClicked: { network.submitPassword(passwordPromptSsid, passwordInput); showPasswordPrompt = false; } - - MouseArea { - id: passwordInputMouseArea - anchors.fill: parent - onClicked: passwordInputField.forceActiveFocus() - } + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onEntered: parent.color = Qt.darker(Theme.accentPrimary, 1.1) + onExited: parent.color = Theme.accentPrimary } - } - } - Rectangle { - Layout.preferredWidth: 80 - Layout.preferredHeight: 36 - radius: 18 - color: Theme.accentPrimary - border.color: Theme.accentPrimary - border.width: 0 - opacity: 1.0 - - Behavior on color { - ColorAnimation { - duration: 100 + Text { + anchors.centerIn: parent + text: "Connect" + color: Theme.backgroundPrimary + font.pixelSize: 14 + font.bold: true } } - - MouseArea { - anchors.fill: parent - onClicked: { - network.submitPassword(passwordPromptSsid, passwordInput); - showPasswordPrompt = false; - } - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onEntered: parent.color = Qt.darker(Theme.accentPrimary, 1.1) - onExited: parent.color = Theme.accentPrimary - } - - Text { - anchors.centerIn: parent - text: "Connect" - color: Theme.backgroundPrimary - font.pixelSize: 14 - font.bold: true - } } } } diff --git a/Components/Corners.qml b/Components/Corners.qml index 0ec37ef..f41d14c 100644 --- a/Components/Corners.qml +++ b/Components/Corners.qml @@ -16,33 +16,33 @@ Shape { property var modelData: null - // Position flags derived from position string - property bool _isTop: position.includes("top") - property bool _isLeft: position.includes("left") - property bool _isRight: position.includes("right") - property bool _isBottom: position.includes("bottom") + // Position flags derived from position string - calculated once + readonly property bool _isTop: position.includes("top") + readonly property bool _isLeft: position.includes("left") + readonly property bool _isRight: position.includes("right") + readonly property bool _isBottom: position.includes("bottom") // Shift the path vertically if offsetY is negative to pull shape up - property real pathOffsetY: Math.min(offsetY, 0) + readonly property real pathOffsetY: Math.min(offsetY, 0) // Base coordinates for left corner shape, shifted by pathOffsetY vertically - property real _baseStartX: 30 * size - property real _baseStartY: (_isTop ? 20 * size : 0) + pathOffsetY - property real _baseLineX: 30 * size - property real _baseLineY: (_isTop ? 0 : 20 * size) + pathOffsetY - property real _baseArcX: 50 * size - property real _baseArcY: (_isTop ? 20 * size : 0) + pathOffsetY + readonly property real _baseStartX: 30 * size + readonly property real _baseStartY: (_isTop ? 20 * size : 0) + pathOffsetY + readonly property real _baseLineX: 30 * size + readonly property real _baseLineY: (_isTop ? 0 : 20 * size) + pathOffsetY + readonly property real _baseArcX: 50 * size + readonly property real _baseArcY: (_isTop ? 20 * size : 0) + pathOffsetY // Mirror coordinates for right corners - property real _startX: _isRight ? (concaveWidth - _baseStartX) : _baseStartX - property real _startY: _baseStartY - property real _lineX: _isRight ? (concaveWidth - _baseLineX) : _baseLineX - property real _lineY: _baseLineY - property real _arcX: _isRight ? (concaveWidth - _baseArcX) : _baseArcX - property real _arcY: _baseArcY + readonly property real _startX: _isRight ? (concaveWidth - _baseStartX) : _baseStartX + readonly property real _startY: _baseStartY + readonly property real _lineX: _isRight ? (concaveWidth - _baseLineX) : _baseLineX + readonly property real _lineY: _baseLineY + readonly property real _arcX: _isRight ? (concaveWidth - _baseArcX) : _baseArcX + readonly property real _arcY: _baseArcY // Arc direction varies by corner to maintain proper concave shape - property int _arcDirection: { + readonly property int _arcDirection: { if (_isTop && _isLeft) return PathArc.Counterclockwise if (_isTop && _isRight) return PathArc.Clockwise if (_isBottom && _isLeft) return PathArc.Clockwise @@ -57,9 +57,10 @@ Shape { x: _isLeft ? offsetX : (parent ? parent.width - width + offsetX : 0) y: _isTop ? offsetY : (parent ? parent.height - height + offsetY : 0) - preferredRendererType: Shape.CurveRenderer - layer.enabled: true - layer.samples: 4 + // Optimized rendering settings - reduced quality for better performance + preferredRendererType: Shape.GeometryRenderer // Use simpler renderer + layer.enabled: false // Disable layer rendering to save memory + antialiasing: true // Use standard antialiasing instead of MSAA ShapePath { strokeWidth: 0 diff --git a/Helpers/IPCHandlers.qml b/Helpers/IPCHandlers.qml index aa8ef69..90ed5c7 100644 --- a/Helpers/IPCHandlers.qml +++ b/Helpers/IPCHandlers.qml @@ -14,7 +14,6 @@ IpcHandler { idleInhibitor.toggle() } - function toggleNotificationPopup(): void { console.log("[IPC] NotificationPopup toggle() called") // Use the global toggle function from the notification manager diff --git a/Widgets/Notification/NotificationIcon.qml b/Widgets/Notification/NotificationIcon.qml index cfb7b6f..0a9556c 100644 --- a/Widgets/Notification/NotificationIcon.qml +++ b/Widgets/Notification/NotificationIcon.qml @@ -8,13 +8,25 @@ Item { id: root width: 22; height: 22 property bool isSilence: false - + property var shell: null Process { id: rightClickProcess command: ["qs","ipc", "call", "globalIPC", "toggleNotificationPopup"] } + // Timer to check when NotificationHistory is loaded + Timer { + id: checkHistoryTimer + interval: 50 + repeat: true + onTriggered: { + if (shell && shell.notificationHistoryWin) { + shell.notificationHistoryWin.visible = true; + checkHistoryTimer.stop(); + } + } + } Item { id: bell @@ -22,11 +34,23 @@ Item { Text { id: bellText anchors.centerIn: parent - text: notificationHistoryWin.hasUnread ? "notifications_unread" : "notifications" + text: { + if (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread) { + return "notifications_unread"; + } else { + return "notifications"; + } + } font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" font.pixelSize: 16 - font.weight: notificationHistoryWin.hasUnread ? Font.Bold : Font.Normal - color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (notificationHistoryWin.hasUnread ? Theme.accentPrimary : Theme.textDisabled) + font.weight: { + if (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread) { + return Font.Bold; + } else { + return Font.Normal; + } + } + color: mouseAreaBell.containsMouse ? Theme.accentPrimary : (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread ? Theme.accentPrimary : Theme.textDisabled) } MouseArea { id: mouseAreaBell @@ -42,10 +66,18 @@ Item { } if (mouse.button === Qt.LeftButton){ - notificationHistoryWin.visible = !notificationHistoryWin.visible - return; + if (shell) { + if (!shell.notificationHistoryWin) { + // Use the shell function to load notification history + shell.loadNotificationHistory(); + checkHistoryTimer.start(); + } else { + // Already loaded, just toggle visibility + shell.notificationHistoryWin.visible = !shell.notificationHistoryWin.visible; + } + } + return; } - } onEntered: notificationTooltip.tooltipVisible = true onExited: notificationTooltip.tooltipVisible = false diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 8f4f93b..016379c 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -10,14 +10,32 @@ import qs.Widgets.SettingsWindow.Tabs.Components PanelWindow { id: panelMain - implicitHeight: screen.height / 2 - implicitWidth: screen.width / 2 + implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400 + implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600 color: "transparent" property int activeTabIndex: 0 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // Add safety checks for component loading + Component.onCompleted: { + // Ensure we start with a valid tab + if (activeTabIndex < 0 || activeTabIndex > 8) { + activeTabIndex = 0; + } + } + + // Cleanup when window is hidden + onVisibleChanged: { + if (!visible) { + // Reset to default tab when hiding to prevent state issues + activeTabIndex = 0; + if (tabName) { + tabName.text = "General"; + } + } + } Component { id: generalSettings @@ -84,7 +102,6 @@ PanelWindow { } } - Rectangle { id: settings color: Theme.backgroundPrimary @@ -98,7 +115,6 @@ PanelWindow { topRightRadius: 20 bottomRightRadius: 20 - Rectangle { id: headerArea anchors { @@ -114,7 +130,6 @@ PanelWindow { anchors.fill: parent spacing: 12 - Text { id: tabName text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : @@ -189,7 +204,6 @@ PanelWindow { } } - Rectangle { anchors { top: headerArea.bottom @@ -213,35 +227,11 @@ PanelWindow { topMargin: 32 } - + // Simplified single loader approach Loader { id: settingsLoader anchors.fill: parent sourceComponent: generalSettings - opacity: 1 - visible: opacity > 0 - - Behavior on opacity { - NumberAnimation { - duration: 150 - easing.type: Easing.InOutQuad - } - } - } - - - Loader { - id: settingsLoader2 - anchors.fill: parent - opacity: 0 - visible: opacity > 0 - - Behavior on opacity { - NumberAnimation { - duration: 150 - easing.type: Easing.InOutQuad - } - } } // Wallpaper Selector Component @@ -252,11 +242,10 @@ PanelWindow { } } - Rectangle { id: tabs color: Theme.surface - width: screen.width / 9 + width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100 height: panelMain.height topLeftRadius: 20 bottomLeftRadius: 20 @@ -356,7 +345,6 @@ PanelWindow { 8: aboutSettings }[index]; - const tabNames = [ "General", "Bar", @@ -370,21 +358,10 @@ PanelWindow { ]; tabName.text = tabNames[index]; - - if (settingsLoader.opacity === 1) { - - settingsLoader2.sourceComponent = newComponent; - settingsLoader.opacity = 0; - settingsLoader2.opacity = 1; - } else { - + // Simple component switching + if (newComponent) { settingsLoader.sourceComponent = newComponent; - settingsLoader2.opacity = 0; - settingsLoader.opacity = 1; } - - - // Tab colors and indicators are now handled by property bindings } } diff --git a/Widgets/Sidebar/Config/CollapsibleCategory.qml b/Widgets/Sidebar/Config/CollapsibleCategory.qml deleted file mode 100644 index d0c2eb7..0000000 --- a/Widgets/Sidebar/Config/CollapsibleCategory.qml +++ /dev/null @@ -1,56 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import qs.Settings - -ColumnLayout { - property alias title: headerText.text - property bool expanded: false // Hidden by default - default property alias content: contentItem.children - - Rectangle { - Layout.fillWidth: true - height: 44 - radius: 12 - color: Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 8 - Item { width: 2 } - Text { - id: headerText - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody - font.bold: true - color: Theme.textPrimary - } - Item { Layout.fillWidth: true } - Rectangle { - width: 32; height: 32 - color: "transparent" - Text { - anchors.centerIn: parent - text: expanded ? "expand_less" : "expand_more" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody - color: Theme.accentPrimary - } - } - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: expanded = !expanded - } - } - Item { height: 8 } - ColumnLayout { - id: contentItem - Layout.fillWidth: true - visible: expanded - spacing: 0 - } -} \ No newline at end of file diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml deleted file mode 100644 index 5cedebe..0000000 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ /dev/null @@ -1,643 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Effects -import QtQuick.Controls -import Quickshell.Widgets -import qs.Components -import qs.Settings - -Rectangle { - id: profileSettingsCard - Layout.fillWidth: true - Layout.preferredHeight: 690 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - // Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - Text { - text: "settings" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - Text { - text: "Profile Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - // Profile Image Input Section - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Profile Image" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - // Profile image - Rectangle { - width: 48 - height: 48 - radius: 24 - - // Border - Rectangle { - anchors.fill: parent - color: "transparent" - radius: 24 - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 - z: 2 - } - - Avatar {} - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: profileImageInput - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.profileImage !== undefined ? Settings.settings.profileImage : "" - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.profileImage = text; - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: profileImageInput.forceActiveFocus() - } - } - } - } - } - - // Show Active Window Icon Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Active Window Icon" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: activeWindowIconSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: activeWindowIconThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; - } - } - } - } - - // Show System Info In Bar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show System Info In Bar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: systemInfoSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: systemInfoThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; - } - } - } - } - - // Show Corners Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Corners" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: cornersSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: cornersThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showCorners = !Settings.settings.showCorners; - } - } - } - } - - // Show Taskbar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Taskbar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: taskbarSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: taskbarThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showTaskbar = !Settings.settings.showTaskbar; - } - } - } - } - - // Show Dock Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Dock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: dockSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: dockThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showDock ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showDock = !Settings.settings.showDock; - } - } - } - } - - // Show Media In Bar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Media In Bar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: mediaSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: mediaThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; - } - } - } - } - - // Dim Windows Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Dim Desktop" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: dimSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: dimThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.dimPanels = !Settings.settings.dimPanels; - } - } - } - } - - // Visualizer Type Selection - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 16 - - Text { - text: "Visualizer Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: visualizerTypeComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["radial", "fire", "diamond"] - currentIndex: model.indexOf(Settings.settings.visualizerType) - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing - text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1) - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: visualizerTypeComboBox.width - width - 12 - y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: visualizerTypeComboBox.height - width: visualizerTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null - currentIndex: visualizerTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: visualizerTypeComboBox.width - contentItem: Text { - text: modelData.charAt(0).toUpperCase() + modelData.slice(1) - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: visualizerTypeComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.visualizerType = model[index]; - } - } - } - - // Video Path Input Section - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 16 - - Text { - text: "Video Path" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: videoPathInput - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.videoPath = text; - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: videoPathInput.forceActiveFocus() - } - } - } - } - } -} diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/Sidebar/Config/SettingsModal.qml index 056f6f5..98f88c0 100644 --- a/Widgets/Sidebar/Config/SettingsModal.qml +++ b/Widgets/Sidebar/Config/SettingsModal.qml @@ -5,6 +5,7 @@ import Quickshell import Quickshell.Wayland import qs.Settings import qs.Services +import qs.Widgets.SettingsWindow import qs.Components PanelWindow { @@ -19,164 +20,55 @@ PanelWindow { margins.top: 0 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - Rectangle { - anchors.fill: parent - color: Theme.backgroundPrimary - radius: 20 - z: 0 - - ColumnLayout { - id: content - anchors.fill: parent - anchors.leftMargin: 32 - anchors.rightMargin: 32 - anchors.topMargin: 32 - spacing: 24 - - // Header - ColumnLayout { - id: header - Layout.fillWidth: true - spacing: 4 - RowLayout { - Layout.fillWidth: true - spacing: 20 - Text { - text: "settings" - font.family: "Material Symbols Outlined" - font.pixelSize: 32 - color: Theme.accentPrimary - } - Text { - text: "Settings" - font.pixelSize: 26 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - Rectangle { - width: 36 - height: 36 - radius: 18 - color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - Text { - anchors.centerIn: parent - text: "close" - font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 20 - color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - } - MouseArea { - id: closeButtonArea - anchors.fill: parent - hoverEnabled: true - onClicked: settingsModal.closeSettings() - } - } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.outline - opacity: 0.12 - } - } - - // Tabs bar (reordered) - Tabs { - id: settingsTabs - Layout.fillWidth: true - tabsModel: [ - { icon: "settings", label: "System" }, - { icon: "wallpaper", label: "Wallpaper" }, - { icon: "cloud", label: "Weather" } - ] - } - - // Scrollable settings area - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: content.height - settingsTabs.height - header.height - 128 - color: "transparent" - border.width: 0 - radius: 20 - - Flickable { - id: flick - anchors.fill: parent - contentWidth: width - contentHeight: tabContentLoader.item ? tabContentLoader.item.implicitHeight : 0 - clip: true - - Loader { - id: tabContentLoader - anchors.top: parent.top - width: parent.width - sourceComponent: settingsTabs.currentIndex === 0 ? systemTab : settingsTabs.currentIndex === 1 ? wallpaperTab : weatherTab - } - } - - Component { - id: systemTab - ColumnLayout { - anchors.fill: parent - ProfileSettings { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } - - Component { - id: wallpaperTab - ColumnLayout { - anchors.fill: parent - WallpaperSettings { - id: wallpaperSettings - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } - - Component { - id: weatherTab - ColumnLayout { - anchors.fill: parent - WeatherSettings { - Layout.fillWidth: true - Layout.alignment: Qt.AlignTop - anchors.margins: 16 - } - } - } - } - } - } + // Property to track the settings window instance + property var settingsWindow: null // Function to open the modal and initialize temp values function openSettings() { - visible = true; - focusTimer.start(); + if (!settingsWindow) { + // Create new window + settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues + if (settingsWindow) { + settingsWindow.visible = true; + // Handle window closure + settingsWindow.visibleChanged.connect(function() { + if (settingsWindow && !settingsWindow.visible) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + }); + } + } else if (settingsWindow.visible) { + // Close and destroy window + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } } // Function to close the modal and release focus function closeSettings() { - visible = false; + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } } - Timer { - id: focusTimer - interval: 100 - repeat: false - onTriggered: { - if (visible) { - // Focus logic can go here if needed - } + Component { + id: settingsComponent + SettingsWindow {} + } + + // Clean up on destruction + Component.onDestruction: { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); } } diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml deleted file mode 100644 index 730a935..0000000 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ /dev/null @@ -1,722 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import qs.Settings - -Rectangle { - id: wallpaperSettingsCard - - Layout.fillWidth: true - Layout.preferredHeight: Settings.settings.useSWWW ? 720 : 360 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - - RowLayout { - Layout.fillWidth: true - spacing: 12 - - Text { - text: "image" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - - Text { - text: "Wallpaper Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Wallpaper Path" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: folderInput - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.wallpaperFolder - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.wallpaperFolder = text; - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: folderInput.forceActiveFocus() - } - - } - - } - - } - - - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Random Wallpaper" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: randomWallpaperSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; - } - } - - } - - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use Wallpaper Theme" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: wallpaperThemeSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wallpaperThemeThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; - } - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Wallpaper Interval (seconds)" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.wallpaperInterval - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: intervalSlider - - Layout.fillWidth: true - from: 10 - to: 900 - stepSize: 10 - value: Settings.settings.wallpaperInterval - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); - } - - background: Rectangle { - x: intervalSlider.leftPadding - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: intervalSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: intervalSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: swwwSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: swwwThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - Text { - text: "Resize Mode" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: resizeComboBox - - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["no", "crop", "fit", "stretch"] - currentIndex: model.indexOf(Settings.settings.wallpaperResize) - onActivated: { - Settings.settings.wallpaperResize = model[index]; - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: resizeComboBox.indicator.width + resizeComboBox.spacing - text: resizeComboBox.displayText - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: resizeComboBox.width - width - 12 - y: resizeComboBox.topPadding + (resizeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: resizeComboBox.height - width: resizeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null - currentIndex: resizeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - - } - - delegate: ItemDelegate { - width: resizeComboBox.width - highlighted: resizeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - Text { - text: "Transition Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: transitionTypeComboBox - - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] - currentIndex: model.indexOf(Settings.settings.transitionType) - onActivated: { - Settings.settings.transitionType = model[index]; - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: transitionTypeComboBox.indicator.width + transitionTypeComboBox.spacing - text: transitionTypeComboBox.displayText - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: transitionTypeComboBox.width - width - 12 - y: transitionTypeComboBox.topPadding + (transitionTypeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: transitionTypeComboBox.height - width: transitionTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null - currentIndex: transitionTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - - } - - delegate: ItemDelegate { - width: transitionTypeComboBox.width - highlighted: transitionTypeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Transition FPS" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.transitionFps - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: fpsSlider - - Layout.fillWidth: true - from: 30 - to: 500 - stepSize: 5 - value: Settings.settings.transitionFps - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionFps = Math.round(value); - } - - background: Rectangle { - x: fpsSlider.leftPadding - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: fpsSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: fpsSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Transition Duration (seconds)" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.transitionDuration.toFixed(3) - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: durationSlider - - Layout.fillWidth: true - from: 0.25 - to: 10 - stepSize: 0.05 - value: Settings.settings.transitionDuration - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionDuration = value; - } - - background: Rectangle { - x: durationSlider.leftPadding - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: durationSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: durationSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - } - -} diff --git a/Widgets/Sidebar/Config/WeatherSettings.qml b/Widgets/Sidebar/Config/WeatherSettings.qml deleted file mode 100644 index 511e79a..0000000 --- a/Widgets/Sidebar/Config/WeatherSettings.qml +++ /dev/null @@ -1,275 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Settings - -Rectangle { - id: weatherSettingsCard - Layout.fillWidth: true - Layout.preferredHeight: 320 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - - RowLayout { - Layout.fillWidth: true - spacing: 12 - - Text { - text: "wb_sunny" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - - Text { - text: "Weather Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "City" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: cityInput - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.weatherCity - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - focus: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhNone - - onTextChanged: { - Settings.settings.weatherCity = text; - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: { - cityInput.forceActiveFocus(); - } - } - } - } - } - - - RowLayout { - spacing: 12 - Layout.fillWidth: true - - Text { - text: "Temperature Unit" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: customSwitch - width: 52 - height: 32 - radius: 16 - color: Theme.accentPrimary - border.color: Theme.accentPrimary - border.width: 2 - - Rectangle { - id: thumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useFahrenheit ? customSwitch.width - width - 2 : 2 - - Text { - anchors.centerIn: parent - text: Settings.settings.useFahrenheit ? "\u00b0F" : "\u00b0C" - font.family: Theme.fontFamily - font.pixelSize: 12 - font.bold: true - color: Theme.textPrimary - } - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useFahrenheit = !Settings.settings.useFahrenheit; - } - } - } - - - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use 12 Hour Clock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: use12HourClockSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.use12HourClock = !Settings.settings.use12HourClock; - } - } - } - } - - - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "US Style Date" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: reverseDayMonthSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: reverseDayMonthThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; - } - } - } - } - } -} diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index 0ddc9c0..d970789 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -4,11 +4,22 @@ import Quickshell import Quickshell.Io import Quickshell.Wayland import qs.Settings -import qs.Widgets.Sidebar.Config +import qs.Widgets.SettingsWindow import qs.Components PanelWithOverlay { id: sidebarPopup + property var shell: null + + // Trigger initial weather loading when component is completed + Component.onCompleted: { + // Load initial weather data after a short delay to ensure all components are ready + Qt.callLater(function() { + if (weather && weather.fetchCityWeather) { + weather.fetchCityWeather(); + } + }); + } function showAt() { sidebarPopupRect.showAt(); @@ -56,17 +67,17 @@ PanelWithOverlay { } function hidePopup() { - if (sidebarPopupRect.settingsModal && sidebarPopupRect.settingsModal.visible) { - sidebarPopupRect.settingsModal.visible = false; + if (shell && shell.settingsWindow && shell.settingsWindow.visible) { + shell.settingsWindow.visible = false; } - if (wallpaperPanel && wallpaperPanel.visible) { - wallpaperPanel.visible = false; + if (wallpaperPanelLoader.active && wallpaperPanelLoader.item && wallpaperPanelLoader.item.visible) { + wallpaperPanelLoader.item.visible = false; } - if (sidebarPopupRect.wifiPanelModal && sidebarPopupRect.wifiPanelModal.visible) { - sidebarPopupRect.wifiPanelModal.visible = false; + if (wifiPanelLoader.active && wifiPanelLoader.item && wifiPanelLoader.item.visible) { + wifiPanelLoader.item.visible = false; } - if (sidebarPopupRect.bluetoothPanelModal && sidebarPopupRect.bluetoothPanelModal.visible) { - sidebarPopupRect.bluetoothPanelModal.visible = false; + if (bluetoothPanelLoader.active && bluetoothPanelLoader.item && bluetoothPanelLoader.item.visible) { + bluetoothPanelLoader.item.visible = false; } if (sidebarPopup.visible) { slideAnim.from = 0; @@ -124,11 +135,44 @@ PanelWithOverlay { } } - property alias settingsModal: settingsModal - property alias wifiPanelModal: wifiPanel.panel - property alias bluetoothPanelModal: bluetoothPanel.panel - SettingsModal { + // Access the shell's SettingsWindow instead of creating a new one + + // LazyLoader for WifiPanel + LazyLoader { + id: wifiPanelLoader + loading: false + component: WifiPanel {} + } + + // LazyLoader for BluetoothPanel + LazyLoader { + id: bluetoothPanelLoader + loading: false + component: BluetoothPanel {} + } + + // LazyLoader for WallpaperPanel + LazyLoader { + id: wallpaperPanelLoader + loading: false + component: WallpaperPanel { + Component.onCompleted: { + if (parent) { + anchors.top = parent.top; + anchors.right = parent.right; + } + } + } + } + + // SettingsIcon component + SettingsIcon { id: settingsModal + onWeatherRefreshRequested: { + if (weather && weather.fetchCityWeather) { + weather.fetchCityWeather(); + } + } } Item { @@ -226,7 +270,14 @@ PanelWithOverlay { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: wifiPanel.showAt() + onClicked: { + if (!wifiPanelLoader.active) { + wifiPanelLoader.loading = true; + } + if (wifiPanelLoader.item) { + wifiPanelLoader.item.showAt(); + } + } } StyledTooltip { @@ -261,7 +312,14 @@ PanelWithOverlay { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: bluetoothPanel.showAt() + onClicked: { + if (!bluetoothPanelLoader.active) { + bluetoothPanelLoader.loading = true; + } + if (bluetoothPanelLoader.item) { + bluetoothPanelLoader.item.showAt(); + } + } } StyledTooltip { @@ -274,16 +332,6 @@ PanelWithOverlay { } } - // Hidden panel components for modal functionality - WifiPanel { - id: wifiPanel - visible: false - } - BluetoothPanel { - id: bluetoothPanel - visible: false - } - Item { Layout.fillHeight: true } @@ -310,10 +358,18 @@ PanelWithOverlay { } onSettingsRequested: { - settingsModal.visible = true; + // Use the SettingsModal's openSettings function + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) { + settingsModal.openSettings(); + } } onWallpaperRequested: { - wallpaperPanel.visible = true; + if (!wallpaperPanelLoader.active) { + wallpaperPanelLoader.loading = true; + } + if (wallpaperPanelLoader.item) { + wallpaperPanelLoader.item.visible = true; + } } } } @@ -408,15 +464,5 @@ PanelWithOverlay { } } } - - WallpaperPanel { - id: wallpaperPanel - Component.onCompleted: { - if (parent) { - anchors.top = parent.top; - anchors.right = parent.right; - } - } - } } } diff --git a/Widgets/Sidebar/Panel/SettingsIcon.qml b/Widgets/Sidebar/Panel/SettingsIcon.qml new file mode 100644 index 0000000..02fdb1d --- /dev/null +++ b/Widgets/Sidebar/Panel/SettingsIcon.qml @@ -0,0 +1,80 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import qs.Settings +import qs.Services +import qs.Widgets.SettingsWindow +import qs.Components + +PanelWindow { + id: settingsModal + implicitWidth: 480 + implicitHeight: 780 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + + // Signal to request weather refresh + signal weatherRefreshRequested() + + // Property to track the settings window instance + property var settingsWindow: null + + // Function to open the modal and initialize temp values + function openSettings() { + if (!settingsWindow) { + // Create new window + settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues + if (settingsWindow) { + settingsWindow.visible = true; + // Handle window closure + settingsWindow.visibleChanged.connect(function() { + if (settingsWindow && !settingsWindow.visible) { + // Trigger weather refresh when settings close + weatherRefreshRequested(); + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + }); + } + } else if (settingsWindow.visible) { + // Close and destroy window + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } + } + + // Function to close the modal and release focus + function closeSettings() { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } + } + + Component { + id: settingsComponent + SettingsWindow {} + } + + // Clean up on destruction + Component.onDestruction: { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + } + +} diff --git a/Widgets/Sidebar/Panel/Weather.qml b/Widgets/Sidebar/Panel/Weather.qml index 664f3fa..2317e9c 100644 --- a/Widgets/Sidebar/Panel/Weather.qml +++ b/Widgets/Sidebar/Panel/Weather.qml @@ -15,13 +15,17 @@ Rectangle { property var weatherData: null property string errorString: "" property bool isVisible: false + property int lastFetchTime: 0 + property bool isLoading: false // Auto-refetch weather when city changes Connections { target: Settings.settings function onWeatherCityChanged() { if (isVisible && city !== "") { - fetchCityWeather() + // Force refresh when city changes + lastFetchTime = 0; + fetchCityWeather(); } } } @@ -33,20 +37,42 @@ Rectangle { } function fetchCityWeather() { + if (!city || city.trim() === "") { + errorString = "No city configured"; + return; + } + + // Check if we should fetch new data (avoid fetching too frequently) + var currentTime = Date.now(); + var timeSinceLastFetch = currentTime - lastFetchTime; + + // Only skip if we have recent data AND lastFetchTime is not 0 (initial state) + if (lastFetchTime > 0 && timeSinceLastFetch < 60000) { // 1 minute + return; // Skip if last fetch was less than 1 minute ago + } + + isLoading = true; + errorString = ""; + WeatherHelper.fetchCityWeather(city, function(result) { weatherData = result.weather; + lastFetchTime = currentTime; errorString = ""; + isLoading = false; }, function(err) { errorString = err; + isLoading = false; } ); } function startWeatherFetch() { isVisible = true - fetchCityWeather() + // Force refresh when panel opens, regardless of time check + lastFetchTime = 0; + fetchCityWeather(); } function stopWeatherFetch() { @@ -77,12 +103,21 @@ Rectangle { Text { id: weatherIcon - text: weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud" + text: isLoading ? "sync" : (weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud") font.family: "Material Symbols Outlined" font.pixelSize: 28 verticalAlignment: Text.AlignVCenter - color: Theme.accentPrimary + color: isLoading ? Theme.accentPrimary : Theme.accentPrimary Layout.alignment: Qt.AlignVCenter + + // Add rotation animation for loading state + RotationAnimation on rotation { + running: isLoading + from: 0 + to: 360 + duration: 1000 + loops: Animation.Infinite + } } ColumnLayout { diff --git a/shell.qml b/shell.qml index 844f8ab..b8b7cfa 100644 --- a/shell.qml +++ b/shell.qml @@ -9,6 +9,7 @@ import qs.Bar.Modules import qs.Widgets import qs.Widgets.LockScreen import qs.Widgets.Notification +import qs.Widgets.SettingsWindow import qs.Settings import qs.Helpers @@ -19,8 +20,17 @@ Scope { id: root property alias appLauncherPanel: appLauncherPanel - property var notificationHistoryWin: notificationHistoryWin + property var notificationHistoryWin: notificationHistoryLoader.active ? notificationHistoryLoader.item : null + property var settingsWindow: null property bool pendingReload: false + + // Function to load notification history + function loadNotificationHistory() { + if (!notificationHistoryLoader.active) { + notificationHistoryLoader.loading = true; + } + return notificationHistoryLoader; + } // Helper function to round value to nearest step function roundToStep(value, step) { @@ -50,7 +60,7 @@ Scope { Bar { id: bar shell: root - property var notificationHistoryWin: notificationHistoryWin + property var notificationHistoryWin: notificationHistoryLoader.active ? notificationHistoryLoader.item : null } Variants { @@ -62,6 +72,9 @@ Scope { } } + Background {} + Overview {} + Applauncher { id: appLauncherPanel visible: false @@ -94,8 +107,8 @@ Scope { } } } - if (notificationHistoryWin) { - notificationHistoryWin.addToHistory({ + if (notificationHistoryLoader.active && notificationHistoryLoader.item) { + notificationHistoryLoader.item.addToHistory({ id: notification.id, appName: notification.appName || "Notification", summary: notification.summary || "", @@ -111,8 +124,33 @@ Scope { id: notificationPopup } - NotificationHistory { - id: notificationHistoryWin + // LazyLoader for NotificationHistory - only load when needed + LazyLoader { + id: notificationHistoryLoader + loading: false + component: NotificationHistory {} + } + + // Centralized LazyLoader for SettingsWindow - prevents crashes on multiple opens + LazyLoader { + id: settingsWindowLoader + loading: false + component: SettingsWindow { + Component.onCompleted: { + root.settingsWindow = this; + } + } + } + + // Function to safely show/hide settings window + function toggleSettingsWindow() { + if (!settingsWindowLoader.active) { + settingsWindowLoader.loading = true; + } + + if (settingsWindowLoader.item) { + settingsWindowLoader.item.visible = !settingsWindowLoader.item.visible; + } } // Reference to the default audio sink from Pipewire From 691974c1ba1fe29671fb18e41ecd596a88511e82 Mon Sep 17 00:00:00 2001 From: Quadbyte Date: Wed, 6 Aug 2025 10:14:11 -0400 Subject: [PATCH 05/43] test commit --- Bar/Bar.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 730c61b..06f5093 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -148,8 +148,6 @@ Scope { sidebarPopup: sidebarPopup } } - - } PanelWindow { From a68907d32450794d4068ae7dd07fe97296158d35 Mon Sep 17 00:00:00 2001 From: Quadbyte Date: Wed, 6 Aug 2025 11:59:49 -0400 Subject: [PATCH 06/43] Corners: wrapped them all in loaders to save a lot of memory when not enabled. --- Bar/Bar.qml | 245 +++++++++++++++------------ Bar/Modules/ActiveWindow.qml | 71 ++++---- Bar/Modules/Applauncher.qml | 42 +++-- Settings/Settings.qml | 2 +- Widgets/LockScreen/LockScreen.qml | 47 ++--- Widgets/Sidebar/Panel/PanelPopup.qml | 63 +++---- shell.qml | 6 +- 7 files changed, 260 insertions(+), 216 deletions(-) diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 06f5093..84cbbe2 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -1,27 +1,30 @@ import QtQuick import QtQuick.Controls +import QtQuick.Effects import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Wayland -import QtQuick.Effects import qs.Bar.Modules -import qs.Settings -import qs.Services import qs.Components import qs.Helpers +import qs.Services +import qs.Settings import qs.Widgets +import qs.Widgets.Notification import qs.Widgets.Sidebar import qs.Widgets.Sidebar.Panel -import qs.Widgets.Notification // Main bar component - creates panels on selected monitors with widgets and corners Scope { id: rootScope + property var shell + property alias visible: barRootItem.visible Item { id: barRootItem + anchors.fill: parent Variants { @@ -32,18 +35,18 @@ Scope { PanelWindow { id: panel + screen: modelData color: "transparent" implicitHeight: barBackground.height anchors.top: true anchors.left: true anchors.right: true - - visible: Settings.settings.barMonitors.includes(modelData.name) || - (Settings.settings.barMonitors.length === 0) + visible: Settings.settings.barMonitors.includes(modelData.name) || (Settings.settings.barMonitors.length === 0) Rectangle { id: barBackground + width: parent.width height: 36 color: Theme.backgroundPrimary @@ -53,6 +56,7 @@ Scope { Row { id: leftWidgetsRow + anchors.verticalCenter: barBackground.verticalCenter anchors.left: barBackground.left anchors.leftMargin: 18 @@ -69,6 +73,7 @@ Scope { Taskbar { anchors.verticalCenter: parent.verticalCenter } + } ActiveWindow { @@ -77,6 +82,7 @@ Scope { Workspace { id: workspace + screen: modelData anchors.horizontalCenter: barBackground.horizontalCenter anchors.verticalCenter: barBackground.verticalCenter @@ -84,6 +90,7 @@ Scope { Row { id: rightWidgetsRow + anchors.verticalCenter: barBackground.verticalCenter anchors.right: barBackground.right anchors.rightMargin: 18 @@ -91,6 +98,7 @@ Scope { SystemTray { id: systemTrayModule + shell: rootScope.shell anchors.verticalCenter: parent.verticalCenter bar: panel @@ -116,17 +124,20 @@ Scope { Battery { id: widgetsBattery + anchors.verticalCenter: parent.verticalCenter } Brightness { id: widgetsBrightness + screen: modelData anchors.verticalCenter: parent.verticalCenter } Volume { id: widgetsVolume + shell: rootScope.shell anchors.verticalCenter: parent.verticalCenter } @@ -138,6 +149,7 @@ Scope { PanelPopup { id: sidebarPopup + shell: rootScope.shell } @@ -147,122 +159,129 @@ Scope { screen: modelData sidebarPopup: sidebarPopup } + } + } - PanelWindow { - id: topLeftPanel - anchors.top: true - anchors.left: true + Loader { + active: Settings.settings.showCorners && (Settings.settings.barMonitors.includes(modelData.name) || (Settings.settings.barMonitors.length === 0)) - color: "transparent" - screen: modelData - margins.top: 36 - WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: Settings.settings.barMonitors.includes(modelData.name) || - (Settings.settings.barMonitors.length === 0) - WlrLayershell.layer: WlrLayer.Background - aboveWindows: false - WlrLayershell.namespace: "swww-daemon" - implicitHeight: 24 + sourceComponent: Item { + PanelWindow { + id: topLeftPanel + + anchors.top: true + anchors.left: true + color: "transparent" + screen: modelData + margins.top: 36 + WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "swww-daemon" + aboveWindows: false + implicitHeight: 24 + + Corners { + id: topLeftCorner + + position: "bottomleft" + size: 1.3 + fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" + offsetX: -39 + 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 + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "swww-daemon" + aboveWindows: false + implicitHeight: 24 + + Corners { + id: topRightCorner + + position: "bottomright" + size: 1.3 + fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" + offsetX: 39 + offsetY: 0 + anchors.top: parent.top + } + + } + + PanelWindow { + id: bottomLeftPanel + + anchors.bottom: true + anchors.left: true + color: "transparent" + screen: modelData + WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "swww-daemon" + aboveWindows: false + implicitHeight: 24 + + Corners { + id: bottomLeftCorner + + position: "topleft" + size: 1.3 + fillColor: Theme.backgroundPrimary + offsetX: -39 + offsetY: 0 + anchors.top: parent.top + } + + } + + PanelWindow { + id: bottomRightPanel + + anchors.bottom: true + anchors.right: true + color: "transparent" + screen: modelData + WlrLayershell.exclusionMode: ExclusionMode.Ignore + WlrLayershell.layer: WlrLayer.Top + WlrLayershell.namespace: "swww-daemon" + aboveWindows: false + implicitHeight: 24 + + Corners { + id: bottomRightCorner + + position: "topright" + size: 1.3 + fillColor: Theme.backgroundPrimary + offsetX: 39 + offsetY: 0 + anchors.top: parent.top + } + + } - Corners { - id: topLeftCorner - position: "bottomleft" - size: 1.3 - fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" - offsetX: -39 - offsetY: 0 - anchors.top: parent.top - visible: Settings.settings.showCorners } + } - PanelWindow { - id: topRightPanel - anchors.top: true - anchors.right: true - color: "transparent" - screen: modelData - margins.top: 36 - WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: Settings.settings.barMonitors.includes(modelData.name) || - (Settings.settings.barMonitors.length === 0) - WlrLayershell.layer: WlrLayer.Background - aboveWindows: false - WlrLayershell.namespace: "swww-daemon" - - implicitHeight: 24 - - Corners { - id: topRightCorner - position: "bottomright" - size: 1.3 - fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" - offsetX: 39 - offsetY: 0 - anchors.top: parent.top - visible: Settings.settings.showCorners - } - } - - PanelWindow { - id: bottomLeftPanel - anchors.bottom: true - anchors.left: true - color: "transparent" - screen: modelData - WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: Settings.settings.barMonitors.includes(modelData.name) || - (Settings.settings.barMonitors.length === 0) - WlrLayershell.layer: WlrLayer.Background - aboveWindows: false - WlrLayershell.namespace: "swww-daemon" - - implicitHeight: 24 - - Corners { - id: bottomLeftCorner - position: "topleft" - size: 1.3 - fillColor: Theme.backgroundPrimary - offsetX: -39 - offsetY: 0 - anchors.top: parent.top - visible: Settings.settings.showCorners - } - } - - PanelWindow { - id: bottomRightPanel - anchors.bottom: true - anchors.right: true - color: "transparent" - screen: modelData - WlrLayershell.exclusionMode: ExclusionMode.Ignore - visible: Settings.settings.barMonitors.includes(modelData.name) || - (Settings.settings.barMonitors.length === 0) - WlrLayershell.layer: WlrLayer.Background - aboveWindows: false - WlrLayershell.namespace: "swww-daemon" - - implicitHeight: 24 - - Corners { - id: bottomRightCorner - position: "topright" - size: 1.3 - fillColor: Theme.backgroundPrimary - offsetX: 39 - offsetY: 0 - anchors.top: parent.top - visible: Settings.settings.showCorners - } - } } + } + } - - property alias visible: barRootItem.visible } diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index cd6ab68..f4fea6a 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -20,20 +20,20 @@ PanelWindow { property int barHeight: 36 color: "transparent" - function getIcon() { - var icon = Quickshell.iconPath(ToplevelManager.activeToplevel.appId.toLowerCase(), true); - if (!icon) { - icon = Quickshell.iconPath(ToplevelManager.activeToplevel.appId, true); - } - if (!icon) { - icon = Quickshell.iconPath(ToplevelManager.activeToplevel.title, true); - } - if (!icon) { - icon = Quickshell.iconPath(ToplevelManager.activeToplevel.title.toLowerCase(), "application-x-executable"); - } - - return icon; + function getIcon() { + var icon = Quickshell.iconPath(ToplevelManager.activeToplevel.appId.toLowerCase(), true); + if (!icon) { + icon = Quickshell.iconPath(ToplevelManager.activeToplevel.appId, true); } + if (!icon) { + icon = Quickshell.iconPath(ToplevelManager.activeToplevel.title, true); + } + if (!icon) { + icon = Quickshell.iconPath(ToplevelManager.activeToplevel.title.toLowerCase(), "application-x-executable"); + } + + return icon; + } Item { id: activeWindowWrapper @@ -139,26 +139,33 @@ PanelWindow { verticalAlignment: Text.AlignVCenter maximumLineCount: 1 } + + Loader { + active: true + anchors.top: parent.top + sourceComponent: Item { + Corners { + id: activeCornerRight + position: "bottomleft" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: parent.top + offsetX: activeWindowTitleContainer.width - 34 + offsetY: -1 + } + + Corners { + id: activeCornerLeft + position: "bottomright" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: parent.top + offsetX: 34 + offsetY: -1 + } + } + } } - Corners { - id: activeCornerRight - position: "bottomleft" - size: 1.1 - fillColor: Theme.backgroundPrimary - offsetX: activeWindowTitleContainer.x + activeWindowTitleContainer.width - 34 - offsetY: -1 - anchors.top: activeWindowTitleContainer.top - } - - Corners { - id: activeCornerLeft - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: activeWindowTitleContainer.top - x: activeWindowTitleContainer.x + 34 - width - offsetY: -1 - } } } diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index f98509c..8bbcc88 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -826,24 +826,32 @@ PanelWithOverlay { } } - Corners { - id: launcherCornerRight - position: "bottomleft" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: root.top - offsetX: 416 - offsetY: 0 - } + Loader { + active: Settings.settings.showCorners + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + sourceComponent: Item { + Corners { + id: launcherCornerRight + position: "bottomleft" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: parent.top + offsetX: 427 + offsetY: 0 + } - Corners { - id: launcherCornerLeft - position: "bottomright" - 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: parent.top + offsetX: -427 + offsetY: 0 + } + } } } } diff --git a/Settings/Settings.qml b/Settings/Settings.qml index 0a09b94..0d066bf 100644 --- a/Settings/Settings.qml +++ b/Settings/Settings.qml @@ -48,7 +48,7 @@ Singleton { property bool showActiveWindow: true property bool showActiveWindowIcon: false property bool showSystemInfoInBar: false - property bool showCorners: true + property bool showCorners: false property bool showTaskbar: true property bool showMediaInBar: false property bool useSWWW: false diff --git a/Widgets/LockScreen/LockScreen.qml b/Widgets/LockScreen/LockScreen.qml index 2eaf479..2ed50b0 100644 --- a/Widgets/LockScreen/LockScreen.qml +++ b/Widgets/LockScreen/LockScreen.qml @@ -294,28 +294,33 @@ WlSessionLock { } } - Corners { - id: topRightCorner - position: "bottomleft" - size: 1.3 - fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" - offsetX: screen.width / 2 + 38 - offsetY: 0 - anchors.top: parent.top - visible: Settings.settings.showCorners - z: 50 - } + Loader { + active: Settings.settings.showCorners + anchors.fill: parent - Corners { - id: topLeftCorner - position: "bottomright" - size: 1.3 - fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" - offsetX: -Screen.width / 2 - 38 - offsetY: 0 - anchors.top: parent.top - visible: Settings.settings.showCorners - z: 51 + sourceComponent: Item { + Corners { + id: topRightCorner + position: "bottomleft" + size: 1.3 + fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" + offsetX: screen.width / 2 + 53 + offsetY: 0 + anchors.top: parent.top + z: 50 + } + + Corners { + id: topLeftCorner + position: "bottomright" + size: 1.3 + fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" + offsetX: -Screen.width / 2 - 53 + offsetY: 0 + anchors.top: parent.top + z: 51 + } + } } Rectangle { diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index d970789..7d1f5e8 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -428,39 +428,44 @@ PanelWithOverlay { } } - Corners { - id: sidebarCornerLeft - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: mainRectangle.top - offsetX: -447 + sidebarPopupRect.slideOffset - offsetY: 0 - visible: Settings.settings.showCorners + Loader { + active: Settings.settings.showCorners + anchors.fill: parent + sourceComponent: Item { + Corners { + id: sidebarCornerLeft + position: "bottomright" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.top: parent.top + offsetX: -447 + sidebarPopupRect.slideOffset + offsetY: 0 - Behavior on offsetX { - enabled: !sidebarPopupRect.isAnimating - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic + Behavior on offsetX { + enabled: !sidebarPopupRect.isAnimating + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } + } } - } - } - Corners { - id: sidebarCornerBottom - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - offsetX: 33 + sidebarPopupRect.slideOffset - offsetY: 46 - visible: Settings.settings.showCorners + Corners { + id: sidebarCornerBottom + position: "bottomright" + size: 1.1 + fillColor: Theme.backgroundPrimary + anchors.bottom: sidebarPopupRect.bottom + offsetX: 33 + sidebarPopupRect.slideOffset + offsetY: 46 - Behavior on offsetX { - enabled: !sidebarPopupRect.isAnimating - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic + Behavior on offsetX { + enabled: !sidebarPopupRect.isAnimating + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } + } } } } diff --git a/shell.qml b/shell.qml index b8b7cfa..fd0ff8c 100644 --- a/shell.qml +++ b/shell.qml @@ -57,6 +57,9 @@ Scope { Quickshell.shell = root; } + Background {} + Overview {} + Bar { id: bar shell: root @@ -72,9 +75,6 @@ Scope { } } - Background {} - Overview {} - Applauncher { id: appLauncherPanel visible: false From d738f85057b2b4963463628651ac3cd86a2da1cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Atoch?= Date: Wed, 6 Aug 2025 12:25:10 -0400 Subject: [PATCH 07/43] ActiveWindow: conditional corners --- Bar/Modules/ActiveWindow.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index f4fea6a..e3d9023 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -141,7 +141,7 @@ PanelWindow { } Loader { - active: true + active: Settings.settings.showCorners anchors.top: parent.top sourceComponent: Item { Corners { From 0bfef118dc68e52048f482d896e4c7e0128290af Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Wed, 6 Aug 2025 23:14:05 +0200 Subject: [PATCH 08/43] Yes --- Bar/Modules/SettingsButton.qml | 71 ++ Programs/zigbrightness | Bin 0 -> 47296 bytes Programs/zigstat | Bin 0 -> 36792 bytes Templates/templates/kitty.conf | 29 + Templates/templates/niri.kdl | 292 +++++++ .../Sidebar/Config/CollapsibleCategory.qml | 56 ++ Widgets/Sidebar/Config/ProfileSettings.qml | 643 ++++++++++++++++ Widgets/Sidebar/Config/WallpaperSettings.qml | 722 ++++++++++++++++++ Widgets/Sidebar/Config/WeatherSettings.qml | 275 +++++++ 9 files changed, 2088 insertions(+) create mode 100644 Bar/Modules/SettingsButton.qml create mode 100755 Programs/zigbrightness create mode 100755 Programs/zigstat create mode 100644 Templates/templates/kitty.conf create mode 100644 Templates/templates/niri.kdl create mode 100644 Widgets/Sidebar/Config/CollapsibleCategory.qml create mode 100644 Widgets/Sidebar/Config/ProfileSettings.qml create mode 100644 Widgets/Sidebar/Config/WallpaperSettings.qml create mode 100644 Widgets/Sidebar/Config/WeatherSettings.qml diff --git a/Bar/Modules/SettingsButton.qml b/Bar/Modules/SettingsButton.qml new file mode 100644 index 0000000..28aafab --- /dev/null +++ b/Bar/Modules/SettingsButton.qml @@ -0,0 +1,71 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs.Settings +import qs.Components +import qs.Widgets.SettingsWindow + +Item { + id: root + width: 22 + height: 22 + + Rectangle { + id: button + anchors.fill: parent + color: "transparent" + radius: width / 2 + + Text { + anchors.centerIn: parent + text: "settings" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 + color: mouseArea.containsMouse ? Theme.accentPrimary : Theme.textPrimary + } + + MouseArea { + id: mouseArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + + onClicked: { + if (!settingsWindowLoader.active) { + // Start loading the settings window + settingsWindowLoader.loading = true; + } + + if (settingsWindowLoader.item) { + // Toggle visibility + if (settingsWindowLoader.item.visible) { + settingsWindowLoader.item.visible = false; + } else { + settingsWindowLoader.item.visible = true; + } + } + } + } + + StyledTooltip { + text: "Settings" + targetItem: mouseArea + tooltipVisible: mouseArea.containsMouse + } + } + + // LazyLoader for SettingsWindow + LazyLoader { + id: settingsWindowLoader + loading: false + component: SettingsWindow { + // Handle window closure - just hide it, don't destroy + onVisibleChanged: { + if (!visible) { + // Window is hidden, but keep it loaded for reuse + } + } + } + } +} \ No newline at end of file diff --git a/Programs/zigbrightness b/Programs/zigbrightness new file mode 100755 index 0000000000000000000000000000000000000000..941653a12af279e6425b94dc53c80fcd288aba9d GIT binary patch literal 47296 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

E+cwXnUi{p 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 literal 0 HcmV?d00001 diff --git a/Templates/templates/kitty.conf b/Templates/templates/kitty.conf new file mode 100644 index 0000000..040feda --- /dev/null +++ b/Templates/templates/kitty.conf @@ -0,0 +1,29 @@ +# The kitty terminal template for wallust +# Add to wallust config: kitty = { src='kitty.conf', dst='~/.config/kitty/colors.conf'} +# And add to kitty config: include colors.conf + +cursor {{ cursor }} + +background {{ background }} +foreground {{ foreground }} + +color0 {{ color0 }} +color1 {{ color1 }} +color2 {{ color2 }} +color3 {{ color3 }} +color4 {{ color4 }} +color5 {{ color5 }} +color6 {{ color6 }} +color7 {{ color7 }} +color8 {{ color8 }} +color9 {{ color9 }} +color10 {{ color10 }} +color11 {{ color11 }} +color12 {{ color12 }} +color13 {{ color13 }} +color14 {{ color14 }} +color15 {{ color15 }} + +mark1_foreground {{ color6 | saturate(0.2) }} +mark2_foreground {{ color7 | saturate(0.2) }} +mark3_foreground {{ color6 | saturate(0.2) }} diff --git a/Templates/templates/niri.kdl b/Templates/templates/niri.kdl new file mode 100644 index 0000000..cd99b52 --- /dev/null +++ b/Templates/templates/niri.kdl @@ -0,0 +1,292 @@ +// Niri configuration for CachyOS +// For documentation and full reference, see: https://github.com/YaLTeR/niri/wiki + +// ────────────── Input Configuration ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Input + +input { + keyboard { + xkb { + layout "de" // Use the German keyboard layout + } + numlock // Enable numlock on startup + } + + touchpad { + tap // Enable tap-to-click + natural-scroll // Enable natural (macOS-style) scrolling + } + + focus-follows-mouse // Automatically focus windows under the mouse pointer + workspace-auto-back-and-forth // Enable workspace back & forth switching +} + +// ────────────── Output Configuration ────────────── +// You can run `niri msg outputs` to get the correct name for your displays. +// You will have to remove "/-" and edit it before it takes effect. +// https://github.com/YaLTeR/niri/wiki/Configuration:-Outputs + + output "DP-1" { + mode "2560x1440@359.979" // Set resolution and refresh rate + scale 1 // No scaling (use 2 for HiDPI) +} + +// ────────────── Keybindings ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Key-Bindings + +binds { + MOD+SHIFT+ESCAPE { show-hotkey-overlay; } + + // ─── Applications ─── + MOD+RETURN hotkey-overlay-title="Open Terminal: Kitty" { spawn "kitty"; } + MOD+CTRL+RETURN hotkey-overlay-title="Open App Launcher: QS" { spawn "qs" "ipc" "call" "globalIPC" "toggleLauncher"; } + MOD+B hotkey-overlay-title="Open Browser: firefox" { spawn "firefox"; } + MOD+ALT+L hotkey-overlay-title="Lock Screen: swaylock" { spawn "swaylock"; } + + // Please choose your own file manager + MOD+E hotkey-overlay-title="File Manager: Nautilus" { spawn "nautilus"; } + + // ─── Audio Controls ─── + XF86AUDIORAISEVOLUME allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1+"; } + XF86AUDIOLOWERVOLUME allow-when-locked=true { spawn "wpctl" "set-volume" "@DEFAULT_AUDIO_SINK@" "0.1-"; } + XF86AUDIOMUTE allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SINK@" "toggle"; } + XF86AUDIOMICMUTE allow-when-locked=true { spawn "wpctl" "set-mute" "@DEFAULT_AUDIO_SOURCE@" "toggle"; } + + // ─── Window Movement and Focus ─── + MOD+Q { close-window; } + + MOD+LEFT { focus-column-left; } + MOD+H { focus-column-left; } + MOD+RIGHT { focus-column-right; } + MOD+L { focus-column-right; } + MOD+UP { focus-window-up; } + MOD+K { focus-window-up; } + MOD+DOWN { focus-window-down; } + MOD+J { focus-window-down; } + + MOD+CTRL+LEFT { move-column-left; } + MOD+CTRL+H { move-column-left; } + MOD+CTRL+RIGHT { move-column-right; } + MOD+CTRL+L { move-column-right; } + MOD+CTRL+UP { move-window-up; } + MOD+CTRL+K { move-window-up; } + MOD+CTRL+DOWN { move-window-down; } + MOD+CTRL+J { move-window-down; } + + MOD+HOME { focus-column-first; } + MOD+END { focus-column-last; } + MOD+CTRL+HOME { move-column-to-first; } + MOD+CTRL+END { move-column-to-last; } + + MOD+SHIFT+LEFT { focus-monitor-left; } + MOD+SHIFT+RIGHT { focus-monitor-right; } + MOD+SHIFT+UP { focus-monitor-up; } + MOD+SHIFT+DOWN { focus-monitor-down; } + + MOD+SHIFT+CTRL+LEFT { move-column-to-monitor-left; } + MOD+SHIFT+CTRL+RIGHT { move-column-to-monitor-right; } + MOD+SHIFT+CTRL+UP { move-column-to-monitor-up; } + MOD+SHIFT+CTRL+DOWN { move-column-to-monitor-down; } + + // ─── Workspace Switching ─── + MOD+WHEELSCROLLDOWN cooldown-ms=150 { focus-workspace-down; } + MOD+WHEELSCROLLUP cooldown-ms=150 { focus-workspace-up; } + MOD+CTRL+WHEELSCROLLDOWN cooldown-ms=150 { move-column-to-workspace-down; } + MOD+CTRL+WHEELSCROLLUP cooldown-ms=150 { move-column-to-workspace-up; } + + MOD+WHEELSCROLLRIGHT { focus-column-right; } + MOD+WHEELSCROLLLEFT { focus-column-left; } + MOD+CTRL+WHEELSCROLLRIGHT { move-column-right; } + MOD+CTRL+WHEELSCROLLLEFT { move-column-left; } + + MOD+SHIFT+WHEELSCROLLDOWN { focus-column-right; } + MOD+SHIFT+WHEELSCROLLUP { focus-column-left; } + MOD+CTRL+SHIFT+WHEELSCROLLDOWN { move-column-right; } + MOD+CTRL+SHIFT+WHEELSCROLLUP { move-column-left; } + + MOD+1 { focus-workspace 1; } + MOD+2 { focus-workspace 2; } + MOD+3 { focus-workspace 3; } + MOD+4 { focus-workspace 4; } + MOD+5 { focus-workspace 5; } + MOD+6 { focus-workspace 6; } + MOD+7 { focus-workspace 7; } + MOD+8 { focus-workspace 8; } + MOD+9 { focus-workspace 9; } + + MOD+CTRL+1 { move-column-to-workspace 1; } + MOD+CTRL+2 { move-column-to-workspace 2; } + MOD+CTRL+3 { move-column-to-workspace 3; } + MOD+CTRL+4 { move-column-to-workspace 4; } + MOD+CTRL+5 { move-column-to-workspace 5; } + MOD+CTRL+6 { move-column-to-workspace 6; } + MOD+CTRL+7 { move-column-to-workspace 7; } + MOD+CTRL+8 { move-column-to-workspace 8; } + MOD+CTRL+9 { move-column-to-workspace 9; } + + MOD+TAB { focus-workspace-previous; } + + // ─── Layout Controls ─── + MOD+CTRL+F { expand-column-to-available-width; } + MOD+C { center-column; } + MOD+CTRL+C { center-visible-columns; } + MOD+MINUS { set-column-width "-10%"; } + MOD+EQUAL { set-column-width "+10%"; } + MOD+SHIFT+MINUS { set-window-height "-10%"; } + MOD+SHIFT+EQUAL { set-window-height "+10%"; } + + // ─── Modes ─── + MOD+T { toggle-window-floating; } + MOD+F { fullscreen-window; } + MOD+W { toggle-column-tabbed-display; } + + // ─── Screenshots ─── + CTRL+SHIFT+1 { screenshot; } + CTRL+SHIFT+2 { screenshot-screen; } + CTRL+SHIFT+3 { screenshot-window; } + + // ─── Emergency Escape Key ─── + // Use this when a fullscreen app blocks your keybinds. + // It disables any active keyboard shortcut inhibitor, restoring control. + MOD+ESCAPE allow-inhibiting=false { toggle-keyboard-shortcuts-inhibit; } + + // ─── Exit / Power ─── + CTRL+ALT+DELETE { quit; } // Also quits Niri + MOD+SHIFT+P { power-off-monitors; } // Turn off screens (useful for OLED or privacy) + MOD+O repeat=false { toggle-overview; } +} + +// ────────────── Startup Applications ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Miscellaneous#spawn-at-startup + + spawn-at-startup "/usr/lib/polkit-kde-authentication-agent-1" "&" // Polkit + spawn-at-startup "xwayland-satellite" // XWayland support + spawn-at-startup "swww-daemon" // Wallpaper daemon + spawn-at-startup "swww img" "/usr/share/wallpapers/cachyos-wallpapers/Skyscraper.png" // Set wallpaper + spawn-at-startup "qs" // Launch Quickshell + spawn-at-startup "vesktop" // Launch Vesktop + + prefer-no-csd // Disable program decorations + screenshot-path null // Disable screenshot saving + +// ────────────── Layout Settings ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Layout + + layout { + gaps 16 // Gap between windows + center-focused-column "never" // Don’t auto-center focused column + + preset-column-widths { + proportion 0.33333 + proportion 0.5 + proportion 0.66667 + } + + focus-ring { + width 3 + active-color "{{ color4 }}" + inactive-color "{{ color0 }}" + } + + shadow { + softness 30 + spread 5 + offset x=0 y=5 + color "#0007" + } + + background-color "transparent" + + struts {} + } + +// ────────────── Animation Settings ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Animations + animations { + workspace-switch { + spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001 + } + window-open { + duration-ms 200 + curve "ease-out-quad" + } + window-close { + duration-ms 200 + curve "ease-out-cubic" + } + horizontal-view-movement { + spring damping-ratio=1.0 stiffness=900 epsilon=0.0001 + } + window-movement { + spring damping-ratio=1.0 stiffness=800 epsilon=0.0001 + } + window-resize { + spring damping-ratio=1.0 stiffness=1000 epsilon=0.0001 + } + config-notification-open-close { + spring damping-ratio=0.6 stiffness=1200 epsilon=0.001 + } + screenshot-ui-open { + duration-ms 300 + curve "ease-out-quad" + } + overview-open-close { + spring damping-ratio=1.0 stiffness=900 epsilon=0.0001 + } + } + +// ────────────── Named Workspaces ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules + + workspace "browser" + workspace "chat" + +// ────────────── Window Rules ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Window-Rules + + window-rule { + match at-startup=true app-id="vesktop" + open-on-workspace "chat" + open-maximized true + } + + window-rule { + match app-id="firefox" + open-on-workspace "browser" + open-maximized true + } + + window-rule { + match app-id=r#"firefox$"# title="^Picture-in-Picture$" + open-floating true // Always float Firefox PiP windows + } + + window-rule { + geometry-corner-radius 20 // Set every window radius to 20 + clip-to-geometry true + } + +// ────────────── Layer Rules ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Layer-Rules + + layer-rule { + match namespace="^swww-daemon$" + place-within-backdrop true + } + +// ────────────── Environment Variables ────────────── +// https://github.com/YaLTeR/niri/wiki/Configuration:-Miscellaneous#environment + + environment { + DISPLAY ":1" + ELECTRON_OZONE_PLATFORM_HINT "auto" + QT_QPA_PLATFORM "wayland" + QT_WAYLAND_DISABLE_WINDOWDECORATION "1" + XDG_SESSION_TYPE "wayland" + XDG_CURRENT_DESKTOP "niri" + } + +// ────────────── Misc ────────────── +hotkey-overlay { + skip-at-startup +} \ No newline at end of file diff --git a/Widgets/Sidebar/Config/CollapsibleCategory.qml b/Widgets/Sidebar/Config/CollapsibleCategory.qml new file mode 100644 index 0000000..d0c2eb7 --- /dev/null +++ b/Widgets/Sidebar/Config/CollapsibleCategory.qml @@ -0,0 +1,56 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import qs.Settings + +ColumnLayout { + property alias title: headerText.text + property bool expanded: false // Hidden by default + default property alias content: contentItem.children + + Rectangle { + Layout.fillWidth: true + height: 44 + radius: 12 + color: Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 8 + Item { width: 2 } + Text { + id: headerText + font.family: Theme.fontFamily + font.pixelSize: Theme.fontSizeBody + font.bold: true + color: Theme.textPrimary + } + Item { Layout.fillWidth: true } + Rectangle { + width: 32; height: 32 + color: "transparent" + Text { + anchors.centerIn: parent + text: expanded ? "expand_less" : "expand_more" + font.family: "Material Symbols Outlined" + font.pixelSize: Theme.fontSizeBody + color: Theme.accentPrimary + } + } + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: expanded = !expanded + } + } + Item { height: 8 } + ColumnLayout { + id: contentItem + Layout.fillWidth: true + visible: expanded + spacing: 0 + } +} \ No newline at end of file diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml new file mode 100644 index 0000000..3451e25 --- /dev/null +++ b/Widgets/Sidebar/Config/ProfileSettings.qml @@ -0,0 +1,643 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Effects +import QtQuick.Controls +import Quickshell.Widgets +import qs.Components +import qs.Settings + +Rectangle { + id: profileSettingsCard + Layout.fillWidth: true + Layout.preferredHeight: 690 + color: Theme.surface + radius: 18 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 18 + spacing: 12 + + // Header + RowLayout { + Layout.fillWidth: true + spacing: 12 + Text { + text: "settings" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.accentPrimary + } + Text { + text: "Profile Settings" + font.family: Theme.fontFamily + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + } + + // Profile Image Input Section + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Profile Image" + font.family: Theme.fontFamily + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + // Profile image + Rectangle { + width: 48 + height: 48 + radius: 24 + + // Border + Rectangle { + anchors.fill: parent + color: "transparent" + radius: 24 + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 2 + z: 2 + } + + Avatar {} + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: profileImageInput + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.profileImage + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.profileImage = text; + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: profileImageInput.forceActiveFocus() + } + } + } + } + } + + // Show Active Window Icon Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Show Active Window Icon" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: activeWindowIconSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: activeWindowIconThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; + } + } + } + } + + // Show System Info In Bar Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Show System Info In Bar" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: systemInfoSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: systemInfoThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; + } + } + } + } + + // Show Corners Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Show Corners" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: cornersSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: cornersThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showCorners = !Settings.settings.showCorners; + } + } + } + } + + // Show Taskbar Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Show Taskbar" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: taskbarSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: taskbarThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showTaskbar = !Settings.settings.showTaskbar; + } + } + } + } + + // Show Dock Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Show Dock" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: dockSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: dockThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showDock ? taskbarSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showDock = !Settings.settings.showDock; + } + } + } + } + + // Show Media In Bar Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Show Media In Bar" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: mediaSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: mediaThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; + } + } + } + } + + // Dim Windows Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Dim Desktop" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Rectangle { + id: dimSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: dimThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.dimPanels = !Settings.settings.dimPanels; + } + } + } + } + + // Visualizer Type Selection + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 16 + + Text { + text: "Visualizer Type" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + ComboBox { + id: visualizerTypeComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["radial", "fire", "diamond"] + currentIndex: model.indexOf(Settings.settings.visualizerType) + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing + text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1) + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: visualizerTypeComboBox.width - width - 12 + y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: visualizerTypeComboBox.height + width: visualizerTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null + currentIndex: visualizerTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: visualizerTypeComboBox.width + contentItem: Text { + text: modelData.charAt(0).toUpperCase() + modelData.slice(1) + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: visualizerTypeComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.visualizerType = model[index]; + } + } + } + + // Video Path Input Section + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 16 + + Text { + text: "Video Path" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: videoPathInput + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.videoPath = text; + } + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: videoPathInput.forceActiveFocus() + } + } + } + } + } +} diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml new file mode 100644 index 0000000..2f5b66d --- /dev/null +++ b/Widgets/Sidebar/Config/WallpaperSettings.qml @@ -0,0 +1,722 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Settings + +Rectangle { + id: wallpaperSettingsCard + + Layout.fillWidth: true + Layout.preferredHeight: Settings.settings.useSWWW ? 720 : 360 + color: Theme.surface + radius: 18 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 18 + spacing: 12 + + // Header + RowLayout { + Layout.fillWidth: true + spacing: 12 + + Text { + text: "image" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.accentPrimary + } + + Text { + text: "Wallpaper Settings" + font.family: Theme.fontFamily + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + } + + // Wallpaper Path + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Wallpaper Path" + font.family: Theme.fontFamily + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + // Folder Path Input + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: folderInput + + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.wallpaperFolder + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.wallpaperFolder = text; + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: folderInput.forceActiveFocus() + } + + } + + } + + } + + + + // Random Wallpaper Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Random Wallpaper" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: randomWallpaperSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: randomWallpaperThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; + } + } + + } + + } + + // Use Wallpaper Theme Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Use Wallpaper Theme" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: wallpaperThemeSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wallpaperThemeThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; + } + } + + } + + } + + // Wallpaper Interval Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + Layout.fillWidth: true + + Text { + text: "Wallpaper Interval (seconds)" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Text { + text: Settings.settings.wallpaperInterval + font.pixelSize: 13 + color: Theme.textPrimary + } + + } + + Slider { + id: intervalSlider + + Layout.fillWidth: true + from: 10 + to: 900 + stepSize: 10 + value: Settings.settings.wallpaperInterval + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.wallpaperInterval = Math.round(value); + } + + background: Rectangle { + x: intervalSlider.leftPadding + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: intervalSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: intervalSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + + } + + handle: Rectangle { + x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + } + + } + + // Use SWWW Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Use SWWW" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: swwwSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: swwwThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useSWWW = !Settings.settings.useSWWW; + } + } + + } + + } + + // Resize Mode Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + visible: Settings.settings.useSWWW + + Text { + text: "Resize Mode" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + ComboBox { + id: resizeComboBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["no", "crop", "fit", "stretch"] + currentIndex: model.indexOf(Settings.settings.wallpaperResize) + onActivated: { + Settings.settings.wallpaperResize = model[index]; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: resizeComboBox.indicator.width + resizeComboBox.spacing + text: resizeComboBox.displayText + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: resizeComboBox.width - width - 12 + y: resizeComboBox.topPadding + (resizeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: resizeComboBox.height + width: resizeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null + currentIndex: resizeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + + } + + delegate: ItemDelegate { + width: resizeComboBox.width + highlighted: resizeComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + + } + + } + + } + + // Transition Type Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + visible: Settings.settings.useSWWW + + Text { + text: "Transition Type" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + ComboBox { + id: transitionTypeComboBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] + currentIndex: model.indexOf(Settings.settings.transitionType) + onActivated: { + Settings.settings.transitionType = model[index]; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: transitionTypeComboBox.indicator.width + transitionTypeComboBox.spacing + text: transitionTypeComboBox.displayText + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: transitionTypeComboBox.width - width - 12 + y: transitionTypeComboBox.topPadding + (transitionTypeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: transitionTypeComboBox.height + width: transitionTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null + currentIndex: transitionTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + + } + + delegate: ItemDelegate { + width: transitionTypeComboBox.width + highlighted: transitionTypeComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + + } + + } + + } + + // Transition FPS Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + visible: Settings.settings.useSWWW + + RowLayout { + Layout.fillWidth: true + + Text { + text: "Transition FPS" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Text { + text: Settings.settings.transitionFps + font.pixelSize: 13 + color: Theme.textPrimary + } + + } + + Slider { + id: fpsSlider + + Layout.fillWidth: true + from: 30 + to: 500 + stepSize: 5 + value: Settings.settings.transitionFps + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionFps = Math.round(value); + } + + background: Rectangle { + x: fpsSlider.leftPadding + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: fpsSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: fpsSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + + } + + handle: Rectangle { + x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + } + + } + + // Transition Duration Setting + ColumnLayout { + spacing: 12 + Layout.fillWidth: true + Layout.topMargin: 16 + visible: Settings.settings.useSWWW + + RowLayout { + Layout.fillWidth: true + + Text { + text: "Transition Duration (seconds)" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + Text { + text: Settings.settings.transitionDuration.toFixed(3) + font.pixelSize: 13 + color: Theme.textPrimary + } + + } + + Slider { + id: durationSlider + + Layout.fillWidth: true + from: 0.25 + to: 10 + stepSize: 0.05 + value: Settings.settings.transitionDuration + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionDuration = value; + } + + background: Rectangle { + x: durationSlider.leftPadding + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: durationSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: durationSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + + } + + handle: Rectangle { + x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + } + + } + + } + +} diff --git a/Widgets/Sidebar/Config/WeatherSettings.qml b/Widgets/Sidebar/Config/WeatherSettings.qml new file mode 100644 index 0000000..d7689dc --- /dev/null +++ b/Widgets/Sidebar/Config/WeatherSettings.qml @@ -0,0 +1,275 @@ +import QtQuick +import QtQuick.Layouts +import qs.Settings + +Rectangle { + id: weatherSettingsCard + Layout.fillWidth: true + Layout.preferredHeight: 320 + color: Theme.surface + radius: 18 + + ColumnLayout { + anchors.fill: parent + anchors.margins: 18 + spacing: 12 + + // Weather Settings Header + RowLayout { + Layout.fillWidth: true + spacing: 12 + + Text { + text: "wb_sunny" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.accentPrimary + } + + Text { + text: "Weather Settings" + font.family: Theme.fontFamily + font.pixelSize: 16 + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true + } + } + + // Weather City Setting + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "City" + font.family: Theme.fontFamily + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: cityInput + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.weatherCity + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + focus: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhNone + + onTextChanged: { + Settings.settings.weatherCity = text; + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: { + cityInput.forceActiveFocus(); + } + } + } + } + } + + // Temperature Unit Setting + RowLayout { + spacing: 12 + Layout.fillWidth: true + + Text { + text: "Temperature Unit" + font.family: Theme.fontFamily + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: customSwitch + width: 52 + height: 32 + radius: 16 + color: Theme.accentPrimary + border.color: Theme.accentPrimary + border.width: 2 + + Rectangle { + id: thumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useFahrenheit ? customSwitch.width - width - 2 : 2 + + Text { + anchors.centerIn: parent + text: Settings.settings.useFahrenheit ? "\u00b0F" : "\u00b0C" + font.family: Theme.fontFamily + font.pixelSize: 12 + font.bold: true + color: Theme.textPrimary + } + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useFahrenheit = !Settings.settings.useFahrenheit; + } + } + } + + + } + + // Random Wallpaper Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Use 12 Hour Clock" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: use12HourClockSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: randomWallpaperThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.use12HourClock = !Settings.settings.use12HourClock; + } + } + } + } + + // Reverse Day Month Setting + RowLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "US Style Date" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + // Custom Material 3 Switch + Rectangle { + id: reverseDayMonthSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: reverseDayMonthThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; + } + } + } + } + } +} From 6ef29ae74594c04e35bcc4bc34858e5fc7b6b8c4 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 00:34:33 +0200 Subject: [PATCH 09/43] Edit SettingsWindow --- Programs/zigbrightness | Bin 47296 -> 0 bytes Programs/zigstat | Bin 36792 -> 0 bytes Widgets/Notification/NotificationPopup.qml | 25 ++- Widgets/SettingsWindow/SettingsWindow.qml | 210 ++++++++++++-------- Widgets/SettingsWindow/Tabs/Bar.qml | 2 +- Widgets/SettingsWindow/Tabs/Display.qml | 2 +- Widgets/SettingsWindow/Tabs/General.qml | 4 +- Widgets/SettingsWindow/Tabs/Misc.qml | 2 +- Widgets/SettingsWindow/Tabs/Network.qml | 4 +- Widgets/SettingsWindow/Tabs/Recording.qml | 2 +- Widgets/SettingsWindow/Tabs/TimeWeather.qml | 4 +- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 18 +- Widgets/Sidebar/Panel/PanelPopup.qml | 31 +-- Widgets/Sidebar/Panel/QuickAccess.qml | 3 +- Widgets/Sidebar/Panel/SettingsIcon.qml | 15 +- Widgets/Sidebar/Panel/SettingsModal.qml | 81 ++++++++ shell.qml | 10 +- 17 files changed, 283 insertions(+), 130 deletions(-) delete mode 100755 Programs/zigbrightness delete mode 100755 Programs/zigstat create mode 100644 Widgets/Sidebar/Panel/SettingsModal.qml diff --git a/Programs/zigbrightness b/Programs/zigbrightness deleted file mode 100755 index 941653a12af279e6425b94dc53c80fcd288aba9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 47296 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

E+cwXnUi{p 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 diff --git a/Widgets/Notification/NotificationPopup.qml b/Widgets/Notification/NotificationPopup.qml index 556e7eb..e62bd24 100644 --- a/Widgets/Notification/NotificationPopup.qml +++ b/Widgets/Notification/NotificationPopup.qml @@ -12,6 +12,13 @@ Item { // Get list of available monitors/screens property var monitors: Quickshell.screens || [] + Component.onCompleted: { + console.log("[NotificationPopup] Initialized with", monitors.length, "monitors"); + for (let i = 0; i < monitors.length; i++) { + console.log("[NotificationPopup] Monitor", i, ":", monitors[i].name); + } + } + // Global visibility state for all notification popups property bool notificationsVisible: true @@ -20,6 +27,17 @@ Item { notificationsVisible = !notificationsVisible; console.log("[NotificationManager] New state: " + notificationsVisible); } + + function addNotification(notification): void { + console.log("[NotificationPopup] Adding notification to popup manager"); + // Add notification to all monitor popups + for (let i = 0; i < children.length; i++) { + let child = children[i]; + if (child.addNotification) { + child.addNotification(notification); + } + } + } // Create a notification popup for each monitor Repeater { @@ -50,8 +68,12 @@ Item { property bool shouldShowOnThisMonitor: { let notificationMonitors = Settings.settings.notificationMonitors || []; let currentScreenName = modelData ? modelData.name : ""; - return notificationMonitors.includes("*") || + // Show notifications on all monitors if notificationMonitors is empty or contains "*" + let shouldShow = notificationMonitors.length === 0 || + notificationMonitors.includes("*") || notificationMonitors.includes(currentScreenName); + console.log("[NotificationPopup] Monitor", currentScreenName, "should show:", shouldShow, "monitors:", JSON.stringify(notificationMonitors)); + return shouldShow; } // Watch for changes in notification monitors setting @@ -75,6 +97,7 @@ Item { property int spacing: 5 function addNotification(notification) { + console.log("[NotificationPopup] Adding notification to monitor popup:", notification.appName); notificationModel.insert(0, { id: notification.id, appName: notification.appName || "Notification", diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 016379c..3f9402b 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -7,17 +7,74 @@ import QtQuick.Effects import qs.Settings import qs.Widgets.SettingsWindow.Tabs import qs.Widgets.SettingsWindow.Tabs.Components +import qs.Components -PanelWindow { +PanelWithOverlay { id: panelMain - implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400 - implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600 - color: "transparent" - + property int activeTabIndex: 0 WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // Function to show wallpaper selector + function showWallpaperSelector() { + if (wallpaperSelector) { + wallpaperSelector.show(); + } + } + + // Function to show settings window + function showSettings() { + show(); + } + + + + + + // Handle activeTabIndex changes + onActiveTabIndexChanged: { + if (activeTabIndex >= 0 && activeTabIndex <= 8) { + loadComponentForTab(activeTabIndex); + } + } + + // Function to load component for a specific tab + function loadComponentForTab(tabIndex) { + const componentMap = { + 0: generalSettings, + 1: barSettings, + 2: timeWeatherSettings, + 3: recordingSettings, + 4: networkSettings, + 5: displaySettings, + 6: wallpaperSettings, + 7: miscSettings, + 8: aboutSettings + }; + + const tabNames = [ + "General", + "Bar", + "Time & Weather", + "Recording", + "Network", + "Display", + "Wallpaper", + "Misc", + "About" + ]; + + if (componentMap[tabIndex]) { + settingsLoader.sourceComponent = componentMap[tabIndex]; + if (tabName) { + tabName.text = tabNames[tabIndex]; + } + + + } + } + // Add safety checks for component loading Component.onCompleted: { // Ensure we start with a valid tab @@ -83,37 +140,47 @@ PanelWindow { } Rectangle { - id: background - color: Theme.backgroundPrimary - anchors.fill: parent - radius: 20 - border.color: Theme.outline - border.width: 1 + id: settingsWindowRect + implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600 + implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400 + visible: parent.visible + color: "transparent" + + // Center the settings window on screen + anchors.centerIn: parent - MultiEffect { - source: background - anchors.fill: background - shadowEnabled: true - shadowColor: Theme.shadow - shadowOpacity: 0.3 - shadowHorizontalOffset: 0 - shadowVerticalOffset: 2 - shadowBlur: 12 - } - } + Rectangle { + id: background + color: Theme.backgroundPrimary + anchors.fill: parent + radius: 20 + border.color: Theme.outline + border.width: 1 - Rectangle { - id: settings - color: Theme.backgroundPrimary - anchors { - left: tabs.right - top: parent.top - bottom: parent.bottom - right: parent.right - margins: 12 + MultiEffect { + source: background + anchors.fill: background + shadowEnabled: true + shadowColor: Theme.shadow + shadowOpacity: 0.3 + shadowHorizontalOffset: 0 + shadowVerticalOffset: 2 + shadowBlur: 12 + } } - topRightRadius: 20 - bottomRightRadius: 20 + + Rectangle { + id: settings + color: Theme.backgroundPrimary + anchors { + left: tabs.right + top: parent.top + bottom: parent.bottom + right: parent.right + margins: 12 + } + topRightRadius: 20 + bottomRightRadius: 20 Rectangle { id: headerArea @@ -149,7 +216,7 @@ PanelWindow { // Wallpaper Selection Button (only visible on Wallpaper tab) Rectangle { - width: 32 + width: 160 height: 32 radius: 16 color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" @@ -157,12 +224,25 @@ PanelWindow { border.width: 1 visible: activeTabIndex === 6 // Wallpaper tab index - Text { + Row { anchors.centerIn: parent - text: "image" - font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 18 - color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + spacing: 6 + + Text { + text: "image" + font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 16 + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: "Select Wallpaper" + font.pixelSize: 13 + font.bold: true + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + anchors.verticalCenter: parent.verticalCenter + } } MouseArea { @@ -198,7 +278,7 @@ PanelWindow { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: panelMain.visible = false + onClicked: panelMain.dismiss() } } } @@ -242,15 +322,15 @@ PanelWindow { } } - Rectangle { - id: tabs - color: Theme.surface - width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100 - height: panelMain.height - topLeftRadius: 20 - bottomLeftRadius: 20 - border.color: Theme.outline - border.width: 1 + Rectangle { + id: tabs + color: Theme.surface + width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100 + height: settingsWindowRect.height + topLeftRadius: 20 + bottomLeftRadius: 20 + border.color: Theme.outline + border.width: 1 Column { width: parent.width @@ -332,36 +412,7 @@ PanelWindow { cursorShape: Qt.PointingHandCursor onClicked: { activeTabIndex = index; - - const newComponent = { - 0: generalSettings, - 1: barSettings, - 2: timeWeatherSettings, - 3: recordingSettings, - 4: networkSettings, - 5: displaySettings, - 6: wallpaperSettings, - 7: miscSettings, - 8: aboutSettings - }[index]; - - const tabNames = [ - "General", - "Bar", - "Time & Weather", - "Recording", - "Network", - "Display", - "Wallpaper", - "Misc", - "About" - ]; - tabName.text = tabNames[index]; - - // Simple component switching - if (newComponent) { - settingsLoader.sourceComponent = newComponent; - } + loadComponentForTab(index); } } @@ -377,4 +428,5 @@ PanelWindow { } } } + } } \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Bar.qml b/Widgets/SettingsWindow/Tabs/Bar.qml index 53e1cc7..751a0ce 100644 --- a/Widgets/SettingsWindow/Tabs/Bar.qml +++ b/Widgets/SettingsWindow/Tabs/Bar.qml @@ -22,7 +22,7 @@ ColumnLayout { Text { text: "Bar Elements" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 diff --git a/Widgets/SettingsWindow/Tabs/Display.qml b/Widgets/SettingsWindow/Tabs/Display.qml index bf450a3..85353a8 100644 --- a/Widgets/SettingsWindow/Tabs/Display.qml +++ b/Widgets/SettingsWindow/Tabs/Display.qml @@ -37,7 +37,7 @@ ColumnLayout { Text { text: "Monitor Selection" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index 0f3d5b0..dab795a 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -22,7 +22,7 @@ ColumnLayout { Text { text: "Profile" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 @@ -115,7 +115,7 @@ ColumnLayout { Text { text: "User Interface" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 diff --git a/Widgets/SettingsWindow/Tabs/Misc.qml b/Widgets/SettingsWindow/Tabs/Misc.qml index 19d0e74..a2a4f16 100644 --- a/Widgets/SettingsWindow/Tabs/Misc.qml +++ b/Widgets/SettingsWindow/Tabs/Misc.qml @@ -22,7 +22,7 @@ ColumnLayout { Text { text: "Media" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index 0223f56..d5cdd06 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -22,7 +22,7 @@ ColumnLayout { Text { text: "Wi-Fi" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary } @@ -105,7 +105,7 @@ ColumnLayout { Text { text: "Bluetooth" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary } diff --git a/Widgets/SettingsWindow/Tabs/Recording.qml b/Widgets/SettingsWindow/Tabs/Recording.qml index 1d5677f..36234b4 100644 --- a/Widgets/SettingsWindow/Tabs/Recording.qml +++ b/Widgets/SettingsWindow/Tabs/Recording.qml @@ -36,7 +36,7 @@ ColumnLayout { Text { text: "Screen Recording" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml index 72aa261..e374dea 100644 --- a/Widgets/SettingsWindow/Tabs/TimeWeather.qml +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -23,7 +23,7 @@ ColumnLayout { Text { text: "Time" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 @@ -176,7 +176,7 @@ ColumnLayout { Text { text: "Weather" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index 950d4f4..0453b27 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -36,13 +36,13 @@ ColumnLayout { spacing: 4 Layout.fillWidth: true - Text { - text: "Wallpaper" - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } + Text { + text: "Wallpaper" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } @@ -131,7 +131,7 @@ ColumnLayout { Text { text: "Automation" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 @@ -364,7 +364,7 @@ ColumnLayout { Text { text: "SWWW" - font.pixelSize: 16 + font.pixelSize: 18 font.bold: true color: Theme.textPrimary Layout.bottomMargin: 8 diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/Sidebar/Panel/PanelPopup.qml index 7d1f5e8..cc6dbe9 100644 --- a/Widgets/Sidebar/Panel/PanelPopup.qml +++ b/Widgets/Sidebar/Panel/PanelPopup.qml @@ -70,9 +70,7 @@ PanelWithOverlay { if (shell && shell.settingsWindow && shell.settingsWindow.visible) { shell.settingsWindow.visible = false; } - if (wallpaperPanelLoader.active && wallpaperPanelLoader.item && wallpaperPanelLoader.item.visible) { - wallpaperPanelLoader.item.visible = false; - } + if (wifiPanelLoader.active && wifiPanelLoader.item && wifiPanelLoader.item.visible) { wifiPanelLoader.item.visible = false; } @@ -151,19 +149,7 @@ PanelWithOverlay { component: BluetoothPanel {} } - // LazyLoader for WallpaperPanel - LazyLoader { - id: wallpaperPanelLoader - loading: false - component: WallpaperPanel { - Component.onCompleted: { - if (parent) { - anchors.top = parent.top; - anchors.right = parent.right; - } - } - } - } + // SettingsIcon component SettingsIcon { @@ -175,6 +161,8 @@ PanelWithOverlay { } } + + Item { anchors.fill: mainRectangle x: sidebarPopupRect.slideOffset @@ -363,12 +351,11 @@ PanelWithOverlay { settingsModal.openSettings(); } } - onWallpaperRequested: { - if (!wallpaperPanelLoader.active) { - wallpaperPanelLoader.loading = true; - } - if (wallpaperPanelLoader.item) { - wallpaperPanelLoader.item.visible = true; + + onWallpaperSelectorRequested: { + // Use the SettingsModal's openSettings function with wallpaper tab (index 6) + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) { + settingsModal.openSettings(6); // 6 is the wallpaper tab index } } } diff --git a/Widgets/Sidebar/Panel/QuickAccess.qml b/Widgets/Sidebar/Panel/QuickAccess.qml index ac33aec..60b9a57 100644 --- a/Widgets/Sidebar/Panel/QuickAccess.qml +++ b/Widgets/Sidebar/Panel/QuickAccess.qml @@ -20,6 +20,7 @@ Rectangle { signal recordingStateMismatch(bool actualState) signal settingsRequested() signal wallpaperRequested() + signal wallpaperSelectorRequested() Rectangle { id: card @@ -161,7 +162,7 @@ Rectangle { cursorShape: Qt.PointingHandCursor hoverEnabled: true onClicked: { - wallpaperRequested() + wallpaperSelectorRequested() } } } diff --git a/Widgets/Sidebar/Panel/SettingsIcon.qml b/Widgets/Sidebar/Panel/SettingsIcon.qml index 02fdb1d..e2a383c 100644 --- a/Widgets/Sidebar/Panel/SettingsIcon.qml +++ b/Widgets/Sidebar/Panel/SettingsIcon.qml @@ -27,12 +27,25 @@ PanelWindow { property var settingsWindow: null // Function to open the modal and initialize temp values - function openSettings() { + function openSettings(initialTabIndex) { if (!settingsWindow) { // Create new window settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues if (settingsWindow) { + // Set the initial tab if provided + if (typeof initialTabIndex === 'number' && initialTabIndex >= 0 && initialTabIndex <= 8) { + settingsWindow.activeTabIndex = initialTabIndex; + } settingsWindow.visible = true; + + // Show wallpaper selector if opening wallpaper tab (after window is visible) + if (typeof initialTabIndex === 'number' && initialTabIndex === 6) { + Qt.callLater(function() { + if (settingsWindow && settingsWindow.showWallpaperSelector) { + settingsWindow.showWallpaperSelector(); + } + }, 100); // Small delay to ensure window is fully loaded + } // Handle window closure settingsWindow.visibleChanged.connect(function() { if (settingsWindow && !settingsWindow.visible) { diff --git a/Widgets/Sidebar/Panel/SettingsModal.qml b/Widgets/Sidebar/Panel/SettingsModal.qml new file mode 100644 index 0000000..98f88c0 --- /dev/null +++ b/Widgets/Sidebar/Panel/SettingsModal.qml @@ -0,0 +1,81 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import qs.Settings +import qs.Services +import qs.Widgets.SettingsWindow +import qs.Components + +PanelWindow { + id: settingsModal + implicitWidth: 480 + implicitHeight: 780 + visible: false + color: "transparent" + anchors.top: true + anchors.right: true + margins.right: 0 + margins.top: 0 + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + + // Property to track the settings window instance + property var settingsWindow: null + + // Function to open the modal and initialize temp values + function openSettings() { + if (!settingsWindow) { + // Create new window + settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues + if (settingsWindow) { + settingsWindow.visible = true; + // Handle window closure + settingsWindow.visibleChanged.connect(function() { + if (settingsWindow && !settingsWindow.visible) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + }); + } + } else if (settingsWindow.visible) { + // Close and destroy window + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } + } + + // Function to close the modal and release focus + function closeSettings() { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.visible = false; + windowToDestroy.destroy(); + } + } + + Component { + id: settingsComponent + SettingsWindow {} + } + + // Clean up on destruction + Component.onDestruction: { + if (settingsWindow) { + var windowToDestroy = settingsWindow; + settingsWindow = null; + windowToDestroy.destroy(); + } + } + + // Refresh weather data when hidden + onVisibleChanged: { + if (!visible && typeof weather !== 'undefined' && weather !== null && weather.fetchCityWeather) { + weather.fetchCityWeather(); + } + } +} diff --git a/shell.qml b/shell.qml index fd0ff8c..8f44ca3 100644 --- a/shell.qml +++ b/shell.qml @@ -97,15 +97,11 @@ Scope { NotificationServer { id: notificationServer onNotification: function (notification) { + console.log("[Notification] Received notification:", notification.appName, "-", notification.summary); notification.tracked = true; if (notificationPopup.notificationsVisible) { - // Add notification to all popup instances - for (let i = 0; i < notificationPopup.children.length; i++) { - let child = notificationPopup.children[i]; - if (child.addNotification) { - child.addNotification(notification); - } - } + // Add notification to the popup manager + notificationPopup.addNotification(notification); } if (notificationHistoryLoader.active && notificationHistoryLoader.item) { notificationHistoryLoader.item.addToHistory({ From 7c2d2b4d669361dcb440201d2c648901c079f22e Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 19:10:32 -0400 Subject: [PATCH 10/43] Settings window: added separators and renamed Recording to Screen Recorder --- Widgets/SettingsWindow/SettingsWindow.qml | 8 +- Widgets/SettingsWindow/Tabs/Bar.qml | 2 +- Widgets/SettingsWindow/Tabs/General.qml | 14 +- Widgets/SettingsWindow/Tabs/Network.qml | 14 +- Widgets/SettingsWindow/Tabs/Recording.qml | 1565 ++++++++++--------- Widgets/SettingsWindow/Tabs/TimeWeather.qml | 14 +- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 276 ++-- 7 files changed, 1042 insertions(+), 851 deletions(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 3f9402b..156d5e0 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -57,7 +57,7 @@ PanelWithOverlay { "General", "Bar", "Time & Weather", - "Recording", + "Screen Recorder", "Network", "Display", "Wallpaper", @@ -202,7 +202,7 @@ PanelWithOverlay { text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : activeTabIndex === 1 ? "Bar" : activeTabIndex === 2 ? "Time & Weather" : - activeTabIndex === 3 ? "Recording" : + activeTabIndex === 3 ? "Screen Recorder" : activeTabIndex === 4 ? "Network" : activeTabIndex === 5 ? "Display" : activeTabIndex === 6 ? "Wallpaper" : @@ -303,7 +303,7 @@ PanelWithOverlay { left: parent.left right: parent.right bottom: parent.bottom - margins: 24 + margins: 16 topMargin: 32 } @@ -344,7 +344,7 @@ PanelWithOverlay { { icon: "tune", text: "General" }, { icon: "space_dashboard", text: "Bar" }, { icon: "schedule", text: "Time & Weather" }, - { icon: "photo_camera", text: "Recording" }, + { icon: "photo_camera", text: "Screen Recorder" }, { icon: "wifi", text: "Network" }, { icon: "monitor", text: "Display" }, { icon: "wallpaper", text: "Wallpaper" }, diff --git a/Widgets/SettingsWindow/Tabs/Bar.qml b/Widgets/SettingsWindow/Tabs/Bar.qml index 751a0ce..2148e16 100644 --- a/Widgets/SettingsWindow/Tabs/Bar.qml +++ b/Widgets/SettingsWindow/Tabs/Bar.qml @@ -21,7 +21,7 @@ ColumnLayout { Layout.fillWidth: true Text { - text: "Bar Elements" + text: "Elements" font.pixelSize: 18 font.bold: true color: Theme.textPrimary diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index dab795a..66a88e2 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -107,11 +107,23 @@ ColumnLayout { } } + Rectangle { + Layout.topMargin: 26 + Layout.bottomMargin: 18 + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + } + height: 1 + color: Theme.outline + opacity: 0.3 + } ColumnLayout { spacing: 4 Layout.fillWidth: true - Layout.topMargin: 58 + Text { text: "User Interface" diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index d5cdd06..b159f78 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -97,11 +97,23 @@ ColumnLayout { } } + Rectangle { + Layout.topMargin: 26 + Layout.bottomMargin: 18 + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + } + height: 1 + color: Theme.outline + opacity: 0.3 + } + ColumnLayout { spacing: 16 Layout.fillWidth: true - Layout.topMargin: 58 Text { text: "Bluetooth" diff --git a/Widgets/SettingsWindow/Tabs/Recording.qml b/Widgets/SettingsWindow/Tabs/Recording.qml index 36234b4..1011538 100644 --- a/Widgets/SettingsWindow/Tabs/Recording.qml +++ b/Widgets/SettingsWindow/Tabs/Recording.qml @@ -1,17 +1,19 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import qs.Settings import qs.Components +import qs.Settings ColumnLayout { id: root + spacing: 0 anchors.fill: parent anchors.margins: 0 ScrollView { id: scrollView + Layout.fillWidth: true Layout.fillHeight: true padding: 0 @@ -29,784 +31,849 @@ ColumnLayout { Layout.preferredHeight: 0 } - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Screen Recording" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Output Directory" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Directory where screen recordings will be saved" - font.pixelSize: 12 - color: Theme.textSecondary - Layout.bottomMargin: 4 - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: videoPathInput - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.videoPath = text; - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: videoPathInput.forceActiveFocus() - } - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Frame Rate" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Target frame rate for screen recordings (default: 60)" - font.pixelSize: 12 - color: Theme.textSecondary - Layout.bottomMargin: 4 - } - - SpinBox { - id: frameRateSpinBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - from: 24 - to: 144 - value: Settings.settings.recordingFrameRate || 60 - stepSize: 1 - - onValueChanged: { - Settings.settings.recordingFrameRate = value; - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: frameRateSpinBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: TextInput { - text: frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale) - font.pixelSize: 13 - color: Theme.textPrimary - selectionColor: Theme.accentPrimary - selectedTextColor: Theme.onAccent - horizontalAlignment: Qt.AlignHCenter - verticalAlignment: Qt.AlignVCenter - readOnly: false - selectByMouse: true - validator: IntValidator { - bottom: frameRateSpinBox.from - top: frameRateSpinBox.to - } - inputMethodHints: Qt.ImhDigitsOnly - - onTextChanged: { - var newValue = parseInt(text); - if (!isNaN(newValue) && newValue >= frameRateSpinBox.from && newValue <= frameRateSpinBox.to) { - frameRateSpinBox.value = newValue; - } - } - - onEditingFinished: { - var newValue = parseInt(text); - if (isNaN(newValue) || newValue < frameRateSpinBox.from || newValue > frameRateSpinBox.to) { - text = frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale); - } - } - } - - up.indicator: Rectangle { - x: parent.width - width - height: parent.height - width: height - color: "transparent" - radius: 16 - - Text { - text: "add" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.textPrimary - anchors.centerIn: parent - } - } - - down.indicator: Rectangle { - x: 0 - height: parent.height - width: height - color: "transparent" - radius: 16 - - Text { - text: "remove" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.textPrimary - anchors.centerIn: parent - } - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Audio Source" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Audio source to capture during recording" - font.pixelSize: 12 - color: Theme.textSecondary - Layout.bottomMargin: 4 - } - - ComboBox { - id: audioSourceComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["default_output", "default_input", "both"] - currentIndex: model.indexOf(Settings.settings.recordingAudioSource || "default_output") - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: audioSourceComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: audioSourceComboBox.indicator.width + audioSourceComboBox.spacing - text: { - switch(audioSourceComboBox.currentText) { - case "default_output": return "System Audio"; - case "default_input": return "Microphone"; - case "both": return "System Audio + Microphone"; - default: return audioSourceComboBox.currentText; - } - } - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: audioSourceComboBox.width - width - 12 - y: audioSourceComboBox.topPadding + (audioSourceComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: audioSourceComboBox.height - width: audioSourceComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: audioSourceComboBox.popup.visible ? audioSourceComboBox.delegateModel : null - currentIndex: audioSourceComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: audioSourceComboBox.width - contentItem: Text { - text: { - switch(modelData) { - case "default_output": return "System Audio"; - case "default_input": return "Microphone"; - case "both": return "System Audio + Microphone"; - default: return modelData; - } - } - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: audioSourceComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.recordingAudioSource = model[index]; - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Video Quality" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Higher quality results in larger file sizes" - font.pixelSize: 12 - color: Theme.textSecondary - Layout.bottomMargin: 4 - } - - ComboBox { - id: qualityComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["medium", "high", "very_high", "ultra"] - currentIndex: model.indexOf(Settings.settings.recordingQuality || "very_high") - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: qualityComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: qualityComboBox.indicator.width + qualityComboBox.spacing - text: { - switch(qualityComboBox.currentText) { - case "medium": return "Medium"; - case "high": return "High"; - case "very_high": return "Very High"; - case "ultra": return "Ultra"; - default: return qualityComboBox.currentText; - } - } - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: qualityComboBox.width - width - 12 - y: qualityComboBox.topPadding + (qualityComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: qualityComboBox.height - width: qualityComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: qualityComboBox.popup.visible ? qualityComboBox.delegateModel : null - currentIndex: qualityComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: qualityComboBox.width - contentItem: Text { - text: { - switch(modelData) { - case "medium": return "Medium"; - case "high": return "High"; - case "very_high": return "Very High"; - case "ultra": return "Ultra"; - default: return modelData; - } - } - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: qualityComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.recordingQuality = model[index]; - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Video Codec" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Different codecs offer different compression and compatibility" - font.pixelSize: 12 - color: Theme.textSecondary - Layout.bottomMargin: 4 - } - - ComboBox { - id: codecComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["h264", "hevc", "av1", "vp8", "vp9"] - currentIndex: model.indexOf(Settings.settings.recordingCodec || "h264") - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: codecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: codecComboBox.indicator.width + codecComboBox.spacing - text: codecComboBox.currentText.toUpperCase() - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: codecComboBox.width - width - 12 - y: codecComboBox.topPadding + (codecComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: codecComboBox.height - width: codecComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: codecComboBox.popup.visible ? codecComboBox.delegateModel : null - currentIndex: codecComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: codecComboBox.width - contentItem: Text { - text: modelData.toUpperCase() - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: codecComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.recordingCodec = model[index]; - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Audio Codec" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Opus is recommended for best performance and smallest audio size" - font.pixelSize: 12 - color: Theme.textSecondary - Layout.bottomMargin: 4 - } - - ComboBox { - id: audioCodecComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["opus", "aac"] - currentIndex: model.indexOf(Settings.settings.audioCodec || "opus") - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: audioCodecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: audioCodecComboBox.indicator.width + audioCodecComboBox.spacing - text: audioCodecComboBox.currentText.toUpperCase() - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: audioCodecComboBox.width - width - 12 - y: audioCodecComboBox.topPadding + (audioCodecComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: audioCodecComboBox.height - width: audioCodecComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: audioCodecComboBox.popup.visible ? audioCodecComboBox.delegateModel : null - currentIndex: audioCodecComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: audioCodecComboBox.width - contentItem: Text { - text: modelData.toUpperCase() - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: audioCodecComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.audioCodec = model[index]; - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Color Range" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Limited is recommended for better compatibility" - font.pixelSize: 12 - color: Theme.textSecondary - Layout.bottomMargin: 4 - } - - ComboBox { - id: colorRangeComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["limited", "full"] - currentIndex: model.indexOf(Settings.settings.colorRange || "limited") - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: colorRangeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: colorRangeComboBox.indicator.width + colorRangeComboBox.spacing - text: colorRangeComboBox.currentText.charAt(0).toUpperCase() + colorRangeComboBox.currentText.slice(1) - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: colorRangeComboBox.width - width - 12 - y: colorRangeComboBox.topPadding + (colorRangeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: colorRangeComboBox.height - width: colorRangeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: colorRangeComboBox.popup.visible ? colorRangeComboBox.delegateModel : null - currentIndex: colorRangeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: colorRangeComboBox.width - contentItem: Text { - text: modelData.charAt(0).toUpperCase() + modelData.slice(1) - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: colorRangeComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.colorRange = model[index]; - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 + // Text { + // text: "Screen Recording" + // font.pixelSize: 18 + // font.bold: true + // color: Theme.textPrimary + // Layout.bottomMargin: 8 + // } + + spacing: 4 Layout.fillWidth: true ColumnLayout { - spacing: 4 + spacing: 8 Layout.fillWidth: true Text { - text: "Show Cursor" + text: "Output Directory" font.pixelSize: 13 font.bold: true color: Theme.textPrimary } Text { - text: "Record mouse cursor in the video" + text: "Directory where screen recordings will be saved" font.pixelSize: 12 color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true + Layout.bottomMargin: 4 } - } - - Rectangle { - id: cursorSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.outline - border.width: 2 Rectangle { - id: cursorThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 - y: 2 - x: Settings.settings.showCursor ? cursorSwitch.width - width - 2 : 2 - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic + TextInput { + id: videoPathInput + + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.videoPath = text; } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: videoPathInput.forceActiveFocus() + } + } + } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showCursor = !Settings.settings.showCursor; - } - } } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Frame Rate" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Target frame rate for screen recordings (default: 60)" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + SpinBox { + id: frameRateSpinBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + from: 24 + to: 144 + value: Settings.settings.recordingFrameRate || 60 + stepSize: 1 + onValueChanged: { + Settings.settings.recordingFrameRate = value; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: frameRateSpinBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: TextInput { + text: frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale) + font.pixelSize: 13 + color: Theme.textPrimary + selectionColor: Theme.accentPrimary + selectedTextColor: Theme.onAccent + horizontalAlignment: Qt.AlignHCenter + verticalAlignment: Qt.AlignVCenter + readOnly: false + selectByMouse: true + inputMethodHints: Qt.ImhDigitsOnly + onTextChanged: { + var newValue = parseInt(text); + if (!isNaN(newValue) && newValue >= frameRateSpinBox.from && newValue <= frameRateSpinBox.to) + frameRateSpinBox.value = newValue; + + } + onEditingFinished: { + var newValue = parseInt(text); + if (isNaN(newValue) || newValue < frameRateSpinBox.from || newValue > frameRateSpinBox.to) + text = frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale); + + } + + validator: IntValidator { + bottom: frameRateSpinBox.from + top: frameRateSpinBox.to + } + + } + + up.indicator: Rectangle { + x: parent.width - width + height: parent.height + width: height + color: "transparent" + radius: 16 + + Text { + text: "add" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.textPrimary + anchors.centerIn: parent + } + + } + + down.indicator: Rectangle { + x: 0 + height: parent.height + width: height + color: "transparent" + radius: 16 + + Text { + text: "remove" + font.family: "Material Symbols Outlined" + font.pixelSize: 20 + color: Theme.textPrimary + anchors.centerIn: parent + } + + } + + } + + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Audio Source" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Audio source to capture during recording" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: audioSourceComboBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["default_output", "default_input", "both"] + currentIndex: model.indexOf(Settings.settings.recordingAudioSource || "default_output") + onActivated: { + Settings.settings.recordingAudioSource = model[index]; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: audioSourceComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: audioSourceComboBox.indicator.width + audioSourceComboBox.spacing + text: { + switch (audioSourceComboBox.currentText) { + case "default_output": + return "System Audio"; + case "default_input": + return "Microphone"; + case "both": + return "System Audio + Microphone"; + default: + return audioSourceComboBox.currentText; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: audioSourceComboBox.width - width - 12 + y: audioSourceComboBox.topPadding + (audioSourceComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: audioSourceComboBox.height + width: audioSourceComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: audioSourceComboBox.popup.visible ? audioSourceComboBox.delegateModel : null + currentIndex: audioSourceComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + + } + + delegate: ItemDelegate { + width: audioSourceComboBox.width + highlighted: audioSourceComboBox.highlightedIndex === index + + contentItem: Text { + text: { + switch (modelData) { + case "default_output": + return "System Audio"; + case "default_input": + return "Microphone"; + case "both": + return "System Audio + Microphone"; + default: + return modelData; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + + } + + } + + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Video Quality" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Higher quality results in larger file sizes" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: qualityComboBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["medium", "high", "very_high", "ultra"] + currentIndex: model.indexOf(Settings.settings.recordingQuality || "very_high") + onActivated: { + Settings.settings.recordingQuality = model[index]; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: qualityComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: qualityComboBox.indicator.width + qualityComboBox.spacing + text: { + switch (qualityComboBox.currentText) { + case "medium": + return "Medium"; + case "high": + return "High"; + case "very_high": + return "Very High"; + case "ultra": + return "Ultra"; + default: + return qualityComboBox.currentText; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: qualityComboBox.width - width - 12 + y: qualityComboBox.topPadding + (qualityComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: qualityComboBox.height + width: qualityComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: qualityComboBox.popup.visible ? qualityComboBox.delegateModel : null + currentIndex: qualityComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + + } + + delegate: ItemDelegate { + width: qualityComboBox.width + highlighted: qualityComboBox.highlightedIndex === index + + contentItem: Text { + text: { + switch (modelData) { + case "medium": + return "Medium"; + case "high": + return "High"; + case "very_high": + return "Very High"; + case "ultra": + return "Ultra"; + default: + return modelData; + } + } + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + + } + + } + + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Video Codec" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Different codecs offer different compression and compatibility" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: codecComboBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["h264", "hevc", "av1", "vp8", "vp9"] + currentIndex: model.indexOf(Settings.settings.recordingCodec || "h264") + onActivated: { + Settings.settings.recordingCodec = model[index]; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: codecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: codecComboBox.indicator.width + codecComboBox.spacing + text: codecComboBox.currentText.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: codecComboBox.width - width - 12 + y: codecComboBox.topPadding + (codecComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: codecComboBox.height + width: codecComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: codecComboBox.popup.visible ? codecComboBox.delegateModel : null + currentIndex: codecComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + + } + + delegate: ItemDelegate { + width: codecComboBox.width + highlighted: codecComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + + } + + } + + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Audio Codec" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Opus is recommended for best performance and smallest audio size" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: audioCodecComboBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["opus", "aac"] + currentIndex: model.indexOf(Settings.settings.audioCodec || "opus") + onActivated: { + Settings.settings.audioCodec = model[index]; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: audioCodecComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: audioCodecComboBox.indicator.width + audioCodecComboBox.spacing + text: audioCodecComboBox.currentText.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: audioCodecComboBox.width - width - 12 + y: audioCodecComboBox.topPadding + (audioCodecComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: audioCodecComboBox.height + width: audioCodecComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: audioCodecComboBox.popup.visible ? audioCodecComboBox.delegateModel : null + currentIndex: audioCodecComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + + } + + delegate: ItemDelegate { + width: audioCodecComboBox.width + highlighted: audioCodecComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData.toUpperCase() + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + + } + + } + + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Color Range" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Limited is recommended for better compatibility" + font.pixelSize: 12 + color: Theme.textSecondary + Layout.bottomMargin: 4 + } + + ComboBox { + id: colorRangeComboBox + + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["limited", "full"] + currentIndex: model.indexOf(Settings.settings.colorRange || "limited") + onActivated: { + Settings.settings.colorRange = model[index]; + } + + background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 + color: Theme.surfaceVariant + border.color: colorRangeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + radius: 16 + } + + contentItem: Text { + leftPadding: 12 + rightPadding: colorRangeComboBox.indicator.width + colorRangeComboBox.spacing + text: colorRangeComboBox.currentText.charAt(0).toUpperCase() + colorRangeComboBox.currentText.slice(1) + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + indicator: Text { + x: colorRangeComboBox.width - width - 12 + y: colorRangeComboBox.topPadding + (colorRangeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary + } + + popup: Popup { + y: colorRangeComboBox.height + width: colorRangeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: colorRangeComboBox.popup.visible ? colorRangeComboBox.delegateModel : null + currentIndex: colorRangeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + + } + + delegate: ItemDelegate { + width: colorRangeComboBox.width + highlighted: colorRangeComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData.charAt(0).toUpperCase() + modelData.slice(1) + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + + } + + } + + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Cursor" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Record mouse cursor in the video" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: cursorSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: cursorThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showCursor ? cursorSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showCursor = !Settings.settings.showCursor; + } + } + + } + + } + + } + } - } - } Item { Layout.fillWidth: true Layout.preferredHeight: 24 } + } + } -} \ No newline at end of file + +} diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml index e374dea..926b4c3 100644 --- a/Widgets/SettingsWindow/Tabs/TimeWeather.qml +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -168,11 +168,23 @@ ColumnLayout { } } + Rectangle { + Layout.topMargin: 26 + Layout.bottomMargin: 18 + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + } + height: 1 + color: Theme.outline + opacity: 0.3 + } + ColumnLayout { spacing: 4 Layout.fillWidth: true - Layout.topMargin: 58 Text { text: "Weather" diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index 0453b27..cb5d964 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -1,20 +1,20 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import qs.Settings import qs.Components import qs.Services +import qs.Settings ColumnLayout { id: root + spacing: 0 anchors.fill: parent anchors.margins: 0 - - ScrollView { id: scrollView + Layout.fillWidth: true Layout.fillHeight: true padding: 0 @@ -36,15 +36,13 @@ ColumnLayout { spacing: 4 Layout.fillWidth: true - Text { - text: "Wallpaper" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - + Text { + text: "Wallpaper" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } // Wallpaper Settings Category ColumnLayout { @@ -93,6 +91,7 @@ ColumnLayout { TextInput { id: folderInput + anchors.fill: parent anchors.leftMargin: 12 anchors.rightMargin: 12 @@ -117,25 +116,38 @@ ColumnLayout { cursorShape: Qt.IBeamCursor onClicked: folderInput.forceActiveFocus() } + } + } + } + } - } + Rectangle { + Layout.topMargin: 26 + Layout.bottomMargin: 18 + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + } + height: 1 + color: Theme.outline + opacity: 0.3 + } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - Layout.topMargin: 58 + ColumnLayout { + spacing: 4 + Layout.fillWidth: true - Text { - text: "Automation" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } + Text { + text: "Automation" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } // Random Wallpaper ColumnLayout { @@ -165,10 +177,12 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: randomWallpaperSwitch + width: 52 height: 32 radius: 16 @@ -178,6 +192,7 @@ ColumnLayout { Rectangle { id: randomWallpaperThumb + width: 28 height: 28 radius: 14 @@ -192,7 +207,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -202,8 +219,11 @@ ColumnLayout { Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; } } + } + } + } // Use Wallpaper Theme @@ -234,10 +254,12 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: wallpaperThemeSwitch + width: 52 height: 32 radius: 16 @@ -247,6 +269,7 @@ ColumnLayout { Rectangle { id: wallpaperThemeThumb + width: 28 height: 28 radius: 14 @@ -261,7 +284,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -271,8 +296,11 @@ ColumnLayout { Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; } } + } + } + } // Wallpaper Interval @@ -308,10 +336,12 @@ ColumnLayout { Item { Layout.fillWidth: true } + } Slider { id: intervalSlider + Layout.fillWidth: true from: 10 to: 900 @@ -338,6 +368,7 @@ ColumnLayout { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -350,95 +381,117 @@ ColumnLayout { border.color: Theme.accentPrimary border.width: 2 } + } + } + } - } + } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - Layout.topMargin: 58 + Rectangle { + Layout.topMargin: 26 + Layout.bottomMargin: 18 + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + } + height: 1 + color: Theme.outline + opacity: 0.3 + } - Text { - text: "SWWW" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } + ColumnLayout { + spacing: 4 + Layout.fillWidth: true - // Use SWWW - ColumnLayout { + Text { + text: "SWWW" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Use SWWW + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { spacing: 8 Layout.fillWidth: true - Layout.topMargin: 8 - RowLayout { - spacing: 8 + ColumnLayout { + spacing: 4 Layout.fillWidth: true - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Use SWWW daemon for advanced wallpaper management" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } + Text { + text: "Use SWWW" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary } + Text { + text: "Use SWWW daemon for advanced wallpaper management" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: swwwSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline + border.width: 2 + Rectangle { - id: swwwSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 + id: swwwThumb - Rectangle { - id: swwwThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic } + } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useSWWW = !Settings.settings.useSWWW; } } + } + } + } + // SWWW Settings (only visible when useSWWW is enabled) ColumnLayout { spacing: 8 @@ -476,6 +529,7 @@ ColumnLayout { ComboBox { id: resizeComboBox + anchors.fill: parent anchors.leftMargin: 12 anchors.rightMargin: 12 @@ -511,7 +565,9 @@ ColumnLayout { model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null currentIndex: resizeComboBox.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator { } + ScrollIndicator.vertical: ScrollIndicator { + } + } background: Rectangle { @@ -520,10 +576,13 @@ ColumnLayout { border.width: 1 radius: 8 } + } delegate: ItemDelegate { width: resizeComboBox.width + highlighted: resizeComboBox.highlightedIndex === index + contentItem: Text { text: modelData color: Theme.textPrimary @@ -531,13 +590,17 @@ ColumnLayout { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft } - highlighted: resizeComboBox.highlightedIndex === index + background: Rectangle { color: parent.highlighted ? Theme.accentPrimary : "transparent" } + } + } + } + } // Transition Type @@ -571,6 +634,7 @@ ColumnLayout { ComboBox { id: transitionTypeComboBox + anchors.fill: parent anchors.leftMargin: 12 anchors.rightMargin: 12 @@ -606,7 +670,9 @@ ColumnLayout { model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null currentIndex: transitionTypeComboBox.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator { } + ScrollIndicator.vertical: ScrollIndicator { + } + } background: Rectangle { @@ -615,10 +681,13 @@ ColumnLayout { border.width: 1 radius: 8 } + } delegate: ItemDelegate { width: transitionTypeComboBox.width + highlighted: transitionTypeComboBox.highlightedIndex === index + contentItem: Text { text: modelData color: Theme.textPrimary @@ -626,13 +695,17 @@ ColumnLayout { verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignLeft } - highlighted: transitionTypeComboBox.highlightedIndex === index + background: Rectangle { color: parent.highlighted ? Theme.accentPrimary : "transparent" } + } + } + } + } // Transition FPS @@ -668,10 +741,12 @@ ColumnLayout { Item { Layout.fillWidth: true } + } Slider { id: fpsSlider + Layout.fillWidth: true from: 30 to: 500 @@ -698,6 +773,7 @@ ColumnLayout { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -710,7 +786,9 @@ ColumnLayout { border.color: Theme.accentPrimary border.width: 2 } + } + } // Transition Duration @@ -746,10 +824,12 @@ ColumnLayout { Item { Layout.fillWidth: true } + } Slider { id: durationSlider + Layout.fillWidth: true from: 0.25 to: 10 @@ -776,6 +856,7 @@ ColumnLayout { color: Theme.accentPrimary radius: 2 } + } handle: Rectangle { @@ -788,15 +869,22 @@ ColumnLayout { border.color: Theme.accentPrimary border.width: 2 } + } + } + } + } + } + } Item { Layout.fillWidth: true Layout.fillHeight: true } -} \ No newline at end of file + +} From 63eeb40c643ebe9283c41d48d1b02dd745c120a0 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 20:03:41 -0400 Subject: [PATCH 11/43] Settings: better horizontal dividers --- Widgets/SettingsWindow/SettingsWindow.qml | 600 +++++++++--------- Widgets/SettingsWindow/Tabs/About.qml | 637 +++++++++++--------- Widgets/SettingsWindow/Tabs/General.qml | 62 +- Widgets/SettingsWindow/Tabs/Network.qml | 21 +- Widgets/SettingsWindow/Tabs/TimeWeather.qml | 55 +- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 461 +++++++------- 6 files changed, 980 insertions(+), 856 deletions(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 156d5e0..b7d9cce 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -1,156 +1,163 @@ -import Quickshell -import Quickshell.Wayland import QtQuick import QtQuick.Controls -import QtQuick.Layouts import QtQuick.Effects +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland +import qs.Components import qs.Settings import qs.Widgets.SettingsWindow.Tabs import qs.Widgets.SettingsWindow.Tabs.Components -import qs.Components PanelWithOverlay { id: panelMain - - property int activeTabIndex: 0 - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + property int activeTabIndex: 0 // Function to show wallpaper selector function showWallpaperSelector() { - if (wallpaperSelector) { + if (wallpaperSelector) wallpaperSelector.show(); - } + } // Function to show settings window function showSettings() { show(); } - - - - - - // Handle activeTabIndex changes - onActiveTabIndexChanged: { - if (activeTabIndex >= 0 && activeTabIndex <= 8) { - loadComponentForTab(activeTabIndex); - } - } // Function to load component for a specific tab function loadComponentForTab(tabIndex) { const componentMap = { - 0: generalSettings, - 1: barSettings, - 2: timeWeatherSettings, - 3: recordingSettings, - 4: networkSettings, - 5: displaySettings, - 6: wallpaperSettings, - 7: miscSettings, - 8: aboutSettings + "0": generalSettings, + "1": barSettings, + "2": timeWeatherSettings, + "3": recordingSettings, + "4": networkSettings, + "5": displaySettings, + "6": wallpaperSettings, + "7": miscSettings, + "8": aboutSettings }; - - const tabNames = [ - "General", - "Bar", - "Time & Weather", - "Screen Recorder", - "Network", - "Display", - "Wallpaper", - "Misc", - "About" - ]; - + const tabNames = ["General", "Bar", "Time & Weather", "Screen Recorder", "Network", "Display", "Wallpaper", "Misc", "About"]; if (componentMap[tabIndex]) { settingsLoader.sourceComponent = componentMap[tabIndex]; - if (tabName) { + if (tabName) tabName.text = tabNames[tabIndex]; - } - } } + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + // Handle activeTabIndex changes + onActiveTabIndexChanged: { + if (activeTabIndex >= 0 && activeTabIndex <= 8) + loadComponentForTab(activeTabIndex); + + } // Add safety checks for component loading Component.onCompleted: { // Ensure we start with a valid tab - if (activeTabIndex < 0 || activeTabIndex > 8) { + if (activeTabIndex < 0 || activeTabIndex > 8) activeTabIndex = 0; - } - } + } // Cleanup when window is hidden onVisibleChanged: { if (!visible) { // Reset to default tab when hiding to prevent state issues activeTabIndex = 0; - if (tabName) { + if (tabName) tabName.text = "General"; - } + } } Component { id: generalSettings - General {} + + General { + } + } Component { id: barSettings - Bar {} + + Bar { + } + } Component { id: timeWeatherSettings - TimeWeather {} + + TimeWeather { + } + } Component { id: recordingSettings - Recording {} + + Recording { + } + } Component { id: networkSettings - Network {} + + Network { + } + } Component { id: miscSettings - Misc {} + + Misc { + } + } Component { id: aboutSettings - About {} + + About { + } + } Component { id: displaySettings - Display {} + + Display { + } + } Component { id: wallpaperSettings - Wallpaper {} + + Wallpaper { + } + } Rectangle { id: settingsWindowRect + implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600 implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400 visible: parent.visible color: "transparent" - // Center the settings window on screen anchors.centerIn: parent Rectangle { id: background + color: Theme.backgroundPrimary anchors.fill: parent radius: 20 @@ -167,11 +174,16 @@ PanelWithOverlay { shadowVerticalOffset: 2 shadowBlur: 12 } + } Rectangle { id: settings + color: Theme.backgroundPrimary + topRightRadius: 20 + bottomRightRadius: 20 + anchors { left: tabs.right top: parent.top @@ -179,151 +191,159 @@ PanelWithOverlay { right: parent.right margins: 12 } - topRightRadius: 20 - bottomRightRadius: 20 - Rectangle { - id: headerArea - anchors { - top: parent.top - left: parent.left - right: parent.right - margins: 16 - } - height: 48 - color: "transparent" + Rectangle { + id: headerArea - RowLayout { - anchors.fill: parent - spacing: 12 + height: 48 + color: "transparent" - Text { - id: tabName - text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : - activeTabIndex === 1 ? "Bar" : - activeTabIndex === 2 ? "Time & Weather" : - activeTabIndex === 3 ? "Screen Recorder" : - activeTabIndex === 4 ? "Network" : - activeTabIndex === 5 ? "Display" : - activeTabIndex === 6 ? "Wallpaper" : - activeTabIndex === 7 ? "Misc" : - activeTabIndex === 8 ? "About" : "General") - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true + anchors { + top: parent.top + left: parent.left + right: parent.right + margins: 16 } - // Wallpaper Selection Button (only visible on Wallpaper tab) - Rectangle { - width: 160 - height: 32 - radius: 16 - color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - visible: activeTabIndex === 6 // Wallpaper tab index - - Row { - anchors.centerIn: parent - spacing: 6 - - Text { - text: "image" - font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 - color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: "Select Wallpaper" - font.pixelSize: 13 - font.bold: true - color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - anchors.verticalCenter: parent.verticalCenter - } - } - - MouseArea { - id: wallpaperButtonArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - // Show the wallpaper selector - wallpaperSelector.show(); - } - } - } - - Rectangle { - width: 32 - height: 32 - radius: 16 - color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 + RowLayout { + anchors.fill: parent + spacing: 12 Text { - anchors.centerIn: parent - text: "close" - font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + id: tabName + + text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : activeTabIndex === 1 ? "Bar" : activeTabIndex === 2 ? "Time & Weather" : activeTabIndex === 3 ? "Screen Recorder" : activeTabIndex === 4 ? "Network" : activeTabIndex === 5 ? "Display" : activeTabIndex === 6 ? "Wallpaper" : activeTabIndex === 7 ? "Misc" : activeTabIndex === 8 ? "About" : "General") font.pixelSize: 18 - color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true } - MouseArea { - id: closeButtonArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: panelMain.dismiss() + // Wallpaper Selection Button (only visible on Wallpaper tab) + Rectangle { + width: 160 + height: 32 + radius: 16 + color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 + visible: activeTabIndex === 6 // Wallpaper tab index + + Row { + anchors.centerIn: parent + spacing: 6 + + Text { + text: "image" + font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 16 + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + anchors.verticalCenter: parent.verticalCenter + } + + Text { + text: "Select Wallpaper" + font.pixelSize: 13 + font.bold: true + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + anchors.verticalCenter: parent.verticalCenter + } + + } + + MouseArea { + id: wallpaperButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // Show the wallpaper selector + wallpaperSelector.show(); + } + } + } + + Rectangle { + width: 32 + height: 32 + radius: 16 + color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 + + Text { + anchors.centerIn: parent + text: "close" + font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 18 + color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + } + + MouseArea { + id: closeButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: panelMain.dismiss() + } + + } + } + } + + Rectangle { + height: 1 + color: Theme.outline + opacity: 0.3 + + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + margins: 16 + } + + } + + Item { + id: settingsContainer + + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + margins: 16 + topMargin: 32 + } + + // Simplified single loader approach + Loader { + id: settingsLoader + + anchors.fill: parent + sourceComponent: generalSettings + } + + // Wallpaper Selector Component + WallpaperSelector { + id: wallpaperSelector + + anchors.fill: parent + } + + } + } - Rectangle { - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - margins: 16 - } - height: 1 - color: Theme.outline - opacity: 0.3 - } - - Item { - id: settingsContainer - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - bottom: parent.bottom - margins: 16 - topMargin: 32 - } - - // Simplified single loader approach - Loader { - id: settingsLoader - anchors.fill: parent - sourceComponent: generalSettings - } - - // Wallpaper Selector Component - WallpaperSelector { - id: wallpaperSelector - anchors.fill: parent - } - } - } - Rectangle { id: tabs + color: Theme.surface width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100 height: settingsWindowRect.height @@ -332,101 +352,135 @@ PanelWithOverlay { border.color: Theme.outline border.width: 1 - Column { - width: parent.width - spacing: 0 - topPadding: 8 - bottomPadding: 8 + Column { + width: parent.width + spacing: 0 + topPadding: 8 + bottomPadding: 8 - Repeater { - id: repeater - model: [ - { icon: "tune", text: "General" }, - { icon: "space_dashboard", text: "Bar" }, - { icon: "schedule", text: "Time & Weather" }, - { icon: "photo_camera", text: "Screen Recorder" }, - { icon: "wifi", text: "Network" }, - { icon: "monitor", text: "Display" }, - { icon: "wallpaper", text: "Wallpaper" }, - { icon: "settings_suggest", text: "Misc" }, - { icon: "info", text: "About" } - ] + Repeater { + id: repeater - delegate: Rectangle { - width: tabs.width - height: 48 - color: "transparent" + model: [{ + "icon": "tune", + "text": "General" + }, { + "icon": "space_dashboard", + "text": "Bar" + }, { + "icon": "schedule", + "text": "Time & Weather" + }, { + "icon": "photo_camera", + "text": "Screen Recorder" + }, { + "icon": "wifi", + "text": "Network" + }, { + "icon": "monitor", + "text": "Display" + }, { + "icon": "wallpaper", + "text": "Wallpaper" + }, { + "icon": "settings_suggest", + "text": "Misc" + }, { + "icon": "info", + "text": "About" + }] - RowLayout { - anchors.fill: parent - spacing: 8 + delegate: Rectangle { + width: tabs.width + height: 48 + color: "transparent" + + RowLayout { + anchors.fill: parent + spacing: 8 + + Rectangle { + id: activeIndicator + + Layout.leftMargin: 8 + Layout.preferredWidth: 3 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignVCenter + radius: 2 + color: Theme.accentPrimary + opacity: index === activeTabIndex ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + + } + + } + + Label { + id: icon + + text: modelData.icon + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary + opacity: index === activeTabIndex ? 1 : 0.8 + Layout.leftMargin: 20 + Layout.preferredWidth: 24 + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignVCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Label { + id: label + + text: modelData.text + font.pixelSize: 16 + color: index === activeTabIndex ? Theme.accentPrimary : (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary) + font.weight: index === activeTabIndex ? Font.DemiBold : (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal) + Layout.fillWidth: true + Layout.preferredHeight: 24 + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.leftMargin: 4 + Layout.rightMargin: 16 + verticalAlignment: Text.AlignVCenter + } + + } + + MouseArea { + id: tabMouseArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + activeTabIndex = index; + loadComponentForTab(index); + } + } Rectangle { - id: activeIndicator - Layout.leftMargin: 8 - Layout.preferredWidth: 3 - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignVCenter - radius: 2 - color: Theme.accentPrimary - opacity: index === activeTabIndex ? 1 : 0 - Behavior on opacity { NumberAnimation { duration: 200 } } + width: parent.width + height: 1 + color: Theme.outline + opacity: 0.6 + visible: index < (repeater.count - 1) + anchors.bottom: parent.bottom } - Label { - id: icon - text: modelData.icon - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary - opacity: index === activeTabIndex ? 1 : 0.8 - Layout.leftMargin: 20 - Layout.preferredWidth: 24 - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignVCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - - Label { - id: label - text: modelData.text - font.pixelSize: 16 - color: index === activeTabIndex ? Theme.accentPrimary : - (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary) - font.weight: index === activeTabIndex ? Font.DemiBold : - (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal) - Layout.fillWidth: true - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: 4 - Layout.rightMargin: 16 - verticalAlignment: Text.AlignVCenter - } } - MouseArea { - id: tabMouseArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - activeTabIndex = index; - loadComponentForTab(index); - } - } - - Rectangle { - width: parent.width - height: 1 - color: Theme.outline - opacity: 0.6 - visible: index < (repeater.count - 1) - anchors.bottom: parent.bottom - } } + } + } + } - } -} \ No newline at end of file + +} diff --git a/Widgets/SettingsWindow/Tabs/About.qml b/Widgets/SettingsWindow/Tabs/About.qml index 14d3bc0..cdcdc11 100644 --- a/Widgets/SettingsWindow/Tabs/About.qml +++ b/Widgets/SettingsWindow/Tabs/About.qml @@ -1,11 +1,11 @@ import QtQuick import QtQuick.Controls -import QtQuick.Layouts import QtQuick.Effects +import QtQuick.Layouts import Quickshell import Quickshell.Io -import qs.Settings import qs.Components +import qs.Settings Item { id: root @@ -15,391 +15,430 @@ Item { property var contributors: [] property string githubDataPath: Settings.settingsDir + "github_data.json" + function loadFromFile() { + const now = Date.now(); + const data = githubData; + if (!data.timestamp || (now - data.timestamp > 3.6e+06)) { + console.log("[About] Cache expired or missing, fetching new data from GitHub..."); + fetchFromGitHub(); + return ; + } + console.log("[About] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)"); + if (data.version) + root.latestVersion = data.version; + + if (data.contributors) + root.contributors = data.contributors; + + } + + function fetchFromGitHub() { + versionProcess.running = true; + contributorsProcess.running = true; + } + + function saveData() { + githubData.timestamp = Date.now(); + Qt.callLater(() => { + githubDataFile.writeAdapter(); + }); + } + Process { id: currentVersionProcess + command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"] + Component.onCompleted: { + running = true; + } + stdout: StdioCollector { onStreamFinished: { - const version = text.trim() + const version = text.trim(); if (version && version !== "Unknown") { - root.currentVersion = version + root.currentVersion = version; } else { - - currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"] - currentVersionProcess.running = true + currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"]; + currentVersionProcess.running = true; } } } - Component.onCompleted: { - running = true - } + } FileView { id: githubDataFile + path: root.githubDataPath blockLoading: true printErrors: true watchChanges: true + onFileChanged: githubDataFile.reload() + onLoaded: loadFromFile() + onLoadFailed: function(error) { + console.log("GitHub data file doesn't exist yet, creating it..."); + githubData.version = "Unknown"; + githubData.contributors = []; + githubData.timestamp = 0; + githubDataFile.writeAdapter(); + fetchFromGitHub(); + } + Component.onCompleted: { + if (path) + reload(); + + } JsonAdapter { id: githubData + property string version: "Unknown" property var contributors: [] property double timestamp: 0 } - onFileChanged: githubDataFile.reload() - onLoaded: loadFromFile() - onLoadFailed: function(error) { - console.log("GitHub data file doesn't exist yet, creating it...") - githubData.version = "Unknown" - githubData.contributors = [] - githubData.timestamp = 0 - githubDataFile.writeAdapter() - fetchFromGitHub() - } - Component.onCompleted: if (path) reload() - } - - function loadFromFile() { - const now = Date.now() - const data = githubData - - if (!data.timestamp || (now - data.timestamp > 3600000)) { - console.log("[About] Cache expired or missing, fetching new data from GitHub...") - fetchFromGitHub() - return - } - console.log("[About] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)") - if (data.version) { - root.latestVersion = data.version - } - if (data.contributors) { - root.contributors = data.contributors - } } Process { id: versionProcess + command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/releases/latest"] + stdout: StdioCollector { onStreamFinished: { try { - const data = JSON.parse(text) + const data = JSON.parse(text); if (data.tag_name) { - const version = data.tag_name - githubData.version = version - root.latestVersion = version - console.log("[About] Latest version fetched from GitHub:", version) + const version = data.tag_name; + githubData.version = version; + root.latestVersion = version; + console.log("[About] Latest version fetched from GitHub:", version); } else { - console.log("No tag_name in GitHub response") + console.log("No tag_name in GitHub response"); } - saveData() + saveData(); } catch (e) { - console.error("Failed to parse version:", e) + console.error("Failed to parse version:", e); } } } + } Process { id: contributorsProcess + command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/contributors?per_page=100"] + stdout: StdioCollector { onStreamFinished: { try { - const data = JSON.parse(text) - githubData.contributors = data || [] - root.contributors = githubData.contributors - console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors") - saveData() + const data = JSON.parse(text); + githubData.contributors = data || []; + root.contributors = githubData.contributors; + console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors"); + saveData(); } catch (e) { - console.error("Failed to parse contributors:", e) - root.contributors = [] + console.error("Failed to parse contributors:", e); + root.contributors = []; } } } - } - function fetchFromGitHub() { - versionProcess.running = true - contributorsProcess.running = true - } - - function saveData() { - githubData.timestamp = Date.now() - Qt.callLater(() => { - githubDataFile.writeAdapter() - }) } Item { anchors.fill: parent - ColumnLayout { id: mainLayout + anchors.left: parent.left anchors.right: parent.right anchors.top: parent.top spacing: 8 - Item { - Layout.fillWidth: true - Layout.preferredHeight: 32 - } - - Text { - text: "Noctalia" - font.pixelSize: 24 - font.bold: true - color: Theme.textPrimary - Layout.alignment: Qt.AlignCenter - } - - GridLayout { - Layout.alignment: Qt.AlignCenter - columns: 2 - rowSpacing: 4 - columnSpacing: 8 - - Text { - text: "Latest Version:" - font.pixelSize: 16 - color: Theme.textSecondary - Layout.alignment: Qt.AlignRight - } - - Text { - text: root.latestVersion - font.pixelSize: 16 - color: Theme.textPrimary - font.bold: true - } - - Text { - text: "Installed Version:" - font.pixelSize: 16 - color: Theme.textSecondary - Layout.alignment: Qt.AlignRight - } - - Text { - text: root.currentVersion - font.pixelSize: 16 - color: Theme.textPrimary - font.bold: true - } - } - - - Rectangle { - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 8 - Layout.preferredWidth: updateText.implicitWidth + 46 - Layout.preferredHeight: 32 - radius: 20 - color: updateArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - visible: { - if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") { - return false - } - const latest = root.latestVersion.replace("v", "").split(".") - const current = root.currentVersion.replace("v", "").split(".") - - - for (let i = 0; i < Math.max(latest.length, current.length); i++) { - const l = parseInt(latest[i] || "0") - const c = parseInt(current[i] || "0") - if (l > c) return true - if (l < c) return false - } - return false - } - - RowLayout { - anchors.centerIn: parent - spacing: 8 - - Text { - text: "system_update" - font.family: "Material Symbols Outlined" - font.pixelSize: 18 - color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary - } - - Text { - id: updateText - text: "Download latest release" - font.pixelSize: 14 - color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary - } - } - - MouseArea { - id: updateArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]) - } - } - } - - Text { - text: "Description something something <.< I hate writing text..." - font.pixelSize: 14 - color: Theme.textSecondary - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 24 - } - - - ColumnLayout { - Layout.fillWidth: true - Layout.topMargin: 32 - Layout.leftMargin: 32 - Layout.rightMargin: 32 - spacing: 16 - - RowLayout { - Layout.alignment: Qt.AlignCenter - spacing: 8 - - Text { - text: "Contributors" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "(" + root.contributors.length + ")" - font.pixelSize: 14 - color: Theme.textSecondary - } - } - - ScrollView { + Item { Layout.fillWidth: true - Layout.preferredHeight: 300 - clip: true + Layout.preferredHeight: 16 + } + + Text { + text: "Noctalia" + font.pixelSize: 24 + font.bold: true + color: Theme.textPrimary + Layout.alignment: Qt.AlignCenter + } + + GridLayout { + Layout.alignment: Qt.AlignCenter + columns: 2 + rowSpacing: 4 + columnSpacing: 8 + + Text { + text: "Latest Version:" + font.pixelSize: 16 + color: Theme.textSecondary + Layout.alignment: Qt.AlignRight + } + + Text { + text: root.latestVersion + font.pixelSize: 16 + color: Theme.textPrimary + font.bold: true + } + + Text { + text: "Installed Version:" + font.pixelSize: 16 + color: Theme.textSecondary + Layout.alignment: Qt.AlignRight + } + + Text { + text: root.currentVersion + font.pixelSize: 16 + color: Theme.textPrimary + font.bold: true + } + + } + + Rectangle { + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 8 + Layout.preferredWidth: updateText.implicitWidth + 46 + Layout.preferredHeight: 32 + radius: 20 + color: updateArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 + visible: { + if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") + return false; + + const latest = root.latestVersion.replace("v", "").split("."); + const current = root.currentVersion.replace("v", "").split("."); + for (let i = 0; i < Math.max(latest.length, current.length); i++) { + const l = parseInt(latest[i] || "0"); + const c = parseInt(current[i] || "0"); + if (l > c) + return true; + + if (l < c) + return false; + + } + return false; + } + + RowLayout { + anchors.centerIn: parent + spacing: 8 + + Text { + text: "system_update" + font.family: "Material Symbols Outlined" + font.pixelSize: 18 + color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + } + + Text { + id: updateText + + text: "Download latest release" + font.pixelSize: 14 + color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + } + + } + + MouseArea { + id: updateArea - Item { anchors.fill: parent - - GridView { - id: contributorsGrid - anchors.centerIn: parent - width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200) - height: parent.height - cellWidth: 200 - cellHeight: 110 - model: root.contributors + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]); + } + } - delegate: Rectangle { - width: contributorsGrid.cellWidth - 4 - height: contributorsGrid.cellHeight - 10 - radius: 20 - color: contributorArea.containsMouse ? Theme.highlight : "transparent" + } - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 12 + Text { + text: "Description something something <.< I hate writing text..." + font.pixelSize: 14 + color: Theme.textSecondary + Layout.alignment: Qt.AlignCenter + Layout.topMargin: 24 + } - - Item { - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: 40 - Layout.preferredHeight: 40 + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } - Image { - id: avatarImage + ColumnLayout { + Layout.fillWidth: true + Layout.leftMargin: 32 + Layout.rightMargin: 32 + spacing: 16 + + RowLayout { + Layout.alignment: Qt.AlignCenter + spacing: 8 + + Text { + text: "Contributors" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "(" + root.contributors.length + ")" + font.pixelSize: 14 + color: Theme.textSecondary + } + + } + + ScrollView { + Layout.fillWidth: true + Layout.preferredHeight: 300 + clip: true + + Item { + anchors.fill: parent + + GridView { + id: contributorsGrid + + anchors.centerIn: parent + width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200) + height: parent.height + cellWidth: 200 + cellHeight: 100 + model: root.contributors + + delegate: Rectangle { + width: contributorsGrid.cellWidth - 4 + height: contributorsGrid.cellHeight - 10 + radius: 20 + color: contributorArea.containsMouse ? Theme.highlight : "transparent" + + RowLayout { anchors.fill: parent - source: modelData.avatar_url || "" - sourceSize: Qt.size(80, 80) - visible: false - mipmap: true - smooth: true - asynchronous: true - fillMode: Image.PreserveAspectCrop - cache: true + anchors.margins: 8 + spacing: 12 + + Item { + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 + + Image { + id: avatarImage + + anchors.fill: parent + source: modelData.avatar_url || "" + sourceSize: Qt.size(80, 80) + visible: false + mipmap: true + smooth: true + asynchronous: true + fillMode: Image.PreserveAspectCrop + cache: true + } + + MultiEffect { + anchors.fill: parent + source: avatarImage + maskEnabled: true + maskSource: mask + } + + Item { + id: mask + + anchors.fill: parent + layer.enabled: true + visible: false + + Rectangle { + anchors.fill: parent + radius: avatarImage.width / 2 + } + + } + + Text { + anchors.centerIn: parent + text: "person" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary + visible: !avatarImage.source || avatarImage.status !== Image.Ready + } + + } + + ColumnLayout { + spacing: 4 + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + + Text { + text: modelData.login || "Unknown" + font.pixelSize: 13 + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: (modelData.contributions || 0) + " commits" + font.pixelSize: 11 + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary + } + + } + } - MultiEffect { - anchors.fill: parent - source: avatarImage - maskEnabled: true - maskSource: mask - } + MouseArea { + id: contributorArea - Item { - id: mask anchors.fill: parent - layer.enabled: true - visible: false - Rectangle { - anchors.fill: parent - radius: avatarImage.width / 2 + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (modelData.html_url) + Quickshell.execDetached(["xdg-open", modelData.html_url]); + } } - - Text { - anchors.centerIn: parent - text: "person" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary - visible: !avatarImage.source || avatarImage.status !== Image.Ready - } } - - ColumnLayout { - spacing: 4 - Layout.alignment: Qt.AlignVCenter - Layout.fillWidth: true - - Text { - text: modelData.login || "Unknown" - font.pixelSize: 13 - color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary - elide: Text.ElideRight - Layout.fillWidth: true - } - - Text { - text: (modelData.contributions || 0) + " commits" - font.pixelSize: 11 - color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary - } - } } - MouseArea { - id: contributorArea - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - if (modelData.html_url) { - Quickshell.execDetached(["xdg-open", modelData.html_url]) - } - } - } } + } - } + } + } - - } } - } \ No newline at end of file + +} diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index 66a88e2..b03c1bd 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -1,11 +1,12 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import qs.Settings import qs.Components +import qs.Settings ColumnLayout { id: root + spacing: 0 anchors.fill: parent anchors.margins: 0 @@ -15,7 +16,6 @@ ColumnLayout { Layout.preferredHeight: 0 } - ColumnLayout { spacing: 4 Layout.fillWidth: true @@ -28,7 +28,6 @@ ColumnLayout { Layout.bottomMargin: 8 } - ColumnLayout { spacing: 2 Layout.fillWidth: true @@ -67,7 +66,9 @@ ColumnLayout { z: 2 } - Avatar {} + Avatar { + } + } Rectangle { @@ -80,6 +81,7 @@ ColumnLayout { TextInput { id: profileImageInput + anchors.fill: parent anchors.leftMargin: 12 anchors.rightMargin: 12 @@ -96,25 +98,27 @@ ColumnLayout { onTextChanged: { Settings.settings.profileImage = text; } + MouseArea { anchors.fill: parent cursorShape: Qt.IBeamCursor onClicked: profileImageInput.forceActiveFocus() } + } + } + } + } + } Rectangle { + Layout.fillWidth: true Layout.topMargin: 26 Layout.bottomMargin: 18 - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - } height: 1 color: Theme.outline opacity: 0.3 @@ -124,7 +128,6 @@ ColumnLayout { spacing: 4 Layout.fillWidth: true - Text { text: "User Interface" font.pixelSize: 18 @@ -133,7 +136,6 @@ ColumnLayout { Layout.bottomMargin: 8 } - ColumnLayout { spacing: 4 Layout.fillWidth: true @@ -160,10 +162,12 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: cornersSwitch + width: 52 height: 32 radius: 16 @@ -173,6 +177,7 @@ ColumnLayout { Rectangle { id: cornersThumb + width: 28 height: 28 radius: 14 @@ -187,7 +192,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -197,10 +204,12 @@ ColumnLayout { Settings.settings.showCorners = !Settings.settings.showCorners; } } - } - } - } + } + + } + + } ColumnLayout { spacing: 8 @@ -229,10 +238,12 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: dockSwitch + width: 52 height: 32 radius: 16 @@ -242,6 +253,7 @@ ColumnLayout { Rectangle { id: dockThumb + width: 28 height: 28 radius: 14 @@ -256,7 +268,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -266,10 +280,12 @@ ColumnLayout { Settings.settings.showDock = !Settings.settings.showDock; } } - } - } - } + } + + } + + } ColumnLayout { spacing: 8 @@ -298,10 +314,12 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: dimSwitch + width: 52 height: 32 radius: 16 @@ -311,6 +329,7 @@ ColumnLayout { Rectangle { id: dimThumb + width: 28 height: 28 radius: 14 @@ -325,7 +344,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -335,13 +356,18 @@ ColumnLayout { Settings.settings.dimPanels = !Settings.settings.dimPanels; } } + } + } + } + } Item { Layout.fillWidth: true Layout.fillHeight: true } -} \ No newline at end of file + +} diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index b159f78..4a2f2c4 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -97,19 +97,14 @@ ColumnLayout { } } - Rectangle { - Layout.topMargin: 26 - Layout.bottomMargin: 18 - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - } - height: 1 - color: Theme.outline - opacity: 0.3 - } - + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } ColumnLayout { spacing: 16 diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml index 926b4c3..497784d 100644 --- a/Widgets/SettingsWindow/Tabs/TimeWeather.qml +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -1,12 +1,13 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import qs.Settings import qs.Components +import qs.Settings import qs.Widgets.SettingsWindow.Tabs.Components ColumnLayout { id: root + spacing: 0 anchors.fill: parent anchors.margins: 0 @@ -16,7 +17,6 @@ ColumnLayout { Layout.preferredHeight: 0 } - ColumnLayout { spacing: 4 Layout.fillWidth: true @@ -29,7 +29,6 @@ ColumnLayout { Layout.bottomMargin: 8 } - ColumnLayout { spacing: 8 Layout.fillWidth: true @@ -57,10 +56,12 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: use12HourClockSwitch + width: 52 height: 32 radius: 16 @@ -70,6 +71,7 @@ ColumnLayout { Rectangle { id: use12HourClockThumb + width: 28 height: 28 radius: 14 @@ -84,7 +86,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -94,10 +98,12 @@ ColumnLayout { Settings.settings.use12HourClock = !Settings.settings.use12HourClock; } } - } - } - } + } + + } + + } ColumnLayout { spacing: 8 @@ -126,10 +132,12 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: reverseDayMonthSwitch + width: 52 height: 32 radius: 16 @@ -139,6 +147,7 @@ ColumnLayout { Rectangle { id: reverseDayMonthThumb + width: 28 height: 28 radius: 14 @@ -153,7 +162,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -163,25 +174,24 @@ ColumnLayout { Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; } } + } + } + } + } Rectangle { + Layout.fillWidth: true Layout.topMargin: 26 Layout.bottomMargin: 18 - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - } height: 1 color: Theme.outline opacity: 0.3 } - ColumnLayout { spacing: 4 Layout.fillWidth: true @@ -194,7 +204,6 @@ ColumnLayout { Layout.bottomMargin: 8 } - ColumnLayout { spacing: 8 Layout.fillWidth: true @@ -223,6 +232,7 @@ ColumnLayout { TextInput { id: cityInput + anchors.fill: parent anchors.leftMargin: 12 anchors.rightMargin: 12 @@ -237,7 +247,6 @@ ColumnLayout { selectByMouse: true activeFocusOnTab: true inputMethodHints: Qt.ImhNone - onTextChanged: { Settings.settings.weatherCity = text; } @@ -249,10 +258,12 @@ ColumnLayout { cityInput.forceActiveFocus(); } } - } - } - } + } + + } + + } ColumnLayout { spacing: 8 @@ -281,15 +292,21 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + + } + + UnitSelector { } - UnitSelector {} } + } + } Item { Layout.fillWidth: true Layout.fillHeight: true } -} \ No newline at end of file + +} diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index cb5d964..58b5571 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -122,264 +122,101 @@ ColumnLayout { } } + } - Rectangle { - Layout.topMargin: 26 - Layout.bottomMargin: 18 - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - } - height: 1 - color: Theme.outline - opacity: 0.3 + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Automation" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 } + // Random Wallpaper ColumnLayout { - spacing: 4 + spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 - Text { - text: "Automation" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - // Random Wallpaper - ColumnLayout { + RowLayout { spacing: 8 Layout.fillWidth: true - Layout.topMargin: 8 - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Random Wallpaper" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically select random wallpapers from the folder" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: randomWallpaperSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; - } - } - - } - - } - - } - - // Use Wallpaper Theme - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use Wallpaper Theme" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically adjust theme colors based on wallpaper" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: wallpaperThemeSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wallpaperThemeThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; - } - } - - } - - } - - } - - // Wallpaper Interval - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Wallpaper Interval" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "How often to change wallpapers automatically (in seconds)" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { + ColumnLayout { + spacing: 4 Layout.fillWidth: true Text { - text: Settings.settings.wallpaperInterval + " seconds" + text: "Random Wallpaper" font.pixelSize: 13 + font.bold: true color: Theme.textPrimary } - Item { + Text { + text: "Automatically select random wallpapers from the folder" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap Layout.fillWidth: true } } - Slider { - id: intervalSlider + Rectangle { + id: randomWallpaperSwitch - Layout.fillWidth: true - from: 10 - to: 900 - stepSize: 10 - value: Settings.settings.wallpaperInterval - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); - } + width: 52 + height: 32 + radius: 16 + color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline + border.width: 2 - background: Rectangle { - x: intervalSlider.leftPadding - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: intervalSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant + Rectangle { + id: randomWallpaperThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } - Rectangle { - width: intervalSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 } } - handle: Rectangle { - x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; + } } } @@ -388,16 +225,172 @@ ColumnLayout { } + // Use Wallpaper Theme + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use Wallpaper Theme" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Automatically adjust theme colors based on wallpaper" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: wallpaperThemeSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wallpaperThemeThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; + } + } + + } + + } + + } + + // Wallpaper Interval + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Wallpaper Interval" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "How often to change wallpapers automatically (in seconds)" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.wallpaperInterval + " seconds" + font.pixelSize: 13 + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + } + + Slider { + id: intervalSlider + + Layout.fillWidth: true + from: 10 + to: 900 + stepSize: 10 + value: Settings.settings.wallpaperInterval + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.wallpaperInterval = Math.round(value); + } + + background: Rectangle { + x: intervalSlider.leftPadding + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: intervalSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: intervalSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + + } + + handle: Rectangle { + x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + } + + } + } Rectangle { + Layout.fillWidth: true Layout.topMargin: 26 Layout.bottomMargin: 18 - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - } height: 1 color: Theme.outline opacity: 0.3 From 5388260020d8d60401499ccba3a7a35f06898daf Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 20:11:33 -0400 Subject: [PATCH 12/43] Settings: some more refinments --- Widgets/SettingsWindow/Tabs/Display.qml | 2 +- Widgets/SettingsWindow/Tabs/Network.qml | 72 ++++++++++++++--------- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 11 +--- 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/Display.qml b/Widgets/SettingsWindow/Tabs/Display.qml index 85353a8..8ed677c 100644 --- a/Widgets/SettingsWindow/Tabs/Display.qml +++ b/Widgets/SettingsWindow/Tabs/Display.qml @@ -32,7 +32,7 @@ ColumnLayout { ColumnLayout { - spacing: 4 + spacing: 12 Layout.fillWidth: true Text { diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index 4a2f2c4..13ee16a 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -3,19 +3,17 @@ import QtQuick.Controls import QtQuick.Layouts import Quickshell import Quickshell.Bluetooth -import qs.Settings import qs.Components +import qs.Settings ColumnLayout { id: root + spacing: 24 - Component.onCompleted: { - - Quickshell.execDetached(["nmcli", "-t", "-f", "WIFI", "radio"]) + Quickshell.execDetached(["nmcli", "-t", "-f", "WIFI", "radio"]); } - ColumnLayout { spacing: 16 Layout.fillWidth: true @@ -53,20 +51,24 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: wifiSwitch + + property bool checked: Settings.settings.wifiEnabled + width: 52 height: 32 radius: 16 - property bool checked: Settings.settings.wifiEnabled color: checked ? Theme.accentPrimary : Theme.surfaceVariant border.color: checked ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { id: wifiThumb + width: 28 height: 28 radius: 14 @@ -81,30 +83,36 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: { - Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled - Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"]) + Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled; + Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"]); } } + } + } + } + } - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 0 + height: 1 + color: Theme.outline + opacity: 0.3 + } ColumnLayout { spacing: 16 @@ -143,20 +151,24 @@ ColumnLayout { wrapMode: Text.WordWrap Layout.fillWidth: true } + } Rectangle { id: bluetoothSwitch + + property bool checked: Settings.settings.bluetoothEnabled + width: 52 height: 32 radius: 16 - property bool checked: Settings.settings.bluetoothEnabled color: checked ? Theme.accentPrimary : Theme.surfaceVariant border.color: checked ? Theme.accentPrimary : Theme.outline border.width: 2 Rectangle { id: bluetoothThumb + width: 28 height: 28 radius: 14 @@ -171,7 +183,9 @@ ColumnLayout { duration: 200 easing.type: Easing.OutCubic } + } + } MouseArea { @@ -179,22 +193,26 @@ ColumnLayout { cursorShape: Qt.PointingHandCursor onClicked: { if (Bluetooth.defaultAdapter) { - Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled - Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled - if (Bluetooth.defaultAdapter.enabled) { - Bluetooth.defaultAdapter.discovering = true - } + Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled; + Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled; + if (Bluetooth.defaultAdapter.enabled) + Bluetooth.defaultAdapter.discovering = true; + } } } - } - } - } - } + } + + } + + } + + } Item { Layout.fillWidth: true Layout.fillHeight: true } -} \ No newline at end of file + +} diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index 58b5571..e2fe2c7 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -37,7 +37,7 @@ ColumnLayout { Layout.fillWidth: true Text { - text: "Wallpaper" + text: "Wallpaper Settings" font.pixelSize: 18 font.bold: true color: Theme.textPrimary @@ -48,14 +48,7 @@ ColumnLayout { ColumnLayout { spacing: 8 Layout.fillWidth: true - Layout.topMargin: 16 - - Text { - text: "Wallpaper Settings" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } + Layout.topMargin: 8 // Wallpaper Folder ColumnLayout { From 0b5f1cd9e57b05a6b87fba2fe023c1f6c567d373 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 20:25:26 -0400 Subject: [PATCH 13/43] Reworked the sidepanel file structure --- Bar/Bar.qml | 4 +- Widgets/SettingsWindow/Tabs/General.qml | 2 +- .../Panel => SidePanel}/BluetoothPanel.qml | 0 Widgets/{Sidebar => SidePanel}/Button.qml | 1 - .../{Sidebar/Panel => SidePanel}/Music.qml | 0 .../Panel => SidePanel}/PanelPopup.qml | 0 .../Panel => SidePanel}/PowerProfile.qml | 0 .../Panel => SidePanel}/QuickAccess.qml | 0 .../Panel => SidePanel}/SettingsIcon.qml | 0 .../Config => SidePanel}/SettingsModal.qml | 0 .../{Sidebar/Panel => SidePanel}/System.qml | 0 .../Panel => SidePanel}/SystemMonitor.qml | 0 .../{Sidebar/Panel => SidePanel}/Weather.qml | 2 +- .../Panel => SidePanel}/WifiPanel.qml | 0 .../Sidebar/Config/CollapsibleCategory.qml | 56 -- Widgets/Sidebar/Config/ProfileSettings.qml | 643 ---------------- Widgets/Sidebar/Config/WallpaperSettings.qml | 722 ------------------ Widgets/Sidebar/Config/WeatherSettings.qml | 275 ------- Widgets/Sidebar/Panel/SettingsModal.qml | 81 -- Widgets/Sidebar/Panel/WallpaperPanel.qml | 170 ----- 20 files changed, 4 insertions(+), 1952 deletions(-) rename Widgets/{Sidebar/Panel => SidePanel}/BluetoothPanel.qml (100%) rename Widgets/{Sidebar => SidePanel}/Button.qml (97%) rename Widgets/{Sidebar/Panel => SidePanel}/Music.qml (100%) rename Widgets/{Sidebar/Panel => SidePanel}/PanelPopup.qml (100%) rename Widgets/{Sidebar/Panel => SidePanel}/PowerProfile.qml (100%) rename Widgets/{Sidebar/Panel => SidePanel}/QuickAccess.qml (100%) rename Widgets/{Sidebar/Panel => SidePanel}/SettingsIcon.qml (100%) rename Widgets/{Sidebar/Config => SidePanel}/SettingsModal.qml (100%) rename Widgets/{Sidebar/Panel => SidePanel}/System.qml (100%) rename Widgets/{Sidebar/Panel => SidePanel}/SystemMonitor.qml (100%) rename Widgets/{Sidebar/Panel => SidePanel}/Weather.qml (99%) rename Widgets/{Sidebar/Panel => SidePanel}/WifiPanel.qml (100%) delete mode 100644 Widgets/Sidebar/Config/CollapsibleCategory.qml delete mode 100644 Widgets/Sidebar/Config/ProfileSettings.qml delete mode 100644 Widgets/Sidebar/Config/WallpaperSettings.qml delete mode 100644 Widgets/Sidebar/Config/WeatherSettings.qml delete mode 100644 Widgets/Sidebar/Panel/SettingsModal.qml delete mode 100644 Widgets/Sidebar/Panel/WallpaperPanel.qml diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 84cbbe2..a7b8a45 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -12,8 +12,8 @@ import qs.Services import qs.Settings import qs.Widgets import qs.Widgets.Notification -import qs.Widgets.Sidebar -import qs.Widgets.Sidebar.Panel +import qs.Widgets.SidePanel + // Main bar component - creates panels on selected monitors with widgets and corners Scope { diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index b03c1bd..d2e199b 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -156,7 +156,7 @@ ColumnLayout { } Text { - text: "Display rounded corners on screen edges" + text: "Display rounded corners" font.pixelSize: 12 color: Theme.textSecondary wrapMode: Text.WordWrap diff --git a/Widgets/Sidebar/Panel/BluetoothPanel.qml b/Widgets/SidePanel/BluetoothPanel.qml similarity index 100% rename from Widgets/Sidebar/Panel/BluetoothPanel.qml rename to Widgets/SidePanel/BluetoothPanel.qml diff --git a/Widgets/Sidebar/Button.qml b/Widgets/SidePanel/Button.qml similarity index 97% rename from Widgets/Sidebar/Button.qml rename to Widgets/SidePanel/Button.qml index d1e642d..ba38128 100644 --- a/Widgets/Sidebar/Button.qml +++ b/Widgets/SidePanel/Button.qml @@ -1,7 +1,6 @@ import QtQuick import Quickshell import qs.Settings -import qs.Widgets.Sidebar.Panel Item { id: buttonRoot diff --git a/Widgets/Sidebar/Panel/Music.qml b/Widgets/SidePanel/Music.qml similarity index 100% rename from Widgets/Sidebar/Panel/Music.qml rename to Widgets/SidePanel/Music.qml diff --git a/Widgets/Sidebar/Panel/PanelPopup.qml b/Widgets/SidePanel/PanelPopup.qml similarity index 100% rename from Widgets/Sidebar/Panel/PanelPopup.qml rename to Widgets/SidePanel/PanelPopup.qml diff --git a/Widgets/Sidebar/Panel/PowerProfile.qml b/Widgets/SidePanel/PowerProfile.qml similarity index 100% rename from Widgets/Sidebar/Panel/PowerProfile.qml rename to Widgets/SidePanel/PowerProfile.qml diff --git a/Widgets/Sidebar/Panel/QuickAccess.qml b/Widgets/SidePanel/QuickAccess.qml similarity index 100% rename from Widgets/Sidebar/Panel/QuickAccess.qml rename to Widgets/SidePanel/QuickAccess.qml diff --git a/Widgets/Sidebar/Panel/SettingsIcon.qml b/Widgets/SidePanel/SettingsIcon.qml similarity index 100% rename from Widgets/Sidebar/Panel/SettingsIcon.qml rename to Widgets/SidePanel/SettingsIcon.qml diff --git a/Widgets/Sidebar/Config/SettingsModal.qml b/Widgets/SidePanel/SettingsModal.qml similarity index 100% rename from Widgets/Sidebar/Config/SettingsModal.qml rename to Widgets/SidePanel/SettingsModal.qml diff --git a/Widgets/Sidebar/Panel/System.qml b/Widgets/SidePanel/System.qml similarity index 100% rename from Widgets/Sidebar/Panel/System.qml rename to Widgets/SidePanel/System.qml diff --git a/Widgets/Sidebar/Panel/SystemMonitor.qml b/Widgets/SidePanel/SystemMonitor.qml similarity index 100% rename from Widgets/Sidebar/Panel/SystemMonitor.qml rename to Widgets/SidePanel/SystemMonitor.qml diff --git a/Widgets/Sidebar/Panel/Weather.qml b/Widgets/SidePanel/Weather.qml similarity index 99% rename from Widgets/Sidebar/Panel/Weather.qml rename to Widgets/SidePanel/Weather.qml index 2317e9c..631d22a 100644 --- a/Widgets/Sidebar/Panel/Weather.qml +++ b/Widgets/SidePanel/Weather.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Layouts import QtQuick.Controls import qs.Settings -import "../../../Helpers/Weather.js" as WeatherHelper +import "../../Helpers/Weather.js" as WeatherHelper Rectangle { id: weatherRoot diff --git a/Widgets/Sidebar/Panel/WifiPanel.qml b/Widgets/SidePanel/WifiPanel.qml similarity index 100% rename from Widgets/Sidebar/Panel/WifiPanel.qml rename to Widgets/SidePanel/WifiPanel.qml diff --git a/Widgets/Sidebar/Config/CollapsibleCategory.qml b/Widgets/Sidebar/Config/CollapsibleCategory.qml deleted file mode 100644 index d0c2eb7..0000000 --- a/Widgets/Sidebar/Config/CollapsibleCategory.qml +++ /dev/null @@ -1,56 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import qs.Settings - -ColumnLayout { - property alias title: headerText.text - property bool expanded: false // Hidden by default - default property alias content: contentItem.children - - Rectangle { - Layout.fillWidth: true - height: 44 - radius: 12 - color: Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - RowLayout { - anchors.fill: parent - anchors.margins: 8 - spacing: 8 - Item { width: 2 } - Text { - id: headerText - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody - font.bold: true - color: Theme.textPrimary - } - Item { Layout.fillWidth: true } - Rectangle { - width: 32; height: 32 - color: "transparent" - Text { - anchors.centerIn: parent - text: expanded ? "expand_less" : "expand_more" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody - color: Theme.accentPrimary - } - } - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: expanded = !expanded - } - } - Item { height: 8 } - ColumnLayout { - id: contentItem - Layout.fillWidth: true - visible: expanded - spacing: 0 - } -} \ No newline at end of file diff --git a/Widgets/Sidebar/Config/ProfileSettings.qml b/Widgets/Sidebar/Config/ProfileSettings.qml deleted file mode 100644 index 3451e25..0000000 --- a/Widgets/Sidebar/Config/ProfileSettings.qml +++ /dev/null @@ -1,643 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Effects -import QtQuick.Controls -import Quickshell.Widgets -import qs.Components -import qs.Settings - -Rectangle { - id: profileSettingsCard - Layout.fillWidth: true - Layout.preferredHeight: 690 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - // Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - Text { - text: "settings" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - Text { - text: "Profile Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - // Profile Image Input Section - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Profile Image" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - // Profile image - Rectangle { - width: 48 - height: 48 - radius: 24 - - // Border - Rectangle { - anchors.fill: parent - color: "transparent" - radius: 24 - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 - z: 2 - } - - Avatar {} - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: profileImageInput - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.profileImage - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.profileImage = text; - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: profileImageInput.forceActiveFocus() - } - } - } - } - } - - // Show Active Window Icon Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Active Window Icon" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: activeWindowIconSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: activeWindowIconThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; - } - } - } - } - - // Show System Info In Bar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show System Info In Bar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: systemInfoSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: systemInfoThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; - } - } - } - } - - // Show Corners Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Corners" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: cornersSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: cornersThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showCorners = !Settings.settings.showCorners; - } - } - } - } - - // Show Taskbar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Taskbar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: taskbarSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: taskbarThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showTaskbar = !Settings.settings.showTaskbar; - } - } - } - } - - // Show Dock Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Dock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: dockSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: dockThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showDock ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showDock = !Settings.settings.showDock; - } - } - } - } - - // Show Media In Bar Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Show Media In Bar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: mediaSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: mediaThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; - } - } - } - } - - // Dim Windows Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Dim Desktop" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Rectangle { - id: dimSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: dimThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.dimPanels = !Settings.settings.dimPanels; - } - } - } - } - - // Visualizer Type Selection - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 16 - - Text { - text: "Visualizer Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: visualizerTypeComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["radial", "fire", "diamond"] - currentIndex: model.indexOf(Settings.settings.visualizerType) - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing - text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1) - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: visualizerTypeComboBox.width - width - 12 - y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: visualizerTypeComboBox.height - width: visualizerTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null - currentIndex: visualizerTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - } - - delegate: ItemDelegate { - width: visualizerTypeComboBox.width - contentItem: Text { - text: modelData.charAt(0).toUpperCase() + modelData.slice(1) - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - highlighted: visualizerTypeComboBox.highlightedIndex === index - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - } - - onActivated: { - Settings.settings.visualizerType = model[index]; - } - } - } - - // Video Path Input Section - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 16 - - Text { - text: "Video Path" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: videoPathInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: videoPathInput - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.videoPath = text; - } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: videoPathInput.forceActiveFocus() - } - } - } - } - } -} diff --git a/Widgets/Sidebar/Config/WallpaperSettings.qml b/Widgets/Sidebar/Config/WallpaperSettings.qml deleted file mode 100644 index 2f5b66d..0000000 --- a/Widgets/Sidebar/Config/WallpaperSettings.qml +++ /dev/null @@ -1,722 +0,0 @@ -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import qs.Settings - -Rectangle { - id: wallpaperSettingsCard - - Layout.fillWidth: true - Layout.preferredHeight: Settings.settings.useSWWW ? 720 : 360 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - // Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - - Text { - text: "image" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - - Text { - text: "Wallpaper Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - // Wallpaper Path - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Wallpaper Path" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - // Folder Path Input - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: folderInput - - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.wallpaperFolder - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.wallpaperFolder = text; - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: folderInput.forceActiveFocus() - } - - } - - } - - } - - - - // Random Wallpaper Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Random Wallpaper" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: randomWallpaperSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; - } - } - - } - - } - - // Use Wallpaper Theme Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use Wallpaper Theme" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: wallpaperThemeSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wallpaperThemeThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; - } - } - - } - - } - - // Wallpaper Interval Setting - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Wallpaper Interval (seconds)" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.wallpaperInterval - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: intervalSlider - - Layout.fillWidth: true - from: 10 - to: 900 - stepSize: 10 - value: Settings.settings.wallpaperInterval - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); - } - - background: Rectangle { - x: intervalSlider.leftPadding - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: intervalSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: intervalSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - // Use SWWW Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: swwwSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: swwwThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - - } - - } - - // Resize Mode Setting - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - Text { - text: "Resize Mode" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: resizeComboBox - - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["no", "crop", "fit", "stretch"] - currentIndex: model.indexOf(Settings.settings.wallpaperResize) - onActivated: { - Settings.settings.wallpaperResize = model[index]; - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: resizeComboBox.indicator.width + resizeComboBox.spacing - text: resizeComboBox.displayText - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: resizeComboBox.width - width - 12 - y: resizeComboBox.topPadding + (resizeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: resizeComboBox.height - width: resizeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null - currentIndex: resizeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - - } - - delegate: ItemDelegate { - width: resizeComboBox.width - highlighted: resizeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - - } - - } - - } - - // Transition Type Setting - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - Text { - text: "Transition Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - ComboBox { - id: transitionTypeComboBox - - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] - currentIndex: model.indexOf(Settings.settings.transitionType) - onActivated: { - Settings.settings.transitionType = model[index]; - } - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: transitionTypeComboBox.indicator.width + transitionTypeComboBox.spacing - text: transitionTypeComboBox.displayText - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: transitionTypeComboBox.width - width - 12 - y: transitionTypeComboBox.topPadding + (transitionTypeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: Theme.textPrimary - } - - popup: Popup { - y: transitionTypeComboBox.height - width: transitionTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null - currentIndex: transitionTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surfaceVariant - border.color: Theme.outline - border.width: 1 - radius: 16 - } - - } - - delegate: ItemDelegate { - width: transitionTypeComboBox.width - highlighted: transitionTypeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" - } - - } - - } - - } - - // Transition FPS Setting - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Transition FPS" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.transitionFps - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: fpsSlider - - Layout.fillWidth: true - from: 30 - to: 500 - stepSize: 5 - value: Settings.settings.transitionFps - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionFps = Math.round(value); - } - - background: Rectangle { - x: fpsSlider.leftPadding - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: fpsSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: fpsSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - // Transition Duration Setting - ColumnLayout { - spacing: 12 - Layout.fillWidth: true - Layout.topMargin: 16 - visible: Settings.settings.useSWWW - - RowLayout { - Layout.fillWidth: true - - Text { - text: "Transition Duration (seconds)" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - Text { - text: Settings.settings.transitionDuration.toFixed(3) - font.pixelSize: 13 - color: Theme.textPrimary - } - - } - - Slider { - id: durationSlider - - Layout.fillWidth: true - from: 0.25 - to: 10 - stepSize: 0.05 - value: Settings.settings.transitionDuration - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionDuration = value; - } - - background: Rectangle { - x: durationSlider.leftPadding - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: durationSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: durationSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - } - -} diff --git a/Widgets/Sidebar/Config/WeatherSettings.qml b/Widgets/Sidebar/Config/WeatherSettings.qml deleted file mode 100644 index d7689dc..0000000 --- a/Widgets/Sidebar/Config/WeatherSettings.qml +++ /dev/null @@ -1,275 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import qs.Settings - -Rectangle { - id: weatherSettingsCard - Layout.fillWidth: true - Layout.preferredHeight: 320 - color: Theme.surface - radius: 18 - - ColumnLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - // Weather Settings Header - RowLayout { - Layout.fillWidth: true - spacing: 12 - - Text { - text: "wb_sunny" - font.family: "Material Symbols Outlined" - font.pixelSize: 20 - color: Theme.accentPrimary - } - - Text { - text: "Weather Settings" - font.family: Theme.fontFamily - font.pixelSize: 16 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - } - - // Weather City Setting - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "City" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: cityInput - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.weatherCity - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - focus: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhNone - - onTextChanged: { - Settings.settings.weatherCity = text; - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: { - cityInput.forceActiveFocus(); - } - } - } - } - } - - // Temperature Unit Setting - RowLayout { - spacing: 12 - Layout.fillWidth: true - - Text { - text: "Temperature Unit" - font.family: Theme.fontFamily - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: customSwitch - width: 52 - height: 32 - radius: 16 - color: Theme.accentPrimary - border.color: Theme.accentPrimary - border.width: 2 - - Rectangle { - id: thumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useFahrenheit ? customSwitch.width - width - 2 : 2 - - Text { - anchors.centerIn: parent - text: Settings.settings.useFahrenheit ? "\u00b0F" : "\u00b0C" - font.family: Theme.fontFamily - font.pixelSize: 12 - font.bold: true - color: Theme.textPrimary - } - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useFahrenheit = !Settings.settings.useFahrenheit; - } - } - } - - - } - - // Random Wallpaper Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Use 12 Hour Clock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: use12HourClockSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.use12HourClock = !Settings.settings.use12HourClock; - } - } - } - } - - // Reverse Day Month Setting - RowLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "US Style Date" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - // Custom Material 3 Switch - Rectangle { - id: reverseDayMonthSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: reverseDayMonthThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; - } - } - } - } - } -} diff --git a/Widgets/Sidebar/Panel/SettingsModal.qml b/Widgets/Sidebar/Panel/SettingsModal.qml deleted file mode 100644 index 98f88c0..0000000 --- a/Widgets/Sidebar/Panel/SettingsModal.qml +++ /dev/null @@ -1,81 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import Quickshell -import Quickshell.Wayland -import qs.Settings -import qs.Services -import qs.Widgets.SettingsWindow -import qs.Components - -PanelWindow { - id: settingsModal - implicitWidth: 480 - implicitHeight: 780 - visible: false - color: "transparent" - anchors.top: true - anchors.right: true - margins.right: 0 - margins.top: 0 - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - - // Property to track the settings window instance - property var settingsWindow: null - - // Function to open the modal and initialize temp values - function openSettings() { - if (!settingsWindow) { - // Create new window - settingsWindow = settingsComponent.createObject(null); // No parent to avoid dependency issues - if (settingsWindow) { - settingsWindow.visible = true; - // Handle window closure - settingsWindow.visibleChanged.connect(function() { - if (settingsWindow && !settingsWindow.visible) { - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.destroy(); - } - }); - } - } else if (settingsWindow.visible) { - // Close and destroy window - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.visible = false; - windowToDestroy.destroy(); - } - } - - // Function to close the modal and release focus - function closeSettings() { - if (settingsWindow) { - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.visible = false; - windowToDestroy.destroy(); - } - } - - Component { - id: settingsComponent - SettingsWindow {} - } - - // Clean up on destruction - Component.onDestruction: { - if (settingsWindow) { - var windowToDestroy = settingsWindow; - settingsWindow = null; - windowToDestroy.destroy(); - } - } - - // Refresh weather data when hidden - onVisibleChanged: { - if (!visible && typeof weather !== 'undefined' && weather !== null && weather.fetchCityWeather) { - weather.fetchCityWeather(); - } - } -} diff --git a/Widgets/Sidebar/Panel/WallpaperPanel.qml b/Widgets/Sidebar/Panel/WallpaperPanel.qml deleted file mode 100644 index be4c95f..0000000 --- a/Widgets/Sidebar/Panel/WallpaperPanel.qml +++ /dev/null @@ -1,170 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import Quickshell -import Quickshell.Io -import Quickshell.Widgets -import Quickshell.Wayland -import qs.Settings -import qs.Services - -PanelWindow { - id: wallpaperPanelModal - implicitWidth: 480 - implicitHeight: 780 - visible: false - color: "transparent" - anchors.top: true - anchors.right: true - margins.right: 0 - margins.top: 0 - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - - property var wallpapers: [] - - Connections { - target: WallpaperManager - function onWallpaperListChanged() { - wallpapers = WallpaperManager.wallpaperList - } - } - - onVisibleChanged: { - if (wallpaperPanel.visible) { - wallpapers = WallpaperManager.wallpaperList - } else { - wallpapers = [] - } - } - - Rectangle { - anchors.fill: parent - color: Theme.backgroundPrimary - radius: 20 - ColumnLayout { - anchors.fill: parent - anchors.margins: 32 - spacing: 0 - RowLayout { - Layout.fillWidth: true - spacing: 20 - Layout.preferredHeight: 48 - Text { - text: "image" - font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader - color: Theme.accentPrimary - } - Text { - text: "Wallpapers" - font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeHeader - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true - } - Rectangle { - width: 36 - height: 36 - radius: 18 - color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - Text { - anchors.centerIn: parent - text: "close" - font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody - color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - } - MouseArea { - id: closeButtonArea - anchors.fill: parent - hoverEnabled: true - onClicked: { - wallpaperPanel.visible = false; - } - cursorShape: Qt.PointingHandCursor - } - } - } - Rectangle { - Layout.fillWidth: true - height: 1 - color: Theme.outline - opacity: 0.12 - } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - anchors.topMargin: 16 - anchors.bottomMargin: 16 - anchors.leftMargin: 0 - anchors.rightMargin: 0 - anchors.margins: 0 - clip: true - ScrollView { - id: scrollView - anchors.fill: parent - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - GridView { - id: wallpaperGrid - anchors.fill: parent - cellWidth: Math.max(120, (scrollView.width / 3) - 12) - cellHeight: cellWidth * 0.6 - model: wallpapers - cacheBuffer: 64 - leftMargin: 8 - rightMargin: 8 - topMargin: 8 - bottomMargin: 8 - delegate: Item { - width: wallpaperGrid.cellWidth - 8 - height: wallpaperGrid.cellHeight - 8 - ClippingRectangle { - id: wallpaperItem - anchors.fill: parent - anchors.margins: 4 - color: Qt.darker(Theme.backgroundPrimary, 1.1) - radius: 12 - border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline - border.width: 2 - Image { - id: wallpaperImage - anchors.fill: parent - source: modelData - fillMode: Image.PreserveAspectCrop - asynchronous: true - cache: true - smooth: true - mipmap: true - - sourceSize.width: Math.min(width, 480) - sourceSize.height: Math.min(height, 270) - - opacity: (wallpaperImage.status == Image.Ready) ? 1.0 : 0.0 - Behavior on opacity { - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic - } - } - } - MouseArea { - anchors.fill: parent - hoverEnabled: true - onClicked: { - WallpaperManager.changeWallpaper(modelData); - } - } - } - } - } - } - } - } - } -} From 8c7f6e491d6f316871da918a99ce58c050a49577 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 20:41:39 -0400 Subject: [PATCH 14/43] Bugfix: PanelWithOverlay would close when clicking in the background of the panel. --- Bar/Modules/AudioDeviceSelector.qml | 186 ++++++++---- Bar/Modules/Calendar.qml | 67 +++-- Widgets/Notification/NotificationHistory.qml | 172 +++++++---- Widgets/SettingsWindow/SettingsWindow.qml | 5 + Widgets/SidePanel/PanelPopup.qml | 272 ++++++++++-------- .../SidePanel/{System.qml => PowerMenu.qml} | 155 +++++----- 6 files changed, 543 insertions(+), 314 deletions(-) rename Widgets/SidePanel/{System.qml => PowerMenu.qml} (97%) diff --git a/Bar/Modules/AudioDeviceSelector.qml b/Bar/Modules/AudioDeviceSelector.qml index 965acc9..5af3ffa 100644 --- a/Bar/Modules/AudioDeviceSelector.qml +++ b/Bar/Modules/AudioDeviceSelector.qml @@ -7,13 +7,67 @@ import qs.Settings PanelWithOverlay { id: ioSelector - signal panelClosed() + property int tabIndex: 0 property Item anchorItem: null + signal panelClosed() + + function sinkNodes() { + let nodes = Pipewire.nodes && Pipewire.nodes.values ? Pipewire.nodes.values.filter(function(n) { + return n.isSink && n.audio && n.isStream === false; + }) : []; + if (Pipewire.defaultAudioSink) + nodes = nodes.slice().sort(function(a, b) { + if (a.id === Pipewire.defaultAudioSink.id) + return -1; + + if (b.id === Pipewire.defaultAudioSink.id) + return 1; + + return 0; + }); + + return nodes; + } + + function sourceNodes() { + let nodes = Pipewire.nodes && Pipewire.nodes.values ? Pipewire.nodes.values.filter(function(n) { + return !n.isSink && n.audio && n.isStream === false; + }) : []; + if (Pipewire.defaultAudioSource) + nodes = nodes.slice().sort(function(a, b) { + if (a.id === Pipewire.defaultAudioSource.id) + return -1; + + if (b.id === Pipewire.defaultAudioSource.id) + return 1; + + return 0; + }); + + return nodes; + } + + Component.onCompleted: { + if (Pipewire.nodes && Pipewire.nodes.values) { + for (var i = 0; i < Pipewire.nodes.values.length; ++i) { + var n = Pipewire.nodes.values[i]; + } + } + } + Component.onDestruction: { + } + onVisibleChanged: { + if (!visible) + panelClosed(); + + } + // Bind all Pipewire nodes so their properties are valid PwObjectTracker { id: nodeTracker + objects: Pipewire.nodes } @@ -27,6 +81,11 @@ PanelWithOverlay { anchors.topMargin: 4 anchors.rightMargin: 4 + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + ColumnLayout { anchors.fill: parent anchors.margins: 16 @@ -37,48 +96,62 @@ PanelWithOverlay { Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter spacing: 0 - + Tabs { id: ioTabs - tabsModel: [ - { label: "Output", icon: "volume_up" }, - { label: "Input", icon: "mic" } - ] + + tabsModel: [{ + "label": "Output", + "icon": "volume_up" + }, { + "label": "Input", + "icon": "mic" + }] currentIndex: tabIndex onTabChanged: { tabIndex = currentIndex; } } + } // Add vertical space between tabs and entries - Item { height: 36; Layout.fillWidth: true } + Item { + height: 36 + Layout.fillWidth: true + } // Output Devices Flickable { id: sinkList + visible: tabIndex === 0 contentHeight: sinkColumn.height clip: true interactive: contentHeight > height width: parent.width height: 220 - ScrollBar.vertical: ScrollBar {} + ColumnLayout { id: sinkColumn + width: sinkList.width spacing: 6 + Repeater { model: ioSelector.sinkNodes() + Rectangle { width: parent.width height: 36 color: "transparent" radius: 6 + RowLayout { anchors.fill: parent anchors.margins: 6 spacing: 8 + Text { text: "volume_up" font.family: "Material Symbols Outlined" @@ -86,10 +159,12 @@ PanelWithOverlay { color: (Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary Layout.alignment: Qt.AlignVCenter } + ColumnLayout { Layout.fillWidth: true spacing: 1 Layout.maximumWidth: sinkList.width - 120 // Reserve space for the Set button + Text { text: modelData.nickname || modelData.description || modelData.name font.bold: true @@ -99,6 +174,7 @@ PanelWithOverlay { maximumLineCount: 1 Layout.fillWidth: true } + Text { text: modelData.description !== modelData.nickname ? modelData.description : "" font.pixelSize: 10 @@ -107,15 +183,19 @@ PanelWithOverlay { maximumLineCount: 1 Layout.fillWidth: true } + } + Rectangle { visible: Pipewire.preferredDefaultAudioSink !== modelData - width: 60; height: 20 + width: 60 + height: 20 radius: 4 color: Theme.accentPrimary border.color: Theme.accentPrimary border.width: 1 Layout.alignment: Qt.AlignVCenter + Text { anchors.centerIn: parent text: "Set" @@ -123,12 +203,15 @@ PanelWithOverlay { font.pixelSize: 10 font.bold: true } + MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: Pipewire.preferredDefaultAudioSink = modelData } + } + Text { text: "(Current)" visible: Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id @@ -136,37 +219,51 @@ PanelWithOverlay { font.pixelSize: 10 Layout.alignment: Qt.AlignVCenter } + } + } + } + } + + ScrollBar.vertical: ScrollBar { + } + } // Input Devices Flickable { id: sourceList + visible: tabIndex === 1 contentHeight: sourceColumn.height clip: true interactive: contentHeight > height width: parent.width height: 220 - ScrollBar.vertical: ScrollBar {} + ColumnLayout { id: sourceColumn + width: sourceList.width spacing: 6 + Repeater { model: ioSelector.sourceNodes() + Rectangle { width: parent.width height: 36 color: "transparent" radius: 6 + RowLayout { anchors.fill: parent anchors.margins: 6 spacing: 8 + Text { text: "mic" font.family: "Material Symbols Outlined" @@ -174,10 +271,12 @@ PanelWithOverlay { color: (Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary Layout.alignment: Qt.AlignVCenter } + ColumnLayout { Layout.fillWidth: true spacing: 1 Layout.maximumWidth: sourceList.width - 120 // Reserve space for the Set button + Text { text: modelData.nickname || modelData.description || modelData.name font.bold: true @@ -187,6 +286,7 @@ PanelWithOverlay { maximumLineCount: 1 Layout.fillWidth: true } + Text { text: modelData.description !== modelData.nickname ? modelData.description : "" font.pixelSize: 10 @@ -195,15 +295,19 @@ PanelWithOverlay { maximumLineCount: 1 Layout.fillWidth: true } + } + Rectangle { visible: Pipewire.preferredDefaultAudioSource !== modelData - width: 60; height: 20 + width: 60 + height: 20 radius: 4 color: Theme.accentPrimary border.color: Theme.accentPrimary border.width: 1 Layout.alignment: Qt.AlignVCenter + Text { anchors.centerIn: parent text: "Set" @@ -211,12 +315,15 @@ PanelWithOverlay { font.pixelSize: 10 font.bold: true } + MouseArea { anchors.fill: parent cursorShape: Qt.PointingHandCursor onClicked: Pipewire.preferredDefaultAudioSource = modelData } + } + Text { text: "(Current)" visible: Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id @@ -224,55 +331,25 @@ PanelWithOverlay { font.pixelSize: 10 Layout.alignment: Qt.AlignVCenter } + } + } + } + } - } - } - } - function sinkNodes() { - let nodes = Pipewire.nodes && Pipewire.nodes.values - ? Pipewire.nodes.values.filter(function(n) { - return n.isSink && n.audio && n.isStream === false; - }) - : []; - if (Pipewire.defaultAudioSink) { - nodes = nodes.slice().sort(function(a, b) { - if (a.id === Pipewire.defaultAudioSink.id) return -1; - if (b.id === Pipewire.defaultAudioSink.id) return 1; - return 0; - }); - } - return nodes; - } - function sourceNodes() { - let nodes = Pipewire.nodes && Pipewire.nodes.values - ? Pipewire.nodes.values.filter(function(n) { - return !n.isSink && n.audio && n.isStream === false; - }) - : []; - if (Pipewire.defaultAudioSource) { - nodes = nodes.slice().sort(function(a, b) { - if (a.id === Pipewire.defaultAudioSource.id) return -1; - if (b.id === Pipewire.defaultAudioSource.id) return 1; - return 0; - }); - } - return nodes; - } + ScrollBar.vertical: ScrollBar { + } - Component.onCompleted: { - if (Pipewire.nodes && Pipewire.nodes.values) { - for (var i = 0; i < Pipewire.nodes.values.length; ++i) { - var n = Pipewire.nodes.values[i]; } + } + } Connections { - target: Pipewire function onReadyChanged() { if (Pipewire.ready && Pipewire.nodes && Pipewire.nodes.values) { for (var i = 0; i < Pipewire.nodes.values.length; ++i) { @@ -280,15 +357,14 @@ PanelWithOverlay { } } } + function onDefaultAudioSinkChanged() { } + function onDefaultAudioSourceChanged() { } + + target: Pipewire } - Component.onDestruction: { - } - onVisibleChanged: { - if (!visible) panelClosed(); - } -} \ No newline at end of file +} diff --git a/Bar/Modules/Calendar.qml b/Bar/Modules/Calendar.qml index 55f1b83..9e9d106 100644 --- a/Bar/Modules/Calendar.qml +++ b/Bar/Modules/Calendar.qml @@ -1,11 +1,11 @@ +import "../../Helpers/Holidays.js" as Holidays import QtQuick import QtQuick.Controls import QtQuick.Layouts import Quickshell +import Quickshell.Wayland import qs.Components import qs.Settings -import Quickshell.Wayland -import "../../Helpers/Holidays.js" as Holidays PanelWithOverlay { id: calendarOverlay @@ -22,6 +22,11 @@ PanelWithOverlay { anchors.topMargin: 4 anchors.rightMargin: 4 + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + ColumnLayout { anchors.fill: parent anchors.margins: 16 @@ -60,13 +65,15 @@ PanelWithOverlay { calendar.month = newDate.getMonth(); } } + } DayOfWeekRow { Layout.fillWidth: true spacing: 0 - Layout.leftMargin: 8 // Align with grid + Layout.leftMargin: 8 // Align with grid Layout.rightMargin: 8 + delegate: Text { text: shortName color: Theme.textPrimary @@ -77,16 +84,11 @@ PanelWithOverlay { horizontalAlignment: Text.AlignHCenter width: 32 } + } MonthGrid { id: calendar - Layout.fillWidth: true - Layout.leftMargin: 8 - Layout.rightMargin: 8 - spacing: 0 - month: Time.date.getMonth() - year: Time.date.getFullYear() property var holidays: [] @@ -96,12 +98,19 @@ PanelWithOverlay { calendar.holidays = holidays; }); } + + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + spacing: 0 + month: Time.date.getMonth() + year: Time.date.getFullYear() onMonthChanged: updateHolidays() onYearChanged: updateHolidays() Component.onCompleted: updateHolidays() + // Optionally, update when the panel becomes visible Connections { - target: calendarOverlay function onVisibleChanged() { if (calendarOverlay.visible) { calendar.month = Time.date.getMonth(); @@ -109,29 +118,35 @@ PanelWithOverlay { calendar.updateHolidays(); } } + + target: calendarOverlay } delegate: Rectangle { - width: 32 - height: 32 - radius: 8 property var holidayInfo: calendar.holidays.filter(function(h) { var d = new Date(h.date); return d.getDate() === model.day && d.getMonth() === model.month && d.getFullYear() === model.year; }) property bool isHoliday: holidayInfo.length > 0 + + width: 32 + height: 32 + radius: 8 color: { if (model.today) return Theme.accentPrimary; + if (mouseArea2.containsMouse) return Theme.backgroundTertiary; + return "transparent"; } // Holiday dot indicator Rectangle { visible: isHoliday - width: 4; height: 4 + width: 4 + height: 4 radius: 4 color: Theme.accentTertiary anchors.top: parent.top @@ -145,7 +160,7 @@ PanelWithOverlay { anchors.centerIn: parent text: model.day color: model.today ? Theme.onAccent : Theme.textPrimary - opacity: model.month === calendar.month ? (mouseArea2.containsMouse ? 1.0 : 0.7) : 0.3 + opacity: model.month === calendar.month ? (mouseArea2.containsMouse ? 1 : 0.7) : 0.3 font.pixelSize: 13 font.family: Theme.fontFamily font.bold: model.today ? true : false @@ -153,6 +168,7 @@ PanelWithOverlay { MouseArea { id: mouseArea2 + anchors.fill: parent hoverEnabled: true onEntered: { @@ -167,21 +183,28 @@ PanelWithOverlay { onExited: holidayTooltip.tooltipVisible = false } - Behavior on color { - ColorAnimation { - duration: 150 - } - } - StyledTooltip { id: holidayTooltip + text: "" tooltipVisible: false targetItem: null delay: 100 } + + Behavior on color { + ColorAnimation { + duration: 150 + } + + } + } + } + } + } -} \ No newline at end of file + +} diff --git a/Widgets/Notification/NotificationHistory.qml b/Widgets/Notification/NotificationHistory.qml index 7e6277a..b0be4ba 100644 --- a/Widgets/Notification/NotificationHistory.qml +++ b/Widgets/Notification/NotificationHistory.qml @@ -1,61 +1,31 @@ import QtQuick +import QtQuick.Layouts import Quickshell import Quickshell.Io -import qs.Settings -import QtQuick.Layouts import qs.Components - +import qs.Settings PanelWithOverlay { id: notificationHistoryWin + property string historyFilePath: Settings.settingsDir + "notification_history.json" property bool hasUnread: notificationHistoryWinRect.hasUnread && !notificationHistoryWinRect.visible - function addToHistory(notification) { notificationHistoryWinRect.addToHistory(notification) } + + function addToHistory(notification) { + notificationHistoryWinRect.addToHistory(notification); + } + Rectangle { id: notificationHistoryWinRect - implicitWidth: 400 + property int maxPopupHeight: 800 property int minPopupHeight: 210 property int contentHeight: headerRow.height + historyList.contentHeight + 56 - 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 + signal unreadChanged(bool hasUnread) - ListModel { - id: historyModel - } - - FileView { - id: historyFileView - path: notificationHistoryWin.historyFilePath - blockLoading: true - printErrors: true - watchChanges: true - - JsonAdapter { - id: historyAdapter - property var notifications: [] - } - - onFileChanged: historyFileView.reload() - onLoaded: notificationHistoryWinRect.loadHistory() - onLoadFailed: function (error) { - historyAdapter.notifications = []; - historyFileView.writeAdapter(); - } - Component.onCompleted: if (path) - reload() - } - function updateHasUnread() { var unread = false; for (let i = 0; i < historyModel.count; ++i) { @@ -80,9 +50,11 @@ PanelWithOverlay { if (typeof n === 'object' && n !== null) { if (n.read === undefined) n.read = false; + // Mark as read if window is open if (notificationHistoryWinRect.visible) n.read = true; + historyModel.append(n); } } @@ -95,19 +67,19 @@ PanelWithOverlay { const count = Math.min(historyModel.count, maxHistory); for (let i = 0; i < count; ++i) { let obj = historyModel.get(i); - if (typeof obj === 'object' && obj !== null) { + if (typeof obj === 'object' && obj !== null) historyArray.push({ - id: obj.id, - appName: obj.appName, - summary: obj.summary, - body: obj.body, - timestamp: obj.timestamp, - read: obj.read === undefined ? false : obj.read + "id": obj.id, + "appName": obj.appName, + "summary": obj.summary, + "body": obj.body, + "timestamp": obj.timestamp, + "read": obj.read === undefined ? false : obj.read }); - } + } historyAdapter.notifications = historyArray; - Qt.callLater(function () { + Qt.callLater(function() { historyFileView.writeAdapter(); }); updateHasUnread(); @@ -116,12 +88,12 @@ PanelWithOverlay { function addToHistory(notification) { 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; - // Remove duplicate by id for (let i = 0; i < historyModel.count; ++i) { if (historyModel.get(i).id === notification.id) { @@ -129,11 +101,10 @@ PanelWithOverlay { break; } } - historyModel.insert(0, notification); - if (historyModel.count > maxHistory) historyModel.remove(maxHistory); + saveHistory(); } @@ -146,6 +117,7 @@ PanelWithOverlay { function formatTimestamp(ts) { 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'); @@ -155,6 +127,15 @@ PanelWithOverlay { return `${y}-${m}-${d} ${h}:${min}`; } + implicitWidth: 400 + 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 onVisibleChanged: { if (visible) { // Mark all as read when popup is opened @@ -167,9 +148,46 @@ PanelWithOverlay { } if (changed) saveHistory(); + } } + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + + ListModel { + id: historyModel + } + + FileView { + id: historyFileView + + path: notificationHistoryWin.historyFilePath + blockLoading: true + printErrors: true + watchChanges: true + onFileChanged: historyFileView.reload() + onLoaded: notificationHistoryWinRect.loadHistory() + onLoadFailed: function(error) { + historyAdapter.notifications = []; + historyFileView.writeAdapter(); + } + Component.onCompleted: { + if (path) { + reload(); + } + } + + JsonAdapter { + id: historyAdapter + + property var notifications: [] + } + + } + Rectangle { width: notificationHistoryWinRect.width height: notificationHistoryWinRect.height @@ -184,6 +202,7 @@ PanelWithOverlay { RowLayout { id: headerRow + spacing: 4 anchors.topMargin: 4 anchors.left: parent.left @@ -193,6 +212,7 @@ PanelWithOverlay { Layout.preferredHeight: 52 anchors.leftMargin: 16 anchors.rightMargin: 16 + Text { text: "Notification History" font.pixelSize: 18 @@ -200,11 +220,14 @@ PanelWithOverlay { color: Theme.textPrimary Layout.alignment: Qt.AlignVCenter } + Item { Layout.fillWidth: true } + Rectangle { id: clearAllButton + width: 90 height: 32 radius: 16 @@ -212,9 +235,11 @@ PanelWithOverlay { border.color: Theme.accentPrimary border.width: 1 Layout.alignment: Qt.AlignVCenter + Row { anchors.centerIn: parent spacing: 6 + Text { text: "delete_sweep" font.family: "Material Symbols Outlined" @@ -222,6 +247,7 @@ PanelWithOverlay { color: clearAllMouseArea.containsMouse ? Theme.onAccent : Theme.accentPrimary verticalAlignment: Text.AlignVCenter } + Text { text: "Clear" font.pixelSize: Theme.fontSizeSmall @@ -229,15 +255,20 @@ PanelWithOverlay { font.bold: true verticalAlignment: Text.AlignVCenter } + } + MouseArea { id: clearAllMouseArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: notificationHistoryWinRect.clearHistory() } + } + } Rectangle { @@ -261,29 +292,36 @@ PanelWithOverlay { radius: 20 z: 0 } + Rectangle { id: listContainer + anchors.fill: parent anchors.topMargin: 12 anchors.bottomMargin: 12 color: "transparent" clip: true + Column { anchors.fill: parent spacing: 0 ListView { id: historyList + width: parent.width height: Math.min(contentHeight, parent.height) spacing: 12 model: historyModel.count > 0 ? historyModel : placeholderModel clip: true + delegate: Item { width: parent.width height: notificationCard.implicitHeight + 12 + Rectangle { id: notificationCard + width: parent.width - 24 anchors.horizontalCenter: parent.horizontalCenter color: Theme.backgroundPrimary @@ -292,16 +330,22 @@ PanelWithOverlay { anchors.bottom: parent.bottom anchors.margins: 0 implicitHeight: contentColumn.implicitHeight + 20 + Column { id: contentColumn + anchors.fill: parent anchors.margins: 14 spacing: 6 + RowLayout { id: headerRow2 + spacing: 8 + Rectangle { id: iconBackground + width: 28 height: 28 radius: 20 @@ -309,6 +353,7 @@ PanelWithOverlay { border.color: Qt.darker(Theme.accentPrimary, 1.2) border.width: 1.2 Layout.alignment: Qt.AlignVCenter + Text { anchors.centerIn: parent text: model.appName ? model.appName.charAt(0).toUpperCase() : "?" @@ -317,11 +362,15 @@ PanelWithOverlay { font.bold: true color: Theme.backgroundPrimary } + } + Column { id: appInfoColumn + spacing: 0 Layout.alignment: Qt.AlignVCenter + Text { text: model.appName || "No Notifications" font.bold: true @@ -330,6 +379,7 @@ PanelWithOverlay { font.pixelSize: Theme.fontSizeSmall verticalAlignment: Text.AlignVCenter } + Text { visible: !model.isPlaceholder text: model.timestamp ? notificationHistoryWinRect.formatTimestamp(model.timestamp) : "" @@ -338,11 +388,15 @@ PanelWithOverlay { font.pixelSize: Theme.fontSizeCaption verticalAlignment: Text.AlignVCenter } + } + Item { Layout.fillWidth: true } + } + Text { text: model.summary || (model.isPlaceholder ? "You're all caught up!" : "") color: Theme.textSecondary @@ -351,6 +405,7 @@ PanelWithOverlay { width: parent.width wrapMode: Text.Wrap } + Text { text: model.body || (model.isPlaceholder ? "No notifications to show." : "") color: Theme.textDisabled @@ -359,12 +414,19 @@ PanelWithOverlay { width: parent.width wrapMode: Text.Wrap } + } + } + } + } + } + } + } Rectangle { @@ -375,14 +437,20 @@ PanelWithOverlay { ListModel { id: placeholderModel + ListElement { appName: "" summary: "" body: "" isPlaceholder: true } + } + } + } + } + } diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index b7d9cce..95b91d2 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -155,6 +155,11 @@ PanelWithOverlay { // Center the settings window on screen anchors.centerIn: parent + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + Rectangle { id: background diff --git a/Widgets/SidePanel/PanelPopup.qml b/Widgets/SidePanel/PanelPopup.qml index cc6dbe9..6119be8 100644 --- a/Widgets/SidePanel/PanelPopup.qml +++ b/Widgets/SidePanel/PanelPopup.qml @@ -3,23 +3,14 @@ import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Wayland +import qs.Components import qs.Settings import qs.Widgets.SettingsWindow -import qs.Components PanelWithOverlay { id: sidebarPopup - property var shell: null - // Trigger initial weather loading when component is completed - Component.onCompleted: { - // Load initial weather data after a short delay to ensure all components are ready - Qt.callLater(function() { - if (weather && weather.fetchCityWeather) { - weather.fetchCityWeather(); - } - }); - } + property var shell: null function showAt() { sidebarPopupRect.showAt(); @@ -37,18 +28,27 @@ PanelWithOverlay { sidebarPopupRect.hidePopup(); } - Rectangle { - id: sidebarPopupRect - implicitWidth: 500 - implicitHeight: 800 - visible: parent.visible - color: "transparent" - anchors.top: parent.top - anchors.right: parent.right + // Trigger initial weather loading when component is completed + Component.onCompleted: { + // Load initial weather data after a short delay to ensure all components are ready + Qt.callLater(function() { + if (weather && weather.fetchCityWeather) + weather.fetchCityWeather(); + + }); + } + + Rectangle { + // Access the shell's SettingsWindow instead of creating a new one + + id: sidebarPopupRect - property real slideOffset: width property bool isAnimating: false + property int leftPadding: 20 + property int bottomPadding: 20 + // Recording properties + property bool isRecording: false function showAt() { if (!sidebarPopup.visible) { @@ -59,24 +59,26 @@ PanelWithOverlay { slideAnim.running = true; if (weather) weather.startWeatherFetch(); + if (systemWidget) systemWidget.panelVisible = true; + if (quickAccessWidget) quickAccessWidget.panelVisible = true; + } } function hidePopup() { - if (shell && shell.settingsWindow && shell.settingsWindow.visible) { + if (shell && shell.settingsWindow && shell.settingsWindow.visible) shell.settingsWindow.visible = false; - } - if (wifiPanelLoader.active && wifiPanelLoader.item && wifiPanelLoader.item.visible) { + if (wifiPanelLoader.active && wifiPanelLoader.item && wifiPanelLoader.item.visible) wifiPanelLoader.item.visible = false; - } - if (bluetoothPanelLoader.active && bluetoothPanelLoader.item && bluetoothPanelLoader.item.visible) { + + if (bluetoothPanelLoader.active && bluetoothPanelLoader.item && bluetoothPanelLoader.item.visible) bluetoothPanelLoader.item.visible = false; - } + if (sidebarPopup.visible) { slideAnim.from = 0; slideAnim.to = width; @@ -84,37 +86,87 @@ PanelWithOverlay { } } + // Start screen recording using Quickshell.execDetached + 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 videoPath = Settings.settings.videoPath; + if (videoPath && !videoPath.endsWith("/")) + videoPath += "/"; + + var outputPath = videoPath + filename; + var command = "gpu-screen-recorder -w portal" + " -f " + Settings.settings.recordingFrameRate + " -a default_output" + " -k " + Settings.settings.recordingCodec + " -ac " + Settings.settings.audioCodec + " -q " + Settings.settings.recordingQuality + " -cursor " + (Settings.settings.showCursor ? "yes" : "no") + " -cr " + Settings.settings.colorRange + " -o " + outputPath; + Quickshell.execDetached(["sh", "-c", command]); + isRecording = true; + quickAccessWidget.isRecording = true; + } + + // Stop recording using Quickshell.execDetached + function stopRecording() { + Quickshell.execDetached(["sh", "-c", "pkill -SIGINT -f 'gpu-screen-recorder.*portal'"]); + // Optionally, force kill after a delay + var cleanupTimer = Qt.createQmlObject('import QtQuick; Timer { interval: 3000; running: true; repeat: false }', sidebarPopupRect); + cleanupTimer.triggered.connect(function() { + Quickshell.execDetached(["sh", "-c", "pkill -9 -f 'gpu-screen-recorder.*portal' 2>/dev/null || true"]); + cleanupTimer.destroy(); + }); + isRecording = false; + quickAccessWidget.isRecording = false; + } + + implicitWidth: 500 + implicitHeight: 800 + visible: parent.visible + color: "transparent" + anchors.top: parent.top + anchors.right: parent.right + // Clean up processes on destruction + Component.onDestruction: { + if (isRecording) + stopRecording(); + + } + + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + NumberAnimation { id: slideAnim + target: sidebarPopupRect property: "slideOffset" duration: 300 easing.type: Easing.OutCubic - onStopped: { if (sidebarPopupRect.slideOffset === sidebarPopupRect.width) { sidebarPopup.visible = false; - 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 @@ -126,68 +178,69 @@ PanelWithOverlay { Behavior on x { enabled: !sidebarPopupRect.isAnimating + NumberAnimation { duration: 300 easing.type: Easing.OutCubic } - } - } - // Access the shell's SettingsWindow instead of creating a new one + } + + } // LazyLoader for WifiPanel LazyLoader { id: wifiPanelLoader + loading: false - component: WifiPanel {} + + component: WifiPanel { + } + } // LazyLoader for BluetoothPanel LazyLoader { id: bluetoothPanelLoader + loading: false - component: BluetoothPanel {} + + component: BluetoothPanel { + } + } - - // SettingsIcon component SettingsIcon { id: settingsModal + onWeatherRefreshRequested: { - if (weather && weather.fetchCityWeather) { + if (weather && weather.fetchCityWeather) weather.fetchCityWeather(); - } + } } - - Item { anchors.fill: mainRectangle x: sidebarPopupRect.slideOffset - - Behavior on x { - enabled: !sidebarPopupRect.isAnimating - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic - } - } + Keys.onEscapePressed: sidebarPopupRect.hidePopup() ColumnLayout { anchors.fill: parent anchors.margins: 20 spacing: 16 - System { + PowerMenu { id: systemWidget + Layout.alignment: Qt.AlignHCenter z: 3 } Weather { id: weather + Layout.alignment: Qt.AlignHCenter z: 2 } @@ -204,8 +257,10 @@ PanelWithOverlay { SystemMonitor { id: systemMonitor + z: 2 } + } // Power profile, Wifi and Bluetooth row @@ -236,6 +291,7 @@ PanelWithOverlay { // Wifi button Rectangle { id: wifiButton + width: 36 height: 36 radius: 18 @@ -255,16 +311,17 @@ PanelWithOverlay { MouseArea { id: wifiButtonArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - if (!wifiPanelLoader.active) { + if (!wifiPanelLoader.active) wifiPanelLoader.loading = true; - } - if (wifiPanelLoader.item) { + + if (wifiPanelLoader.item) wifiPanelLoader.item.showAt(); - } + } } @@ -273,11 +330,13 @@ PanelWithOverlay { targetItem: wifiButtonArea tooltipVisible: wifiButtonArea.containsMouse } + } // Bluetooth button Rectangle { id: bluetoothButton + width: 36 height: 36 radius: 18 @@ -297,16 +356,17 @@ PanelWithOverlay { MouseArea { id: bluetoothButtonArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - if (!bluetoothPanelLoader.active) { + if (!bluetoothPanelLoader.active) bluetoothPanelLoader.loading = true; - } - if (bluetoothPanelLoader.item) { + + if (bluetoothPanelLoader.item) bluetoothPanelLoader.item.showAt(); - } + } } @@ -315,9 +375,13 @@ PanelWithOverlay { targetItem: bluetoothButtonArea tooltipVisible: bluetoothButtonArea.containsMouse } + } + } + } + } Item { @@ -326,101 +390,60 @@ PanelWithOverlay { // QuickAccess widget QuickAccess { + // 6 is the wallpaper tab index + id: quickAccessWidget + Layout.alignment: Qt.AlignHCenter Layout.topMargin: -16 z: 2 isRecording: sidebarPopupRect.isRecording - onRecordingRequested: { sidebarPopupRect.startRecording(); } - onStopRecordingRequested: { sidebarPopupRect.stopRecording(); } - - onRecordingStateMismatch: function (actualState) { + onRecordingStateMismatch: function(actualState) { isRecording = actualState; quickAccessWidget.isRecording = actualState; } - onSettingsRequested: { // Use the SettingsModal's openSettings function - if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) { + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) settingsModal.openSettings(); - } - } + } onWallpaperSelectorRequested: { // Use the SettingsModal's openSettings function with wallpaper tab (index 6) - if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) { - settingsModal.openSettings(6); // 6 is the wallpaper tab index - } + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) + settingsModal.openSettings(6); + } } + } - Keys.onEscapePressed: sidebarPopupRect.hidePopup() - } - // Recording properties - property bool isRecording: false + Behavior on x { + enabled: !sidebarPopupRect.isAnimating - // Start screen recording using Quickshell.execDetached - 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(); + NumberAnimation { + duration: 300 + easing.type: Easing.OutCubic + } - var filename = hours + "-" + minutes + "-" + day + "-" + month + "-" + year + ".mp4"; - var videoPath = Settings.settings.videoPath; - if (videoPath && !videoPath.endsWith("/")) { - videoPath += "/"; } - var outputPath = videoPath + filename; - var command = "gpu-screen-recorder -w portal" + - " -f " + Settings.settings.recordingFrameRate + - " -a default_output" + - " -k " + Settings.settings.recordingCodec + - " -ac " + Settings.settings.audioCodec + - " -q " + Settings.settings.recordingQuality + - " -cursor " + (Settings.settings.showCursor ? "yes" : "no") + - " -cr " + Settings.settings.colorRange + - " -o " + outputPath; - Quickshell.execDetached(["sh", "-c", command]); - isRecording = true; - quickAccessWidget.isRecording = true; - } - // Stop recording using Quickshell.execDetached - function stopRecording() { - Quickshell.execDetached(["sh", "-c", "pkill -SIGINT -f 'gpu-screen-recorder.*portal'"]); - // Optionally, force kill after a delay - var cleanupTimer = Qt.createQmlObject('import QtQuick; Timer { interval: 3000; running: true; repeat: false }', sidebarPopupRect); - cleanupTimer.triggered.connect(function () { - Quickshell.execDetached(["sh", "-c", "pkill -9 -f 'gpu-screen-recorder.*portal' 2>/dev/null || true"]); - cleanupTimer.destroy(); - }); - isRecording = false; - quickAccessWidget.isRecording = false; - } - - // Clean up processes on destruction - Component.onDestruction: { - if (isRecording) { - stopRecording(); - } } Loader { active: Settings.settings.showCorners anchors.fill: parent + sourceComponent: Item { Corners { id: sidebarCornerLeft + position: "bottomright" size: 1.1 fillColor: Theme.backgroundPrimary @@ -430,15 +453,19 @@ PanelWithOverlay { Behavior on offsetX { enabled: !sidebarPopupRect.isAnimating + NumberAnimation { duration: 300 easing.type: Easing.OutCubic } + } + } Corners { id: sidebarCornerBottom + position: "bottomright" size: 1.1 fillColor: Theme.backgroundPrimary @@ -448,13 +475,20 @@ PanelWithOverlay { Behavior on offsetX { enabled: !sidebarPopupRect.isAnimating + NumberAnimation { duration: 300 easing.type: Easing.OutCubic } + } + } + } + } + } + } diff --git a/Widgets/SidePanel/System.qml b/Widgets/SidePanel/PowerMenu.qml similarity index 97% rename from Widgets/SidePanel/System.qml rename to Widgets/SidePanel/PowerMenu.qml index 1ffe456..12d6da9 100644 --- a/Widgets/SidePanel/System.qml +++ b/Widgets/SidePanel/PowerMenu.qml @@ -1,26 +1,64 @@ import QtQuick -import QtQuick.Layouts import QtQuick.Controls import QtQuick.Effects +import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Widgets +import qs.Components +import qs.Helpers +import qs.Services import qs.Settings import qs.Widgets import qs.Widgets.LockScreen -import qs.Helpers -import qs.Services -import qs.Components Rectangle { id: systemWidget + + property string uptimeText: "--:--" + property bool panelVisible: false + + function logout() { + if (WorkspaceManager.isNiri) + logoutProcessNiri.running = true; + else if (WorkspaceManager.isHyprland) + logoutProcessHyprland.running = true; + else + console.warn("No supported compositor detected for logout"); + } + + function suspend() { + suspendProcess.running = true; + } + + function shutdown() { + shutdownProcess.running = true; + } + + function reboot() { + rebootProcess.running = true; + } + + function updateSystemInfo() { + uptimeProcess.running = true; + } + width: 440 height: 80 color: "transparent" anchors.horizontalCenterOffset: -2 + onPanelVisibleChanged: { + if (panelVisible) + updateSystemInfo(); + + } + Component.onCompleted: { + uptimeProcess.running = true; + } Rectangle { id: card + anchors.fill: parent color: Theme.surface radius: 18 @@ -30,19 +68,16 @@ Rectangle { anchors.margins: 18 spacing: 12 - RowLayout { Layout.fillWidth: true spacing: 12 - Rectangle { width: 48 height: 48 radius: 24 color: Theme.accentPrimary - Rectangle { anchors.fill: parent color: "transparent" @@ -52,10 +87,11 @@ Rectangle { z: 2 } - Avatar {} + Avatar { + } + } - ColumnLayout { spacing: 4 Layout.fillWidth: true @@ -74,16 +110,16 @@ Rectangle { font.pixelSize: 12 color: Theme.textSecondary } + } - Item { Layout.fillWidth: true } - Rectangle { id: systemButton + width: 32 height: 32 radius: 16 @@ -101,6 +137,7 @@ Rectangle { MouseArea { id: systemButtonArea + anchors.fill: parent cursorShape: Qt.PointingHandCursor hoverEnabled: true @@ -108,24 +145,30 @@ Rectangle { systemMenu.visible = !systemMenu.visible; } } + StyledTooltip { id: systemTooltip - text: "System" + + text: "Power Menu" targetItem: systemButton tooltipVisible: systemButtonArea.containsMouse } + } + } + } + } PanelWithOverlay { id: systemMenu + anchors.top: systemButton.bottom anchors.right: systemButton.right Rectangle { - width: 160 height: 220 color: Theme.surface @@ -136,17 +179,19 @@ Rectangle { z: 9999 anchors.top: parent.top anchors.right: parent.right - - anchors.rightMargin: 32 anchors.topMargin: systemButton.y + systemButton.height + 48 + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + ColumnLayout { anchors.fill: parent anchors.margins: 8 spacing: 4 - Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -172,10 +217,12 @@ Rectangle { color: lockButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } + } MouseArea { id: lockButtonArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor @@ -184,9 +231,9 @@ Rectangle { systemMenu.visible = false; } } + } - Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -211,10 +258,12 @@ Rectangle { color: suspendButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } + } MouseArea { id: suspendButtonArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor @@ -223,9 +272,9 @@ Rectangle { systemMenu.visible = false; } } + } - Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -251,10 +300,12 @@ Rectangle { color: rebootButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } + } MouseArea { id: rebootButtonArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor @@ -263,9 +314,9 @@ Rectangle { systemMenu.visible = false; } } + } - Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -290,10 +341,12 @@ Rectangle { color: logoutButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } + } MouseArea { id: logoutButtonArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor @@ -302,9 +355,9 @@ Rectangle { systemMenu.visible = false; } } + } - Rectangle { Layout.fillWidth: true Layout.preferredHeight: 36 @@ -329,10 +382,12 @@ Rectangle { color: shutdownButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } + } MouseArea { id: shutdownButtonArea + anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor @@ -341,96 +396,72 @@ Rectangle { systemMenu.visible = false; } } + } + } + } + } - - property string uptimeText: "--:--" - - Process { id: uptimeProcess + command: ["sh", "-c", "uptime | awk -F 'up ' '{print $2}' | awk -F ',' '{print $1}' | xargs"] running: false + stdout: StdioCollector { onStreamFinished: { uptimeText = this.text.trim(); uptimeProcess.running = false; } } + } Process { id: shutdownProcess + command: ["shutdown", "-h", "now"] running: false } Process { id: rebootProcess + command: ["reboot"] running: false } Process { id: suspendProcess + command: ["systemctl", "suspend"] running: false } Process { id: logoutProcessNiri + command: ["niri", "msg", "action", "quit", "--skip-confirmation"] running: false } Process { id: logoutProcessHyprland + command: ["hyprctl", "dispatch", "exit"] running: false } Process { id: logoutProcess + command: ["loginctl", "terminate-user", Quickshell.env("USER")] running: false } - function logout() { - if (WorkspaceManager.isNiri) { - logoutProcessNiri.running = true; - } else if (WorkspaceManager.isHyprland) { - logoutProcessHyprland.running = true; - } else { - - console.warn("No supported compositor detected for logout"); - } - } - - function suspend() { - suspendProcess.running = true; - } - - function shutdown() { - shutdownProcess.running = true; - } - - function reboot() { - rebootProcess.running = true; - } - - property bool panelVisible: false - - - onPanelVisibleChanged: { - if (panelVisible) { - updateSystemInfo(); - } - } - - Timer { interval: 60000 repeat: true @@ -438,16 +469,8 @@ Rectangle { onTriggered: updateSystemInfo() } - Component.onCompleted: { - uptimeProcess.running = true; - } - - function updateSystemInfo() { - uptimeProcess.running = true; - } - - LockScreen { id: lockScreen } + } From b7f103cc9997d9b3db54cfdfd3a9c505ba9994e7 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 20:45:07 -0400 Subject: [PATCH 15/43] Applauncher: fix clicking in the panel bg would close the panel. --- Bar/Modules/Applauncher.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index 8bbcc88..086fef7 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -210,6 +210,11 @@ PanelWithOverlay { root.selectedIndex = 0; } + // Prevent closing when clicking in the panel bg + MouseArea { + anchors.fill: parent + } + Rectangle { id: root width: 460 From 27059b9bac69e33c56c83d509a1354592151451d Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 21:10:14 -0400 Subject: [PATCH 16/43] WallpaperSelect: fix thumbnails rounded corner with a mask --- .../Tabs/Components/WallpaperSelector.qml | 63 +++++++++++++++---- 1 file changed, 50 insertions(+), 13 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml index ba96960..c1db484 100644 --- a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml @@ -1,12 +1,21 @@ import QtQuick import QtQuick.Controls +import QtQuick.Effects import QtQuick.Layouts -import qs.Settings import qs.Components import qs.Services +import qs.Settings Rectangle { id: wallpaperOverlay + + // Function to show the overlay and load wallpapers + function show() { + // Ensure wallpapers are loaded + WallpaperManager.loadWallpapers(); + wallpaperOverlay.visible = true; + } + anchors.fill: parent color: Theme.backgroundPrimary visible: false @@ -22,10 +31,11 @@ Rectangle { // Content area that stops event propagation MouseArea { + // Stop event propagation + anchors.fill: parent anchors.margins: 24 onClicked: { - // Stop event propagation } ColumnLayout { @@ -46,6 +56,7 @@ Rectangle { GridView { id: wallpaperGrid + anchors.fill: parent cellWidth: Math.max(120, (parent.width / 3) - 12) cellHeight: cellWidth * 0.6 @@ -62,8 +73,9 @@ Rectangle { Rectangle { id: wallpaperItem + anchors.fill: parent - anchors.margins: 4 + anchors.margins: 3 color: Theme.surface radius: 12 border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline @@ -71,25 +83,49 @@ Rectangle { Image { id: wallpaperImage + anchors.fill: parent - anchors.margins: 4 + anchors.margins: 2 source: modelData fillMode: Image.PreserveAspectCrop asynchronous: true cache: true smooth: true mipmap: true - sourceSize.width: Math.min(width, 480) sourceSize.height: Math.min(height, 270) + opacity: (wallpaperImage.status == Image.Ready) ? 1 : 0 + // Apply circular mask for rounded corners + layer.enabled: true - opacity: (wallpaperImage.status == Image.Ready) ? 1.0 : 0.0 Behavior on opacity { NumberAnimation { duration: 300 easing.type: Easing.OutCubic } + } + + layer.effect: MultiEffect { + maskEnabled: true + maskSource: mask + } + + } + + Item { + id: mask + + anchors.fill: wallpaperImage + layer.enabled: true + visible: false + + Rectangle { + width: wallpaperImage.width + height: wallpaperImage.height + radius: 12 + } + } MouseArea { @@ -101,18 +137,19 @@ Rectangle { wallpaperOverlay.visible = false; } } + } + } + } + } + } + } + } - // Function to show the overlay and load wallpapers - function show() { - // Ensure wallpapers are loaded - WallpaperManager.loadWallpapers(); - wallpaperOverlay.visible = true; - } -} \ No newline at end of file +} From 58c508e708a8aa051a3e0daf009f0a5f03373a9a Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 21:20:02 -0400 Subject: [PATCH 17/43] Bigger settings panel - does not work well in 1080p --- Widgets/SettingsWindow/SettingsWindow.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 95b91d2..f873504 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -148,8 +148,8 @@ PanelWithOverlay { Rectangle { id: settingsWindowRect - implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600 - implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400 + implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width * 2 / 3 : 600 + implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height * 2 / 3 : 400 visible: parent.visible color: "transparent" // Center the settings window on screen From 7cc8b6926c8457153c91adddb8b4accff3b8ef72 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 21:28:56 -0400 Subject: [PATCH 18/43] Settings: Tabs sidebar is proportional to the window size (20%) --- Widgets/SettingsWindow/SettingsWindow.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index f873504..05733a2 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -350,7 +350,7 @@ PanelWithOverlay { id: tabs color: Theme.surface - width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100 + width: parent.width * 0.2 height: settingsWindowRect.height topLeftRadius: 20 bottomLeftRadius: 20 From 68d7add47439778f1518f784b81b2a5ad25931e7 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Wed, 6 Aug 2025 21:31:00 -0400 Subject: [PATCH 19/43] Settings: Tabs sidebar at 25% width (looks better) --- Widgets/SettingsWindow/SettingsWindow.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 05733a2..309639c 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -350,7 +350,7 @@ PanelWithOverlay { id: tabs color: Theme.surface - width: parent.width * 0.2 + width: parent.width * 0.25 height: settingsWindowRect.height topLeftRadius: 20 bottomLeftRadius: 20 From 0b49aff13d479f8f55a5843035f55ef0b98b127a Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 15:38:27 +0200 Subject: [PATCH 20/43] Add scaling to everything, edit SidePanel --- Bar/Bar.qml | 10 +- Bar/Modules/ActiveWindow.qml | 4 +- Bar/Modules/Applauncher.qml | 14 +- Bar/Modules/AudioDeviceSelector.qml | 20 +- Bar/Modules/Bluetooth.qml | 14 +- Bar/Modules/Calendar.qml | 6 +- Bar/Modules/ClockWidget.qml | 2 +- Bar/Modules/CustomTrayMenu.qml | 8 +- Bar/Modules/Media.qml | 22 +- Bar/Modules/SettingsButton.qml | 2 +- Bar/Modules/SystemInfo.qml | 12 +- Bar/Modules/SystemTray.qml | 16 +- Bar/Modules/Taskbar.qml | 2 +- Bar/Modules/Wifi.qml | 24 +- Bar/Modules/Workspace.qml | 18 +- Components/Avatar.qml | 2 +- Components/CircularProgressBar.qml | 4 +- Components/CircularSpectrum.qml | 8 +- Components/Corners.qml | 6 +- Components/IconButton.qml | 4 +- Components/PillIndicator.qml | 8 +- Components/Spinner.qml | 3 +- Components/StyledTooltip.qml | 14 +- Components/Tabs.qml | 14 +- README.md | 2 +- Settings/Theme.qml | 20 + Widgets/Dock.qml | 4 +- Widgets/LockScreen/BatteryCharge.qml | 4 +- Widgets/LockScreen/LockScreen.qml | 90 +- Widgets/Notification/NotificationIcon.qml | 2 +- Widgets/SettingsWindow/SettingsWindow.qml | 535 +++--- Widgets/SettingsWindow/Tabs/About.qml | 36 +- Widgets/SettingsWindow/Tabs/Bar.qml | 746 ++++---- .../Tabs/Components/UnitSelector.qml | 18 +- .../Tabs/Components/WallpaperSelector.qml | 16 +- Widgets/SettingsWindow/Tabs/Display.qml | 588 +++---- Widgets/SettingsWindow/Tabs/General.qml | 568 +++--- Widgets/SettingsWindow/Tabs/Misc.qml | 188 +- Widgets/SettingsWindow/Tabs/Network.qml | 420 ++--- Widgets/SettingsWindow/Tabs/Recording.qml | 72 +- Widgets/SettingsWindow/Tabs/TimeWeather.qml | 477 ++--- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 1538 +++++++++-------- Widgets/SidePanel/Button.qml | 2 +- Widgets/SidePanel/Music.qml | 122 +- Widgets/SidePanel/PanelPopup.qml | 196 +-- Widgets/SidePanel/PowerMenu.qml | 156 +- Widgets/SidePanel/PowerProfile.qml | 32 +- Widgets/SidePanel/SettingsIcon.qml | 4 +- Widgets/SidePanel/SettingsModal.qml | 4 +- Widgets/SidePanel/SystemMonitor.qml | 42 +- Widgets/SidePanel/Weather.qml | 48 +- shell.qml | 14 + 52 files changed, 3138 insertions(+), 3043 deletions(-) diff --git a/Bar/Bar.qml b/Bar/Bar.qml index a7b8a45..b542def 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -48,7 +48,7 @@ Scope { id: barBackground width: parent.width - height: 36 + height: 36 * Theme.uiScale color: Theme.backgroundPrimary anchors.top: parent.top anchors.left: parent.left @@ -59,8 +59,8 @@ Scope { anchors.verticalCenter: barBackground.verticalCenter anchors.left: barBackground.left - anchors.leftMargin: 18 - spacing: 12 + anchors.leftMargin: 18 * Theme.uiScale + spacing: 12 * Theme.uiScale SystemInfo { anchors.verticalCenter: parent.verticalCenter @@ -93,8 +93,8 @@ Scope { anchors.verticalCenter: barBackground.verticalCenter anchors.right: barBackground.right - anchors.rightMargin: 18 - spacing: 12 + anchors.rightMargin: 18 * Theme.uiScale + spacing: 12 * Theme.uiScale SystemTray { id: systemTrayModule diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index e3d9023..206de29 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -17,7 +17,7 @@ PanelWindow { visible: Settings.settings.showActiveWindow && !activeWindowWrapper.finallyHidden implicitHeight: activeWindowTitleContainer.height implicitWidth: 0 - property int barHeight: 36 + property int barHeight: 36 * Theme.uiScale color: "transparent" function getIcon() { @@ -127,7 +127,7 @@ PanelWindow { Text { id: activeWindowTitle text: ToplevelManager?.activeToplevel?.title && ToplevelManager?.activeToplevel?.title.length > 60 ? ToplevelManager?.activeToplevel?.title.substring(0, 60) + "..." : ToplevelManager?.activeToplevel?.title || "" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary anchors.left: icon.right anchors.leftMargin: Settings.settings.showActiveWindowIcon ? 4 : 6 diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index 086fef7..d62e300 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -532,7 +532,7 @@ PanelWithOverlay { Text { text: "search" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader + font.pixelSize: Theme.fontSizeHeader * Theme.uiScale color: searchField.activeFocus ? Theme.accentPrimary : Theme.textSecondary verticalAlignment: Text.AlignVCenter Layout.alignment: Qt.AlignVCenter @@ -545,7 +545,7 @@ PanelWithOverlay { placeholderTextColor: Theme.textSecondary background: null font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody + font.pixelSize: Theme.fontSizeBody * Theme.uiScale Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter onTextChanged: root.updateFilter() @@ -689,7 +689,7 @@ PanelWithOverlay { visible: !modelData.isCalculator && !modelData.isClipboard && !modelData.isCommand && !parent.iconLoaded && modelData.type !== 'image' text: "broken_image" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader + font.pixelSize: Theme.fontSizeHeader * Theme.uiScale color: Theme.accentPrimary } } @@ -702,7 +702,7 @@ PanelWithOverlay { text: modelData.name color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textPrimary) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale font.bold: hovered || isSelected verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -716,7 +716,7 @@ PanelWithOverlay { (modelData.comment || modelData.genericName || "No description available") color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textSecondary : Theme.textSecondary) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption + font.pixelSize: Theme.fontSizeCaption * Theme.uiScale font.italic: !(modelData.comment || modelData.genericName) opacity: modelData.isClipboard ? 0.8 : modelData.isCommand ? 0.9 : ((modelData.comment || modelData.genericName) ? 1.0 : 0.6) elide: Text.ElideRight @@ -734,7 +734,7 @@ PanelWithOverlay { Text { text: modelData.isCalculator ? "content_copy" : "chevron_right" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: Theme.fontSizeBody * Theme.uiScale color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textSecondary) @@ -818,7 +818,7 @@ PanelWithOverlay { anchors.centerIn: parent text: "star" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale color: (parent.MouseArea.containsMouse || hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textDisabled) diff --git a/Bar/Modules/AudioDeviceSelector.qml b/Bar/Modules/AudioDeviceSelector.qml index 5af3ffa..590a82c 100644 --- a/Bar/Modules/AudioDeviceSelector.qml +++ b/Bar/Modules/AudioDeviceSelector.qml @@ -155,7 +155,7 @@ PanelWithOverlay { Text { text: "volume_up" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: (Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary Layout.alignment: Qt.AlignVCenter } @@ -168,7 +168,7 @@ PanelWithOverlay { Text { text: modelData.nickname || modelData.description || modelData.name font.bold: true - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: (Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary elide: Text.ElideRight maximumLineCount: 1 @@ -177,7 +177,7 @@ PanelWithOverlay { Text { text: modelData.description !== modelData.nickname ? modelData.description : "" - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale color: Theme.textSecondary elide: Text.ElideRight maximumLineCount: 1 @@ -200,7 +200,7 @@ PanelWithOverlay { anchors.centerIn: parent text: "Set" color: Theme.onAccent - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale font.bold: true } @@ -216,7 +216,7 @@ PanelWithOverlay { text: "(Current)" visible: Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id color: Theme.accentPrimary - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale Layout.alignment: Qt.AlignVCenter } @@ -267,7 +267,7 @@ PanelWithOverlay { Text { text: "mic" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: (Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary Layout.alignment: Qt.AlignVCenter } @@ -280,7 +280,7 @@ PanelWithOverlay { Text { text: modelData.nickname || modelData.description || modelData.name font.bold: true - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: (Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary elide: Text.ElideRight maximumLineCount: 1 @@ -289,7 +289,7 @@ PanelWithOverlay { Text { text: modelData.description !== modelData.nickname ? modelData.description : "" - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale color: Theme.textSecondary elide: Text.ElideRight maximumLineCount: 1 @@ -312,7 +312,7 @@ PanelWithOverlay { anchors.centerIn: parent text: "Set" color: Theme.onAccent - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale font.bold: true } @@ -328,7 +328,7 @@ PanelWithOverlay { text: "(Current)" visible: Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id color: Theme.accentPrimary - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale Layout.alignment: Qt.AlignVCenter } diff --git a/Bar/Modules/Bluetooth.qml b/Bar/Modules/Bluetooth.qml index e8ae996..fda61ec 100644 --- a/Bar/Modules/Bluetooth.qml +++ b/Bar/Modules/Bluetooth.qml @@ -45,7 +45,7 @@ Item { } } font.family: mouseAreaBluetooth.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: mouseAreaBluetooth.containsMouse ? Theme.accentPrimary : Theme.textPrimary } @@ -125,13 +125,13 @@ Item { Text { text: "bluetooth" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.accentPrimary } Text { text: "Bluetooth Devices" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary Layout.fillWidth: true @@ -180,7 +180,7 @@ Item { Text { text: modelData.connected ? "bluetooth" : "bluetooth_disabled" font.family: "Material Symbols Outlined" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) } @@ -199,7 +199,7 @@ Item { return deviceName; } color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale elide: Text.ElideRight Layout.fillWidth: true } @@ -217,7 +217,7 @@ Item { } } color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 + font.pixelSize: 11 * Theme.uiScale elide: Text.ElideRight Layout.fillWidth: true } @@ -265,7 +265,7 @@ Item { Text { text: "Scanning for devices..." - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary } diff --git a/Bar/Modules/Calendar.qml b/Bar/Modules/Calendar.qml index 9e9d106..baf7f9f 100644 --- a/Bar/Modules/Calendar.qml +++ b/Bar/Modules/Calendar.qml @@ -52,7 +52,7 @@ PanelWithOverlay { text: calendar.title color: Theme.textPrimary opacity: 0.7 - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.family: Theme.fontFamily font.bold: true } @@ -78,7 +78,7 @@ PanelWithOverlay { text: shortName color: Theme.textPrimary opacity: 0.8 - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.family: Theme.fontFamily font.bold: true horizontalAlignment: Text.AlignHCenter @@ -161,7 +161,7 @@ PanelWithOverlay { text: model.day color: model.today ? Theme.onAccent : Theme.textPrimary opacity: model.month === calendar.month ? (mouseArea2.containsMouse ? 1 : 0.7) : 0.3 - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.family: Theme.fontFamily font.bold: model.today ? true : false } diff --git a/Bar/Modules/ClockWidget.qml b/Bar/Modules/ClockWidget.qml index eba2afb..a539f45 100644 --- a/Bar/Modules/ClockWidget.qml +++ b/Bar/Modules/ClockWidget.qml @@ -15,7 +15,7 @@ Rectangle { text: Time.time font.family: Theme.fontFamily font.weight: Font.Bold - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale color: Theme.textPrimary anchors.centerIn: parent } diff --git a/Bar/Modules/CustomTrayMenu.qml b/Bar/Modules/CustomTrayMenu.qml index a64ac7b..9a994e3 100644 --- a/Bar/Modules/CustomTrayMenu.qml +++ b/Bar/Modules/CustomTrayMenu.qml @@ -128,7 +128,7 @@ PopupWindow { color: (modelData?.enabled ?? true) ? bg.hoverTextColor : Theme.textDisabled; text: modelData?.text ?? ""; font.family: Theme.fontFamily; - font.pixelSize: Theme.fontSizeSmall; + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale; verticalAlignment: Text.AlignVCenter; elide: Text.ElideRight; } @@ -145,7 +145,7 @@ PopupWindow { // Material Symbols Outlined chevron right for submenu text: modelData?.hasChildren ? "menu" : ""; font.family: "Material Symbols Outlined"; - font.pixelSize: 18; + font.pixelSize: 18 * Theme.uiScale; verticalAlignment: Text.AlignVCenter; visible: modelData?.hasChildren ?? false; color: Theme.textPrimary; @@ -362,7 +362,7 @@ PopupWindow { color: (modelData?.enabled ?? true) ? bg.hoverTextColor : Theme.textDisabled; text: modelData?.text ?? ""; font.family: Theme.fontFamily; - font.pixelSize: Theme.fontSizeSmall; + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale; verticalAlignment: Text.AlignVCenter; elide: Text.ElideRight; } @@ -378,7 +378,7 @@ PopupWindow { Text { text: modelData?.hasChildren ? "\uE5CC" : ""; font.family: "Material Symbols Outlined"; - font.pixelSize: 18; + font.pixelSize: 18 * Theme.uiScale; verticalAlignment: Text.AlignVCenter; visible: modelData?.hasChildren ?? false; color: Theme.textPrimary; diff --git a/Bar/Modules/Media.qml b/Bar/Modules/Media.qml index f2dbd1f..1aa46ab 100644 --- a/Bar/Modules/Media.qml +++ b/Bar/Modules/Media.qml @@ -10,7 +10,7 @@ import qs.Components Item { id: mediaControl width: visible ? mediaRow.width : 0 - height: 36 + height: 36 * Theme.uiScale visible: Settings.settings.showMediaInBar && MusicManager.currentPlayer RowLayout { @@ -20,8 +20,8 @@ Item { Item { id: albumArtContainer - width: 24 - height: 24 + width: 24 * Theme.uiScale + height: 24 * Theme.uiScale Layout.alignment: Qt.AlignVCenter // Circular spectrum visualizer @@ -29,8 +29,8 @@ Item { id: spectrum values: MusicManager.cavaValues anchors.centerIn: parent - innerRadius: 10 - outerRadius: 18 + innerRadius: 10 * Theme.uiScale + outerRadius: 18 * Theme.uiScale fillColor: Theme.accentPrimary strokeColor: Theme.accentPrimary strokeWidth: 0 @@ -40,10 +40,10 @@ Item { // Album art image Rectangle { id: albumArtwork - width: 20 - height: 20 + width: 20 * Theme.uiScale + height: 20 * Theme.uiScale anchors.centerIn: parent - radius: 12 // circle + radius: 12 * Theme.uiScale // circle color: Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) border.width: 1 @@ -79,7 +79,7 @@ Item { anchors.centerIn: parent text: "music_note" font.family: "Material Symbols Outlined" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.4) visible: !albumArt.visible } @@ -96,7 +96,7 @@ Item { anchors.centerIn: parent text: MusicManager.isPlaying ? "pause" : "play_arrow" font.family: "Material Symbols Outlined" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: "white" } } @@ -117,7 +117,7 @@ Item { text: MusicManager.trackTitle + " - " + MusicManager.trackArtist color: Theme.textPrimary font.family: Theme.fontFamily - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale elide: Text.ElideRight Layout.maximumWidth: 300 Layout.alignment: Qt.AlignVCenter diff --git a/Bar/Modules/SettingsButton.qml b/Bar/Modules/SettingsButton.qml index 28aafab..97f46c5 100644 --- a/Bar/Modules/SettingsButton.qml +++ b/Bar/Modules/SettingsButton.qml @@ -21,7 +21,7 @@ Item { anchors.centerIn: parent text: "settings" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: mouseArea.containsMouse ? Theme.accentPrimary : Theme.textPrimary } diff --git a/Bar/Modules/SystemInfo.qml b/Bar/Modules/SystemInfo.qml index 110deb1..3361f94 100644 --- a/Bar/Modules/SystemInfo.qml +++ b/Bar/Modules/SystemInfo.qml @@ -17,7 +17,7 @@ Row { Text { id: cpuUsageIcon font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: Theme.fontSizeBody * Theme.uiScale text: "speed" verticalAlignment: Text.AlignVCenter anchors.verticalCenter: parent.verticalCenter @@ -27,7 +27,7 @@ Row { Text { id: cpuUsageText font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale color: Theme.textPrimary text: Sysinfo.cpuUsageStr anchors.verticalCenter: parent.verticalCenter @@ -41,7 +41,7 @@ Row { spacing: 3 Text { font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: Theme.fontSizeBody * Theme.uiScale text: "thermometer" verticalAlignment: Text.AlignVCenter anchors.verticalCenter: parent.verticalCenter @@ -50,7 +50,7 @@ Row { Text { font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale color: Theme.textPrimary text: Sysinfo.cpuTempStr anchors.verticalCenter: parent.verticalCenter @@ -64,7 +64,7 @@ Row { spacing: 3 Text { font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: Theme.fontSizeBody * Theme.uiScale text: "memory" color: Theme.accentPrimary verticalAlignment: Text.AlignVCenter @@ -73,7 +73,7 @@ Row { Text { font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale color: Theme.textPrimary text: Sysinfo.memoryUsageStr anchors.verticalCenter: parent.verticalCenter diff --git a/Bar/Modules/SystemTray.qml b/Bar/Modules/SystemTray.qml index 7db68bf..50afb04 100644 --- a/Bar/Modules/SystemTray.qml +++ b/Bar/Modules/SystemTray.qml @@ -21,8 +21,8 @@ Row { Repeater { model: systemTray.items delegate: Item { - width: 24 - height: 24 + width: 24 * Theme.uiScale + height: 24 * Theme.uiScale // Hide Spotify icon, or adjust to your liking visible: modelData && modelData.id !== "spotify" property bool isHovered: trayMouseArea.containsMouse @@ -47,17 +47,17 @@ Row { Rectangle { anchors.centerIn: parent - width: 16 - height: 16 - radius: 6 + width: 16 * Theme.uiScale + height: 16 * Theme.uiScale + radius: 6 * Theme.uiScale color: "transparent" clip: true IconImage { id: trayIcon anchors.centerIn: parent - width: 16 - height: 16 + width: 16 * Theme.uiScale + height: 16 * Theme.uiScale smooth: false asynchronous: true backer.fillMode: Image.PreserveAspectFit @@ -122,7 +122,7 @@ Row { if (modelData.hasMenu && modelData.menu && trayMenu) { // Anchor the menu to the tray icon item (parent) and position it below the icon const menuX = (width / 2) - (trayMenu.width / 2); - const menuY = height + 20; + const menuY = height + 20 * Theme.uiScale; trayMenu.menu = modelData.menu; trayMenu.showAt(parent, menuX, menuY); } else diff --git a/Bar/Modules/Taskbar.qml b/Bar/Modules/Taskbar.qml index 792babc..1440d51 100644 --- a/Bar/Modules/Taskbar.qml +++ b/Bar/Modules/Taskbar.qml @@ -83,7 +83,7 @@ Item { visible: !appIcon.visible text: appButton.appId ? appButton.appId.charAt(0).toUpperCase() : "?" font.family: Theme.fontFamily - font.pixelSize: Math.max(10, Settings.settings.taskbarIconSize * 0.4375) + font.pixelSize: Math.max(10, Settings.settings.taskbarIconSize * 0.4375 * Theme.uiScale) font.bold: true color: appButton.isActive ? Theme.onAccent : Theme.textPrimary } diff --git a/Bar/Modules/Wifi.qml b/Bar/Modules/Wifi.qml index 13918f5..cae7100 100644 --- a/Bar/Modules/Wifi.qml +++ b/Bar/Modules/Wifi.qml @@ -51,7 +51,7 @@ Item { return connected ? network.signalIcon(parent.currentSignal) : "wifi_off" } font.family: mouseAreaWifi.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: mouseAreaWifi.containsMouse ? Theme.accentPrimary : Theme.textPrimary } @@ -120,13 +120,13 @@ Item { Text { text: "wifi" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.accentPrimary } Text { text: "WiFi Networks" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary Layout.fillWidth: true @@ -183,7 +183,7 @@ Item { Text { text: network.signalIcon(modelData.signal) font.family: "Material Symbols Outlined" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) } @@ -194,7 +194,7 @@ Item { Text { text: modelData.ssid || "Unknown Network" color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale elide: Text.ElideRight Layout.fillWidth: true } @@ -202,7 +202,7 @@ Item { Text { text: modelData.security && modelData.security !== "--" ? modelData.security : "Open" color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 + font.pixelSize: 11 * Theme.uiScale elide: Text.ElideRight Layout.fillWidth: true } @@ -211,7 +211,7 @@ Item { visible: network.connectStatusSsid === modelData.ssid && network.connectStatus === "error" && network.connectError.length > 0 text: network.connectError color: Theme.error - font.pixelSize: 11 + font.pixelSize: 11 * Theme.uiScale elide: Text.ElideRight Layout.fillWidth: true } @@ -234,7 +234,7 @@ Item { visible: network.connectStatus === "success" && !network.connectingSsid text: "check_circle" font.family: "Material Symbols Outlined" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale color: "#43a047" anchors.centerIn: parent } @@ -243,7 +243,7 @@ Item { visible: network.connectStatus === "error" && !network.connectingSsid text: "error" font.family: "Material Symbols Outlined" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale color: Theme.error anchors.centerIn: parent } @@ -253,7 +253,7 @@ Item { visible: modelData.connected text: "connected" color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary - font.pixelSize: 11 + font.pixelSize: 11 * Theme.uiScale } } @@ -309,7 +309,7 @@ Item { anchors.fill: parent anchors.margins: 12 text: passwordInput - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -364,7 +364,7 @@ Item { anchors.centerIn: parent text: "Connect" color: Theme.backgroundPrimary - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale font.bold: true } } diff --git a/Bar/Modules/Workspace.qml b/Bar/Modules/Workspace.qml index a658a6a..b9825b6 100644 --- a/Bar/Modules/Workspace.qml +++ b/Bar/Modules/Workspace.qml @@ -40,7 +40,7 @@ Item { return total; } - height: 36 + height: 36 * Theme.uiScale Component.onCompleted: { localWorkspaces.clear(); @@ -115,14 +115,14 @@ Item { Rectangle { id: workspaceBackground - width: parent.width - 15 - height: 26 + width: parent.width - 15 * Theme.uiScale + height: 26 * Theme.uiScale anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - radius: 12 + radius: 12 * Theme.uiScale color: Theme.surfaceVariant border.color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.1) - border.width: 1 + border.width: 1 * Theme.uiScale layer.enabled: true layer.effect: MultiEffect { shadowColor: "black" @@ -145,7 +145,7 @@ Item { model: localWorkspaces Item { id: workspacePillContainer - height: 12 + height: 12 * Theme.uiScale width: { if (model.isFocused) return 44; @@ -245,12 +245,12 @@ Item { Rectangle { id: pillBurst anchors.centerIn: workspacePillContainer - width: workspacePillContainer.width + 18 * root.masterProgress - height: workspacePillContainer.height + 18 * root.masterProgress + width: workspacePillContainer.width + 18 * root.masterProgress * Theme.uiScale + height: workspacePillContainer.height + 18 * root.masterProgress * Theme.uiScale radius: width / 2 color: "transparent" border.color: root.effectColor - border.width: 2 + 6 * (1.0 - root.masterProgress) + border.width: (2 + 6 * (1.0 - root.masterProgress)) * Theme.uiScale opacity: root.effectsActive && model.isFocused ? (1.0 - root.masterProgress) * 0.7 : 0 visible: root.effectsActive && model.isFocused z: 1 diff --git a/Components/Avatar.qml b/Components/Avatar.qml index 3a369bd..b2788aa 100644 --- a/Components/Avatar.qml +++ b/Components/Avatar.qml @@ -43,7 +43,7 @@ Item { anchors.centerIn: parent text: "person" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.onAccent visible: Settings.settings.profileImage === undefined || Settings.settings.profileImage === "" z: 0 diff --git a/Components/CircularProgressBar.qml b/Components/CircularProgressBar.qml index da03bfa..61ef17f 100644 --- a/Components/CircularProgressBar.qml +++ b/Components/CircularProgressBar.qml @@ -9,11 +9,11 @@ Rectangle { property int size: 80 property color backgroundColor: Theme.surfaceVariant property color progressColor: Theme.accentPrimary - property int strokeWidth: 6 + property int strokeWidth: 6 * Theme.uiScale property bool showText: true property string units: "%" property string text: Math.round(progress * 100) + units - property int textSize: 10 + property int textSize: 10 * Theme.uiScale property color textColor: Theme.textPrimary // Notch properties diff --git a/Components/CircularSpectrum.qml b/Components/CircularSpectrum.qml index 634aefd..635f550 100644 --- a/Components/CircularSpectrum.qml +++ b/Components/CircularSpectrum.qml @@ -4,11 +4,11 @@ import qs.Settings Item { id: root - property int innerRadius: 34 - property int outerRadius: 48 + property int innerRadius: 34 * Theme.uiScale + property int outerRadius: 48 * Theme.uiScale property color fillColor: "#fff" property color strokeColor: "#fff" - property int strokeWidth: 0 + property int strokeWidth: 0 * Theme.uiScale property var values: [] property int usableOuter: 48 @@ -24,7 +24,7 @@ Item { Rectangle { 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) + width: Math.max(2 * Theme.uiScale, (root.innerRadius * 2 * Math.PI) / root.values.length - 4 * Theme.uiScale) height: Settings.settings.visualizerType === "diamond" ? value * 2 * (usableOuter - root.innerRadius) : value * (usableOuter - root.innerRadius) radius: width / 2 color: root.fillColor diff --git a/Components/Corners.qml b/Components/Corners.qml index f41d14c..c8ac17e 100644 --- a/Components/Corners.qml +++ b/Components/Corners.qml @@ -6,11 +6,11 @@ Shape { id: root property string position: "topleft" // Corner position: topleft/topright/bottomleft/bottomright - property real size: 1.0 // Scale multiplier for entire corner + property real size: 1.0 * Theme.uiScale // Scale multiplier for entire corner property int concaveWidth: 100 * size property int concaveHeight: 60 * size - property int offsetX: -20 - property int offsetY: -20 + property int offsetX: -20 * Theme.uiScale + property int offsetY: -20 * Theme.uiScale property color fillColor: Theme.accentPrimary property int arcRadius: 20 * size diff --git a/Components/IconButton.qml b/Components/IconButton.qml index c6c0a85..6dc4b1a 100644 --- a/Components/IconButton.qml +++ b/Components/IconButton.qml @@ -19,7 +19,7 @@ MouseArea { Rectangle { anchors.fill: parent - radius: 8 + radius: 8 * Theme.uiScale color: root.hovering ? Theme.accentPrimary : "transparent" } Text { @@ -27,7 +27,7 @@ MouseArea { anchors.centerIn: parent text: root.icon font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: root.hovering ? Theme.onAccent : Theme.textPrimary horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter diff --git a/Components/PillIndicator.qml b/Components/PillIndicator.qml index afba173..addf5ec 100644 --- a/Components/PillIndicator.qml +++ b/Components/PillIndicator.qml @@ -13,8 +13,8 @@ Item { property color iconCircleColor: Theme.accentPrimary property color iconTextColor: Theme.backgroundPrimary property color collapsedIconColor: Theme.textPrimary - property int pillHeight: 22 - property int iconSize: 22 + property int pillHeight: 22 * Theme.uiScale + property int iconSize: 22 * Theme.uiScale property int pillPaddingHorizontal: 14 property bool autoHide: false @@ -47,7 +47,7 @@ Item { id: textItem anchors.centerIn: parent text: revealPill.text - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale font.family: Theme.fontFamily font.weight: Font.Bold color: textColor @@ -89,7 +89,7 @@ Item { Text { anchors.centerIn: parent font.family: showPill ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale text: revealPill.icon color: showPill ? iconTextColor : collapsedIconColor } diff --git a/Components/Spinner.qml b/Components/Spinner.qml index 9441c54..c902e17 100644 --- a/Components/Spinner.qml +++ b/Components/Spinner.qml @@ -1,4 +1,5 @@ import QtQuick +import qs.Settings Item { id: root @@ -6,7 +7,7 @@ Item { property bool running: false property color color: "white" property int size: 16 - property int strokeWidth: 2 + property int strokeWidth: 2 * Theme.uiScale property int duration: 1000 implicitWidth: size diff --git a/Components/StyledTooltip.qml b/Components/StyledTooltip.qml index ae444e7..90b6948 100644 --- a/Components/StyledTooltip.qml +++ b/Components/StyledTooltip.qml @@ -33,8 +33,8 @@ Window { } function _showNow() { - width = Math.max(50, tooltipText.implicitWidth + 24) - height = Math.max(50, tooltipText.implicitHeight + 16) + width = Math.max(50 * Theme.uiScale, tooltipText.implicitWidth + 24 * Theme.uiScale) + height = Math.max(50 * Theme.uiScale, tooltipText.implicitHeight + 16 * Theme.uiScale) if (!targetItem) return; @@ -75,10 +75,10 @@ Window { Rectangle { anchors.fill: parent - radius: 20 + radius: 20 * Theme.uiScale color: Theme.backgroundTertiary || "#222" border.color: Theme.outline || "#444" - border.width: 1 + border.width: 1 * Theme.uiScale opacity: 0.97 z: 1 } @@ -88,7 +88,7 @@ Window { text: tooltipWindow.text color: Theme.textPrimary font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -105,7 +105,7 @@ Window { } onTextChanged: { - width = Math.max(minimumWidth, tooltipText.implicitWidth + 24); - height = Math.max(minimumHeight, tooltipText.implicitHeight + 16); + width = Math.max(minimumWidth * Theme.uiScale, tooltipText.implicitWidth + 24 * Theme.uiScale); + height = Math.max(minimumHeight * Theme.uiScale, tooltipText.implicitHeight + 16 * Theme.uiScale); } } \ No newline at end of file diff --git a/Components/Tabs.qml b/Components/Tabs.qml index d3befea..319d2fe 100644 --- a/Components/Tabs.qml +++ b/Components/Tabs.qml @@ -17,8 +17,8 @@ Item { model: root.tabsModel delegate: Rectangle { id: tabWrapper - implicitHeight: tab.height - implicitWidth: 56 + implicitHeight: tab.height * Theme.uiScale + implicitWidth: 56 * Theme.uiScale color: "transparent" property bool hovered: false @@ -48,7 +48,7 @@ Item { Text { text: modelData.icon font.family: "Material Symbols Outlined" - font.pixelSize: 22 + font.pixelSize: 22 * Theme.uiScale color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : tabWrapper.hovered ? (Theme ? Theme.accentPrimary : "#7C3AED") : (Theme ? Theme.textSecondary : "#444") Layout.alignment: Qt.AlignCenter } @@ -56,7 +56,7 @@ Item { // Label Text { text: modelData.label - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale 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 @@ -64,9 +64,9 @@ Item { // Underline for active tab Rectangle { - width: 24 - height: 2 - radius: 1 + width: 24 * Theme.uiScale + height: 2 * Theme.uiScale + radius: 1 * Theme.uiScale color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : "transparent" Layout.alignment: Qt.AlignCenter } diff --git a/README.md b/README.md index 9f9e6a6..6713e84 100644 --- a/README.md +++ b/README.md @@ -226,4 +226,4 @@ Thank you to everyone who supports me and this project 💜! ## License -This project is licensed under the terms of the [MIT License](./LICENSE). +This project is licensed under the terms of the [MIT License](./LICENSE). \ No newline at end of file diff --git a/Settings/Theme.qml b/Settings/Theme.qml index 74f3afc..531018c 100644 --- a/Settings/Theme.qml +++ b/Settings/Theme.qml @@ -8,6 +8,23 @@ import qs.Settings Singleton { id: root + // Design screen width + readonly property int designScreenWidth: 1920 + + // Scaling dampening factor - reduces the scaling effect for higher resolutions + readonly property real scalingDampening: 0.2 + + // Automatic scaling based on screen width + function screenWidthRatio() { + // Get the primary screen width + if (Quickshell.screens && Quickshell.screens.length > 0) { + var rawRatio = Quickshell.screens[0].width / designScreenWidth + // Apply dampening to reduce scaling for higher resolutions + return Math.min(2.0, 1.0 + (rawRatio - 1.0) * scalingDampening) + } + return 1.0 + } + function applyOpacity(color, opacity) { return color.replace("#", "#" + opacity); } @@ -100,6 +117,9 @@ Singleton { property color shadow: applyOpacity(themeData.shadow, "B3") property color overlay: applyOpacity(themeData.overlay, "66") + // Global UI scale factor - automatically calculated based on screen width + property real uiScale: screenWidthRatio() + // Font Properties property string fontFamily: "Roboto" // Family for all text diff --git a/Widgets/Dock.qml b/Widgets/Dock.qml index 94b69f3..c2375da 100644 --- a/Widgets/Dock.qml +++ b/Widgets/Dock.qml @@ -302,7 +302,7 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter text: "close" font.family: "Material Symbols Outlined" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: Theme.textPrimary } @@ -310,7 +310,7 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter text: "Close" font.family: Theme.fontFamily - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: Theme.textPrimary } } diff --git a/Widgets/LockScreen/BatteryCharge.qml b/Widgets/LockScreen/BatteryCharge.qml index dd96c25..93100ac 100644 --- a/Widgets/LockScreen/BatteryCharge.qml +++ b/Widgets/LockScreen/BatteryCharge.qml @@ -58,7 +58,7 @@ Item { Text { text: batteryIcon() font.family: "Material Symbols Outlined" - font.pixelSize: 28 + font.pixelSize: 28 * Theme.uiScale color: charging ? Theme.accentPrimary : Theme.textSecondary verticalAlignment: Text.AlignVBottom } @@ -66,7 +66,7 @@ Item { Text { text: Math.round(percent) + "%" font.family: Theme.fontFamily - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale color: Theme.textSecondary verticalAlignment: Text.AlignVBottom } diff --git a/Widgets/LockScreen/LockScreen.qml b/Widgets/LockScreen/LockScreen.qml index 2ed50b0..fde716e 100644 --- a/Widgets/LockScreen/LockScreen.qml +++ b/Widgets/LockScreen/LockScreen.qml @@ -152,21 +152,21 @@ WlSessionLock { ColumnLayout { anchors.centerIn: parent spacing: 30 - width: Math.min(parent.width * 0.8, 400) + width: Math.min(parent.width * 0.8, 400 * Theme.uiScale) Rectangle { Layout.alignment: Qt.AlignHCenter - width: 80 - height: 80 - radius: 40 + width: 80 * Theme.uiScale + height: 80 * Theme.uiScale + radius: 40 * Theme.uiScale color: Theme.accentPrimary Rectangle { anchors.fill: parent color: "transparent" - radius: 40 + radius: 40 * Theme.uiScale border.color: Theme.accentPrimary - border.width: 3 + border.width: 3 * Theme.uiScale z: 2 } @@ -183,28 +183,28 @@ WlSessionLock { Layout.alignment: Qt.AlignHCenter text: Quickshell.env("USER") font.family: Theme.fontFamily - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale font.weight: Font.Medium color: Theme.textPrimary } Rectangle { Layout.fillWidth: true - height: 50 - radius: 25 + height: 50 * Theme.uiScale + radius: 25 * Theme.uiScale color: Theme.surface opacity: passwordInput.activeFocus ? 0.8 : 0.3 border.color: passwordInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 + border.width: 2 * Theme.uiScale TextInput { id: passwordInput anchors.fill: parent - anchors.margins: 15 + anchors.margins: 15 * Theme.uiScale verticalAlignment: TextInput.AlignVCenter horizontalAlignment: TextInput.AlignHCenter font.family: Theme.fontFamily - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: Theme.textPrimary echoMode: TextInput.Password passwordCharacter: "●" @@ -218,7 +218,7 @@ WlSessionLock { text: "Enter password..." color: Theme.textSecondary font.family: Theme.fontFamily - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale visible: !passwordInput.text && !passwordInput.activeFocus } @@ -238,9 +238,9 @@ WlSessionLock { id: errorMessageRect Layout.alignment: Qt.AlignHCenter width: parent.width * 0.8 - height: 44 + height: 44 * Theme.uiScale color: Theme.overlay - radius: 20 + radius: 20 * Theme.uiScale visible: lock.errorMessage !== "" Text { @@ -248,7 +248,7 @@ WlSessionLock { text: lock.errorMessage color: Theme.error font.family: Theme.fontFamily - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale opacity: 1 visible: lock.errorMessage !== "" } @@ -256,13 +256,13 @@ WlSessionLock { Rectangle { Layout.alignment: Qt.AlignHCenter - width: 120 - height: 44 - radius: 20 + width: 120 * Theme.uiScale + height: 44 * Theme.uiScale + radius: 20 * Theme.uiScale opacity: unlockButtonArea.containsMouse ? 0.8 : 0.5 color: unlockButtonArea.containsMouse ? Theme.accentPrimary : Theme.surface border.color: Theme.accentPrimary - border.width: 2 + border.width: 2 * Theme.uiScale enabled: !lock.authenticating Text { @@ -270,7 +270,7 @@ WlSessionLock { anchors.centerIn: parent text: lock.authenticating ? "..." : "Unlock" font.family: Theme.fontFamily - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale font.bold: true color: unlockButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } @@ -324,12 +324,12 @@ WlSessionLock { } Rectangle { - width: infoColumn.width + 32 - height: infoColumn.height + 8 + width: infoColumn.width + 32 * Theme.uiScale + height: infoColumn.height + 8 * Theme.uiScale color: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" anchors.horizontalCenter: parent.horizontalCenter - bottomLeftRadius: 20 - bottomRightRadius: 20 + bottomLeftRadius: 20 * Theme.uiScale + bottomRightRadius: 20 * Theme.uiScale ColumnLayout { id: infoColumn @@ -343,7 +343,7 @@ WlSessionLock { id: timeText text: Qt.formatDateTime(new Date(), "HH:mm") font.family: Theme.fontFamily - font.pixelSize: 48 + font.pixelSize: 48 * Theme.uiScale font.bold: true color: Theme.textPrimary horizontalAlignment: Text.AlignHCenter @@ -353,7 +353,7 @@ WlSessionLock { id: dateText text: Qt.formatDateTime(new Date(), "dddd, MMMM d") font.family: Theme.fontFamily - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: Theme.textSecondary opacity: 0.8 horizontalAlignment: Text.AlignHCenter @@ -369,7 +369,7 @@ WlSessionLock { Text { text: weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud" font.family: "Material Symbols Outlined" - font.pixelSize: 28 + font.pixelSize: 28 * Theme.uiScale color: Theme.accentPrimary verticalAlignment: Text.AlignVCenter } @@ -377,7 +377,7 @@ WlSessionLock { Text { 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: 18 + font.pixelSize: 18 * Theme.uiScale color: Theme.textSecondary verticalAlignment: Text.AlignVCenter } @@ -388,7 +388,7 @@ WlSessionLock { color: Theme.error visible: weatherError !== "" font.family: Theme.fontFamily - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter } @@ -430,12 +430,12 @@ WlSessionLock { spacing: 12 Rectangle { - width: 48 - height: 48 - radius: 24 + width: 48 * Theme.uiScale + height: 48 * Theme.uiScale + radius: 24 * Theme.uiScale color: shutdownArea.containsMouse ? Theme.error : "transparent" border.color: Theme.error - border.width: 1 + border.width: 1 * Theme.uiScale MouseArea { id: shutdownArea @@ -450,18 +450,18 @@ WlSessionLock { anchors.centerIn: parent text: "power_settings_new" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: shutdownArea.containsMouse ? Theme.onAccent : Theme.error } } Rectangle { - width: 48 - height: 48 - radius: 24 + width: 48 * Theme.uiScale + height: 48 * Theme.uiScale + radius: 24 * Theme.uiScale color: rebootArea.containsMouse ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary - border.width: 1 + border.width: 1 * Theme.uiScale MouseArea { id: rebootArea @@ -476,18 +476,18 @@ WlSessionLock { anchors.centerIn: parent text: "refresh" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: rebootArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } } Rectangle { - width: 48 - height: 48 - radius: 24 + width: 48 * Theme.uiScale + height: 48 * Theme.uiScale + radius: 24 * Theme.uiScale color: logoutArea.containsMouse ? Theme.accentSecondary : "transparent" border.color: Theme.accentSecondary - border.width: 1 + border.width: 1 * Theme.uiScale MouseArea { id: logoutArea @@ -502,7 +502,7 @@ WlSessionLock { anchors.centerIn: parent text: "exit_to_app" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: logoutArea.containsMouse ? Theme.onAccent : Theme.accentSecondary } } diff --git a/Widgets/Notification/NotificationIcon.qml b/Widgets/Notification/NotificationIcon.qml index 0a9556c..586d977 100644 --- a/Widgets/Notification/NotificationIcon.qml +++ b/Widgets/Notification/NotificationIcon.qml @@ -42,7 +42,7 @@ Item { } } font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale font.weight: { if (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread) { return Font.Bold; diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 309639c..625d579 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -148,8 +148,8 @@ PanelWithOverlay { Rectangle { id: settingsWindowRect - implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width * 2 / 3 : 600 - implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height * 2 / 3 : 400 + implicitWidth: Quickshell.screens.length > 0 ? Math.min(Quickshell.screens[0].width * 2 / 3, 1200) * Theme.uiScale : 600 * Theme.uiScale + implicitHeight: Quickshell.screens.length > 0 ? Math.min(Quickshell.screens[0].height * 2 / 3, 800) * Theme.uiScale : 400 * Theme.uiScale visible: parent.visible color: "transparent" // Center the settings window on screen @@ -160,14 +160,15 @@ PanelWithOverlay { anchors.fill: parent } + // Background rectangle Rectangle { id: background color: Theme.backgroundPrimary anchors.fill: parent - radius: 20 + radius: 20 * Theme.uiScale border.color: Theme.outline - border.width: 1 + border.width: 1 * Theme.uiScale MultiEffect { source: background @@ -184,10 +185,11 @@ PanelWithOverlay { Rectangle { id: settings + clip: true color: Theme.backgroundPrimary - topRightRadius: 20 - bottomRightRadius: 20 + topRightRadius: 20 * Theme.uiScale + bottomRightRadius: 20 * Theme.uiScale anchors { left: tabs.right @@ -197,285 +199,288 @@ PanelWithOverlay { margins: 12 } - Rectangle { - id: headerArea + Rectangle { + id: headerArea - height: 48 - color: "transparent" + height: 48 * Theme.uiScale + color: "transparent" - anchors { - top: parent.top - left: parent.left - right: parent.right - margins: 16 - } - - RowLayout { - anchors.fill: parent - spacing: 12 - - Text { - id: tabName - - text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : activeTabIndex === 1 ? "Bar" : activeTabIndex === 2 ? "Time & Weather" : activeTabIndex === 3 ? "Screen Recorder" : activeTabIndex === 4 ? "Network" : activeTabIndex === 5 ? "Display" : activeTabIndex === 6 ? "Wallpaper" : activeTabIndex === 7 ? "Misc" : activeTabIndex === 8 ? "About" : "General") - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.fillWidth: true + anchors { + top: parent.top + left: parent.left + right: parent.right + margins: 16 } - // Wallpaper Selection Button (only visible on Wallpaper tab) - Rectangle { - width: 160 - height: 32 - radius: 16 - color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - visible: activeTabIndex === 6 // Wallpaper tab index - - Row { - anchors.centerIn: parent - spacing: 6 - - Text { - text: "image" - font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 - color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - anchors.verticalCenter: parent.verticalCenter - } - - Text { - text: "Select Wallpaper" - font.pixelSize: 13 - font.bold: true - color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - anchors.verticalCenter: parent.verticalCenter - } - - } - - MouseArea { - id: wallpaperButtonArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - // Show the wallpaper selector - wallpaperSelector.show(); - } - } - - } - - Rectangle { - width: 32 - height: 32 - radius: 16 - color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 + RowLayout { + anchors.fill: parent + spacing: 12 * Theme.uiScale Text { - anchors.centerIn: parent - text: "close" - font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 18 - color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + id: tabName + + text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : activeTabIndex === 1 ? "Bar" : activeTabIndex === 2 ? "Time & Weather" : activeTabIndex === 3 ? "Screen Recorder" : activeTabIndex === 4 ? "Network" : activeTabIndex === 5 ? "Display" : activeTabIndex === 6 ? "Wallpaper" : activeTabIndex === 7 ? "Misc" : activeTabIndex === 8 ? "About" : "General") + font.pixelSize: 18 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + Layout.fillWidth: true } - MouseArea { - id: closeButtonArea + // Wallpaper Selection Button (only visible on Wallpaper tab) + Rectangle { + width: 160 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale + color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 * Theme.uiScale + visible: activeTabIndex === 6 // Wallpaper tab index - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: panelMain.dismiss() - } + Row { + anchors.centerIn: parent + spacing: 6 * Theme.uiScale - } - - } - - } - - Rectangle { - height: 1 - color: Theme.outline - opacity: 0.3 - - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - margins: 16 - } - - } - - Item { - id: settingsContainer - - anchors { - top: headerArea.bottom - left: parent.left - right: parent.right - bottom: parent.bottom - margins: 16 - topMargin: 32 - } - - // Simplified single loader approach - Loader { - id: settingsLoader - - anchors.fill: parent - sourceComponent: generalSettings - } - - // Wallpaper Selector Component - WallpaperSelector { - id: wallpaperSelector - - anchors.fill: parent - } - - } - - } - - Rectangle { - id: tabs - - color: Theme.surface - width: parent.width * 0.25 - height: settingsWindowRect.height - topLeftRadius: 20 - bottomLeftRadius: 20 - border.color: Theme.outline - border.width: 1 - - Column { - width: parent.width - spacing: 0 - topPadding: 8 - bottomPadding: 8 - - Repeater { - id: repeater - - model: [{ - "icon": "tune", - "text": "General" - }, { - "icon": "space_dashboard", - "text": "Bar" - }, { - "icon": "schedule", - "text": "Time & Weather" - }, { - "icon": "photo_camera", - "text": "Screen Recorder" - }, { - "icon": "wifi", - "text": "Network" - }, { - "icon": "monitor", - "text": "Display" - }, { - "icon": "wallpaper", - "text": "Wallpaper" - }, { - "icon": "settings_suggest", - "text": "Misc" - }, { - "icon": "info", - "text": "About" - }] - - delegate: Rectangle { - width: tabs.width - height: 48 - color: "transparent" - - RowLayout { - anchors.fill: parent - spacing: 8 - - Rectangle { - id: activeIndicator - - Layout.leftMargin: 8 - Layout.preferredWidth: 3 - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignVCenter - radius: 2 - color: Theme.accentPrimary - opacity: index === activeTabIndex ? 1 : 0 - - Behavior on opacity { - NumberAnimation { - duration: 200 - } + Text { + text: "image" + font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 16 * Theme.uiScale + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + anchors.verticalCenter: parent.verticalCenter + } + Text { + text: "Select Wallpaper" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + anchors.verticalCenter: parent.verticalCenter } } - Label { - id: icon + MouseArea { + id: wallpaperButtonArea - text: modelData.icon - font.family: "Material Symbols Outlined" - font.pixelSize: 24 - color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary - opacity: index === activeTabIndex ? 1 : 0.8 - Layout.leftMargin: 20 - Layout.preferredWidth: 24 - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignVCenter - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // Show the wallpaper selector + wallpaperSelector.show(); + } } - Label { - id: label - - text: modelData.text - font.pixelSize: 16 - color: index === activeTabIndex ? Theme.accentPrimary : (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary) - font.weight: index === activeTabIndex ? Font.DemiBold : (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal) - Layout.fillWidth: true - Layout.preferredHeight: 24 - Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: 4 - Layout.rightMargin: 16 - verticalAlignment: Text.AlignVCenter - } - - } - - MouseArea { - id: tabMouseArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - activeTabIndex = index; - loadComponentForTab(index); - } } Rectangle { - width: parent.width - height: 1 - color: Theme.outline - opacity: 0.6 - visible: index < (repeater.count - 1) - anchors.bottom: parent.bottom + width: 32 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale + color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 * Theme.uiScale + + Text { + anchors.centerIn: parent + text: "close" + font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" + font.pixelSize: 18 * Theme.uiScale + color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary + } + + MouseArea { + id: closeButtonArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: panelMain.dismiss() + } + + } + + } + + } + + Rectangle { + height: 1 * Theme.uiScale + color: Theme.outline + opacity: 0.3 + + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + margins: 16 + } + + } + + ColumnLayout { + id: settingsContainer + + anchors { + top: headerArea.bottom + left: parent.left + right: parent.right + bottom: parent.bottom + topMargin: 32 + } + + // Simplified single loader approach + Loader { + id: settingsLoader + + Layout.fillWidth: true + Layout.fillHeight: true + sourceComponent: generalSettings + active: true + } + + // Wallpaper Selector Component + WallpaperSelector { + id: wallpaperSelector + + anchors.fill: parent + } + + } + + } + + Rectangle { + id: tabs + + color: Theme.surface + width: parent.width * 0.25 + height: settingsWindowRect.height + topLeftRadius: 20 * Theme.uiScale + bottomLeftRadius: 20 * Theme.uiScale + border.color: Theme.outline + border.width: 1 * Theme.uiScale + + Column { + width: parent.width + spacing: 0 * Theme.uiScale + topPadding: 8 * Theme.uiScale + bottomPadding: 8 * Theme.uiScale + + Repeater { + id: repeater + + model: [{ + "icon": "tune", + "text": "General" + }, { + "icon": "space_dashboard", + "text": "Bar" + }, { + "icon": "schedule", + "text": "Time & Weather" + }, { + "icon": "photo_camera", + "text": "Screen Recorder" + }, { + "icon": "wifi", + "text": "Network" + }, { + "icon": "monitor", + "text": "Display" + }, { + "icon": "wallpaper", + "text": "Wallpaper" + }, { + "icon": "settings_suggest", + "text": "Misc" + }, { + "icon": "info", + "text": "About" + }] + + delegate: Rectangle { + width: tabs.width + height: 48 * Theme.uiScale + color: "transparent" + + RowLayout { + anchors.fill: parent + spacing: 8 * Theme.uiScale + + Rectangle { + id: activeIndicator + + Layout.leftMargin: 8 * Theme.uiScale + Layout.preferredWidth: 3 * Theme.uiScale + Layout.preferredHeight: 24 * Theme.uiScale + Layout.alignment: Qt.AlignVCenter + radius: 2 * Theme.uiScale + color: Theme.accentPrimary + opacity: index === activeTabIndex ? 1 : 0 + + Behavior on opacity { + NumberAnimation { + duration: 200 + } + + } + + } + + Label { + id: icon + + text: modelData.icon + font.family: "Material Symbols Outlined" + font.pixelSize: 24 * Theme.uiScale + color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary + opacity: index === activeTabIndex ? 1 : 0.8 + Layout.leftMargin: 20 * Theme.uiScale + Layout.preferredWidth: 24 * Theme.uiScale + Layout.preferredHeight: 24 * Theme.uiScale + Layout.alignment: Qt.AlignVCenter + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + + Label { + id: label + + text: modelData.text + font.pixelSize: 16 * Theme.uiScale + color: index === activeTabIndex ? Theme.accentPrimary : (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary) + font.weight: index === activeTabIndex ? Font.DemiBold : (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal) + Layout.fillWidth: true + Layout.preferredHeight: 24 * Theme.uiScale + Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter + Layout.leftMargin: 4 * Theme.uiScale + Layout.rightMargin: 16 * Theme.uiScale + verticalAlignment: Text.AlignVCenter + } + + } + + MouseArea { + id: tabMouseArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + activeTabIndex = index; + loadComponentForTab(index); + } + } + + Rectangle { + width: parent.width + height: 1 * Theme.uiScale + color: Theme.outline + opacity: 0.6 + visible: index < (repeater.count - 1) + anchors.bottom: parent.bottom + } + } } @@ -487,5 +492,3 @@ PanelWithOverlay { } } - -} diff --git a/Widgets/SettingsWindow/Tabs/About.qml b/Widgets/SettingsWindow/Tabs/About.qml index cdcdc11..bb9bca7 100644 --- a/Widgets/SettingsWindow/Tabs/About.qml +++ b/Widgets/SettingsWindow/Tabs/About.qml @@ -147,14 +147,18 @@ Item { } - Item { + ScrollView { anchors.fill: parent + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { id: mainLayout - anchors.left: parent.left - anchors.right: parent.right + width: parent.availableWidth anchors.top: parent.top spacing: 8 @@ -165,7 +169,7 @@ Item { Text { text: "Noctalia" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale font.bold: true color: Theme.textPrimary Layout.alignment: Qt.AlignCenter @@ -179,28 +183,28 @@ Item { Text { text: "Latest Version:" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: Theme.textSecondary Layout.alignment: Qt.AlignRight } Text { text: root.latestVersion - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: Theme.textPrimary font.bold: true } Text { text: "Installed Version:" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: Theme.textSecondary Layout.alignment: Qt.AlignRight } Text { text: root.currentVersion - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: Theme.textPrimary font.bold: true } @@ -242,7 +246,7 @@ Item { Text { text: "system_update" font.family: "Material Symbols Outlined" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary } @@ -250,7 +254,7 @@ Item { id: updateText text: "Download latest release" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary } @@ -271,7 +275,7 @@ Item { Text { text: "Description something something <.< I hate writing text..." - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: Theme.textSecondary Layout.alignment: Qt.AlignCenter Layout.topMargin: 24 @@ -298,14 +302,14 @@ Item { Text { text: "Contributors" - font.pixelSize: 18 + font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "(" + root.contributors.length + ")" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: Theme.textSecondary } @@ -384,7 +388,7 @@ Item { anchors.centerIn: parent text: "person" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary visible: !avatarImage.source || avatarImage.status !== Image.Ready } @@ -398,7 +402,7 @@ Item { Text { text: modelData.login || "Unknown" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary elide: Text.ElideRight Layout.fillWidth: true @@ -406,7 +410,7 @@ Item { Text { text: (modelData.contributions || 0) + " commits" - font.pixelSize: 11 + font.pixelSize: 11 * Theme.uiScale color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary } diff --git a/Widgets/SettingsWindow/Tabs/Bar.qml b/Widgets/SettingsWindow/Tabs/Bar.qml index 2148e16..874591e 100644 --- a/Widgets/SettingsWindow/Tabs/Bar.qml +++ b/Widgets/SettingsWindow/Tabs/Bar.qml @@ -4,377 +4,387 @@ import QtQuick.Layouts import qs.Settings import qs.Components -ColumnLayout { - id: root - spacing: 0 +ScrollView { anchors.fill: parent - anchors.margins: 0 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } - - + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Elements" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Active Window Icon" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display the icon of the currently focused window in the bar" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: activeWindowIconSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: activeWindowIconThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; - } - } - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Active Window" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display the title of the currently focused window below the bar" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: activeWindowSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: activeWindowThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showActiveWindow ? activeWindowSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showActiveWindow = !Settings.settings.showActiveWindow; - } - } - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show System Info" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display system information (CPU, RAM, etc.) in the bar" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: systemInfoSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: systemInfoThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; - } - } - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Taskbar" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display a taskbar showing currently open windows" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: taskbarSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: taskbarThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showTaskbar = !Settings.settings.showTaskbar; - } - } - } - } - } - - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Media" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display media controls and information in the bar" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: mediaSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: mediaThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; - } - } - } - } - } - } - - Item { + id: root + width: parent.availableWidth + spacing: 0 Layout.fillWidth: true Layout.fillHeight: true + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } + + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Elements" + font.pixelSize: 18 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Active Window Icon" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display the icon of the currently focused window in the bar" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: activeWindowIconSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: activeWindowIconThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Active Window" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display the title of the currently focused window below the bar" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: activeWindowSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: activeWindowThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showActiveWindow ? activeWindowSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showActiveWindow = !Settings.settings.showActiveWindow; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show System Info" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display system information (CPU, RAM, etc.) in the bar" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: systemInfoSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: systemInfoThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Taskbar" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display a taskbar showing currently open windows" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: taskbarSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: taskbarThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showTaskbar = !Settings.settings.showTaskbar; + } + } + } + } + } + + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Show Media" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display media controls and information in the bar" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + + Rectangle { + id: mediaSwitch + width: 52 + height: 32 + radius: 16 + color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: mediaThumb + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; + } + } + } + } + } + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } } } \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml index e8b9c8a..f4263ee 100644 --- a/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml @@ -6,21 +6,21 @@ import qs.Components Rectangle { id: root - width: 64 - height: 32 - radius: 16 + width: 64 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale color: Theme.surfaceVariant border.color: Theme.outline - border.width: 1 + border.width: 1 * Theme.uiScale property bool useFahrenheit: Settings.settings.useFahrenheit Rectangle { id: slider - width: parent.width / 2 - 4 - height: parent.height - 4 - radius: 14 + width: parent.width / 2 - 4 * Theme.uiScale + height: parent.height - 4 * Theme.uiScale + radius: 14 * Theme.uiScale color: Theme.accentPrimary x: 2 + (useFahrenheit ? parent.width / 2 : 0) y: 2 @@ -46,7 +46,7 @@ Rectangle { Text { anchors.centerIn: parent text: "°C" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: !useFahrenheit color: !useFahrenheit ? Theme.onAccent : Theme.textPrimary @@ -74,7 +74,7 @@ Rectangle { Text { anchors.centerIn: parent text: "°F" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: useFahrenheit color: useFahrenheit ? Theme.onAccent : Theme.textPrimary diff --git a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml index c1db484..9ccb6fb 100644 --- a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml @@ -58,7 +58,7 @@ Rectangle { id: wallpaperGrid anchors.fill: parent - cellWidth: Math.max(120, (parent.width / 3) - 12) + cellWidth: Math.max(120 * Theme.uiScale, (parent.width / 3) - 12 * Theme.uiScale) cellHeight: cellWidth * 0.6 model: WallpaperManager.wallpaperList cacheBuffer: 64 @@ -68,8 +68,8 @@ Rectangle { bottomMargin: 8 delegate: Item { - width: wallpaperGrid.cellWidth - 8 - height: wallpaperGrid.cellHeight - 8 + width: wallpaperGrid.cellWidth - 8 * Theme.uiScale + height: wallpaperGrid.cellHeight - 8 * Theme.uiScale Rectangle { id: wallpaperItem @@ -77,9 +77,9 @@ Rectangle { anchors.fill: parent anchors.margins: 3 color: Theme.surface - radius: 12 + radius: 12 * Theme.uiScale border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline - border.width: 2 + border.width: 2 * Theme.uiScale Image { id: wallpaperImage @@ -92,8 +92,8 @@ Rectangle { cache: true smooth: true mipmap: true - sourceSize.width: Math.min(width, 480) - sourceSize.height: Math.min(height, 270) + sourceSize.width: Math.min(width, 480 * Theme.uiScale) + sourceSize.height: Math.min(height, 270 * Theme.uiScale) opacity: (wallpaperImage.status == Image.Ready) ? 1 : 0 // Apply circular mask for rounded corners layer.enabled: true @@ -123,7 +123,7 @@ Rectangle { Rectangle { width: wallpaperImage.width height: wallpaperImage.height - radius: 12 + radius: 12 * Theme.uiScale } } diff --git a/Widgets/SettingsWindow/Tabs/Display.qml b/Widgets/SettingsWindow/Tabs/Display.qml index 8ed677c..bfe5aa6 100644 --- a/Widgets/SettingsWindow/Tabs/Display.qml +++ b/Widgets/SettingsWindow/Tabs/Display.qml @@ -5,361 +5,371 @@ import Quickshell import qs.Settings import qs.Components -ColumnLayout { - id: root - spacing: 0 +ScrollView { anchors.fill: parent - anchors.margins: 0 - - // Get list of available monitors/screens - property var monitors: Quickshell.screens || [] + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded - // Sorted monitors by name - property var sortedMonitors: { - let sorted = [...monitors]; - sorted.sort((a, b) => { - let nameA = a.name || "Unknown"; - let nameB = b.name || "Unknown"; - return nameA.localeCompare(nameB); - }); - return sorted; - } - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } - - ColumnLayout { - spacing: 12 - Layout.fillWidth: true + id: root + width: parent.availableWidth + spacing: 0 + anchors.top: parent.top + anchors.margins: 0 - Text { - text: "Monitor Selection" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 + // Get list of available monitors/screens + property var monitors: Quickshell.screens || [] + + // Sorted monitors by name + property var sortedMonitors: { + let sorted = [...monitors]; + sorted.sort((a, b) => { + let nameA = a.name || "Unknown"; + let nameB = b.name || "Unknown"; + return nameA.localeCompare(nameB); + }); + return sorted; + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 } ColumnLayout { - spacing: 8 + spacing: 12 Layout.fillWidth: true - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Bar Monitors" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Select which monitors to display the top panel/bar on" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } + Text { + text: "Monitor Selection" + font.pixelSize: 18 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 } - - Flow { - Layout.fillWidth: true + + ColumnLayout { spacing: 8 + Layout.fillWidth: true - Repeater { - model: root.sortedMonitors - delegate: Rectangle { - id: barCheckbox - property bool isChecked: false - - Component.onCompleted: { - // Initialize checkbox state from settings - let monitors = Settings.settings.barMonitors || []; - isChecked = monitors.includes(modelData.name); - } - - width: checkboxContent.implicitWidth + 16 - height: 32 - radius: 16 - color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant - border.color: isChecked ? Theme.accentPrimary : Theme.outline - border.width: 1 + RowLayout { + spacing: 8 + Layout.fillWidth: true - RowLayout { - id: checkboxContent - anchors.centerIn: parent - spacing: 6 + ColumnLayout { + spacing: 4 + Layout.fillWidth: true - Text { - text: barCheckbox.isChecked ? "check" : "" - font.family: "Material Symbols Outlined" - font.pixelSize: 14 - color: barCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary - visible: barCheckbox.isChecked - } - - Text { - text: modelData.name || "Unknown" - font.pixelSize: 12 - color: barCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary - } + Text { + text: "Bar Monitors" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary } - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - isChecked = !isChecked; - - // Update settings array when checkbox is toggled + Text { + text: "Select which monitors to display the top panel/bar on" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } + } + + + Flow { + Layout.fillWidth: true + spacing: 8 + + Repeater { + model: root.sortedMonitors + delegate: Rectangle { + id: barCheckbox + property bool isChecked: false + + Component.onCompleted: { + // Initialize checkbox state from settings let monitors = Settings.settings.barMonitors || []; - monitors = [...monitors]; // Create copy to trigger reactivity - - if (isChecked) { - if (!monitors.includes(modelData.name)) { - monitors.push(modelData.name); - } - } else { - monitors = monitors.filter(name => name !== modelData.name); + isChecked = monitors.includes(modelData.name); + } + + width: checkboxContent.implicitWidth + 16 + height: 32 + radius: 16 + color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: isChecked ? Theme.accentPrimary : Theme.outline + border.width: 1 + + RowLayout { + id: checkboxContent + anchors.centerIn: parent + spacing: 6 + + Text { + text: barCheckbox.isChecked ? "check" : "" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 * Theme.uiScale + color: barCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary + visible: barCheckbox.isChecked + } + + Text { + text: modelData.name || "Unknown" + font.pixelSize: 12 * Theme.uiScale + color: barCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + isChecked = !isChecked; + + // Update settings array when checkbox is toggled + let monitors = Settings.settings.barMonitors || []; + monitors = [...monitors]; // Create copy to trigger reactivity + + if (isChecked) { + if (!monitors.includes(modelData.name)) { + monitors.push(modelData.name); + } + } else { + monitors = monitors.filter(name => name !== modelData.name); + } + + Settings.settings.barMonitors = monitors; + console.log("Bar monitors updated:", JSON.stringify(monitors)); } - - Settings.settings.barMonitors = monitors; - console.log("Bar monitors updated:", JSON.stringify(monitors)); } } } } } - } - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { + ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 - ColumnLayout { - spacing: 4 + RowLayout { + spacing: 8 Layout.fillWidth: true - Text { - text: "Dock Monitors" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Select which monitors to display the application dock on" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap + ColumnLayout { + spacing: 4 Layout.fillWidth: true + + Text { + text: "Dock Monitors" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Select which monitors to display the application dock on" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } } } - } - Flow { - Layout.fillWidth: true - spacing: 8 + Flow { + Layout.fillWidth: true + spacing: 8 - Repeater { - model: root.sortedMonitors - delegate: Rectangle { - id: dockCheckbox - property bool isChecked: false - - Component.onCompleted: { - // Initialize with current settings - let monitors = Settings.settings.dockMonitors || []; - isChecked = monitors.includes(modelData.name); - } - - width: checkboxContent.implicitWidth + 16 - height: 32 - radius: 16 - color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant - border.color: isChecked ? Theme.accentPrimary : Theme.outline - border.width: 1 - - RowLayout { - id: checkboxContent - anchors.centerIn: parent - spacing: 6 - - Text { - text: dockCheckbox.isChecked ? "check" : "" - font.family: "Material Symbols Outlined" - font.pixelSize: 14 - color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary - visible: dockCheckbox.isChecked - } - - Text { - text: modelData.name || "Unknown" - font.pixelSize: 12 - color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - // Toggle state immediately for UI responsiveness - isChecked = !isChecked; - - // Update settings + Repeater { + model: root.sortedMonitors + delegate: Rectangle { + id: dockCheckbox + property bool isChecked: false + + Component.onCompleted: { + // Initialize with current settings let monitors = Settings.settings.dockMonitors || []; - monitors = [...monitors]; // Copy array - - if (isChecked) { - // Add to array if not already there - if (!monitors.includes(modelData.name)) { - monitors.push(modelData.name); - } - } else { - // Remove from array - monitors = monitors.filter(name => name !== modelData.name); + isChecked = monitors.includes(modelData.name); + } + + width: checkboxContent.implicitWidth + 16 + height: 32 + radius: 16 + color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: isChecked ? Theme.accentPrimary : Theme.outline + border.width: 1 + + RowLayout { + id: checkboxContent + anchors.centerIn: parent + spacing: 6 + + Text { + text: dockCheckbox.isChecked ? "check" : "" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 * Theme.uiScale + color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary + visible: dockCheckbox.isChecked + } + + Text { + text: modelData.name || "Unknown" + font.pixelSize: 12 * Theme.uiScale + color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // Toggle state immediately for UI responsiveness + isChecked = !isChecked; + + // Update settings + let monitors = Settings.settings.dockMonitors || []; + monitors = [...monitors]; // Copy array + + if (isChecked) { + // Add to array if not already there + if (!monitors.includes(modelData.name)) { + monitors.push(modelData.name); + } + } else { + // Remove from array + monitors = monitors.filter(name => name !== modelData.name); + } + + Settings.settings.dockMonitors = monitors; + console.log("Dock monitors updated:", JSON.stringify(monitors)); } - - Settings.settings.dockMonitors = monitors; - console.log("Dock monitors updated:", JSON.stringify(monitors)); } } } } } - } - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { + ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 - ColumnLayout { - spacing: 4 + RowLayout { + spacing: 8 Layout.fillWidth: true - Text { - text: "Notification Monitors" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Select which monitors to display system notifications on" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap + ColumnLayout { + spacing: 4 Layout.fillWidth: true + + Text { + text: "Notification Monitors" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Select which monitors to display system notifications on" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } } } - } - Flow { - Layout.fillWidth: true - spacing: 8 + Flow { + Layout.fillWidth: true + spacing: 8 - Repeater { - model: root.sortedMonitors - delegate: Rectangle { - id: notificationCheckbox - property bool isChecked: false - - Component.onCompleted: { - // Initialize with current settings - let monitors = Settings.settings.notificationMonitors || []; - isChecked = monitors.includes(modelData.name); - } - - width: checkboxContent.implicitWidth + 16 - height: 32 - radius: 16 - color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant - border.color: isChecked ? Theme.accentPrimary : Theme.outline - border.width: 1 - - RowLayout { - id: checkboxContent - anchors.centerIn: parent - spacing: 6 - - Text { - text: notificationCheckbox.isChecked ? "check" : "" - font.family: "Material Symbols Outlined" - font.pixelSize: 14 - color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary - visible: notificationCheckbox.isChecked - } - - Text { - text: modelData.name || "Unknown" - font.pixelSize: 12 - color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary - } - } - - MouseArea { - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - // Toggle state immediately for UI responsiveness - isChecked = !isChecked; - - // Update settings + Repeater { + model: root.sortedMonitors + delegate: Rectangle { + id: notificationCheckbox + property bool isChecked: false + + Component.onCompleted: { + // Initialize with current settings let monitors = Settings.settings.notificationMonitors || []; - monitors = [...monitors]; // Copy array - - if (isChecked) { - // Add to array if not already there - if (!monitors.includes(modelData.name)) { - monitors.push(modelData.name); - } - } else { - // Remove from array - monitors = monitors.filter(name => name !== modelData.name); + isChecked = monitors.includes(modelData.name); + } + + width: checkboxContent.implicitWidth + 16 + height: 32 + radius: 16 + color: isChecked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: isChecked ? Theme.accentPrimary : Theme.outline + border.width: 1 + + RowLayout { + id: checkboxContent + anchors.centerIn: parent + spacing: 6 + + Text { + text: notificationCheckbox.isChecked ? "check" : "" + font.family: "Material Symbols Outlined" + font.pixelSize: 14 * Theme.uiScale + color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary + visible: notificationCheckbox.isChecked + } + + Text { + text: modelData.name || "Unknown" + font.pixelSize: 12 * Theme.uiScale + color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + // Toggle state immediately for UI responsiveness + isChecked = !isChecked; + + // Update settings + let monitors = Settings.settings.notificationMonitors || []; + monitors = [...monitors]; // Copy array + + if (isChecked) { + // Add to array if not already there + if (!monitors.includes(modelData.name)) { + monitors.push(modelData.name); + } + } else { + // Remove from array + monitors = monitors.filter(name => name !== modelData.name); + } + + Settings.settings.notificationMonitors = monitors; + console.log("Notification monitors updated:", JSON.stringify(monitors)); } - - Settings.settings.notificationMonitors = monitors; - console.log("Notification monitors updated:", JSON.stringify(monitors)); } } } } } } - } - Item { - Layout.fillWidth: true - Layout.fillHeight: true + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } } } \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index d2e199b..ab9b03d 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -4,105 +4,215 @@ import QtQuick.Layouts import qs.Components import qs.Settings -ColumnLayout { - id: root - - spacing: 0 +ScrollView { anchors.fill: parent - anchors.margins: 0 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } - + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ColumnLayout { - spacing: 4 - Layout.fillWidth: true + id: root + width: parent.availableWidth + spacing: 0 * Theme.uiScale + anchors.top: parent.top + anchors.margins: 0 * Theme.uiScale - Text { - text: "Profile" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 } + + ColumnLayout { - spacing: 2 + spacing: 4 * Theme.uiScale Layout.fillWidth: true Text { - text: "Profile Image" - font.pixelSize: 13 + text: "Profile" + font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary + Layout.bottomMargin: 8 } - Text { - text: "Your profile picture displayed in various places throughout the shell" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - Layout.bottomMargin: 4 - } - - RowLayout { - spacing: 8 + ColumnLayout { + spacing: 2 * Theme.uiScale Layout.fillWidth: true - Rectangle { - width: 48 - height: 48 - radius: 24 + Text { + text: "Profile Image" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Your profile picture displayed in various places throughout the shell" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + Layout.bottomMargin: 4 + } + + RowLayout { + spacing: 8 * Theme.uiScale + Layout.fillWidth: true Rectangle { - anchors.fill: parent - color: "transparent" - radius: 24 - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 - z: 2 + width: 48 * Theme.uiScale + height: 48 * Theme.uiScale + radius: 24 * Theme.uiScale + + Rectangle { + anchors.fill: parent + color: "transparent" + radius: 24 * Theme.uiScale + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 2 * Theme.uiScale + z: 2 + } + + Avatar { + } + } - Avatar { + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 * Theme.uiScale + radius: 16 * Theme.uiScale + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 * Theme.uiScale + + TextInput { + id: profileImageInput + + anchors.fill: parent + anchors.leftMargin: 12 * Theme.uiScale + anchors.rightMargin: 12 * Theme.uiScale + anchors.topMargin: 6 * Theme.uiScale + anchors.bottomMargin: 6 * Theme.uiScale + text: Settings.settings.profileImage + font.pixelSize: 13 * Theme.uiScale + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.profileImage = text; + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: profileImageInput.forceActiveFocus() + } + + } + } } - Rectangle { + } + + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 * Theme.uiScale + Layout.bottomMargin: 18 * Theme.uiScale + height: 1 * Theme.uiScale + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 4 * Theme.uiScale + Layout.fillWidth: true + + Text { + text: "User Interface" + font.pixelSize: 18 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + ColumnLayout { + spacing: 4 * Theme.uiScale + Layout.fillWidth: true + + RowLayout { + spacing: 8 * Theme.uiScale Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - TextInput { - id: profileImageInput + ColumnLayout { + spacing: 4 * Theme.uiScale + Layout.fillWidth: true + + Text { + text: "Show Corners" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display rounded corners" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: cornersSwitch + + width: 52 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale + color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline + border.width: 2 * Theme.uiScale + + Rectangle { + id: cornersThumb + + width: 28 * Theme.uiScale + height: 28 * Theme.uiScale + radius: 14 * Theme.uiScale + color: Theme.surface + border.color: Theme.outline + border.width: 1 * Theme.uiScale + y: 2 + x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.profileImage - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.profileImage = text; } MouseArea { anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: profileImageInput.forceActiveFocus() + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showCorners = !Settings.settings.showCorners; + } } } @@ -111,252 +221,154 @@ ColumnLayout { } - } - - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "User Interface" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - RowLayout { - spacing: 8 + ColumnLayout { + spacing: 8 * Theme.uiScale Layout.fillWidth: true + Layout.topMargin: 4 * Theme.uiScale - ColumnLayout { - spacing: 4 + RowLayout { + spacing: 8 * Theme.uiScale Layout.fillWidth: true - Text { - text: "Show Corners" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display rounded corners" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap + ColumnLayout { + spacing: 4 * Theme.uiScale Layout.fillWidth: true + + Text { + text: "Show Dock" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display a dock at the bottom of the screen for quick access to applications" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } - } - - Rectangle { - id: cornersSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline - border.width: 2 - Rectangle { - id: cornersThumb + id: dockSwitch - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 + width: 52 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale + color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline + border.width: 2 * Theme.uiScale + + Rectangle { + id: dockThumb + + width: 28 * Theme.uiScale + height: 28 * Theme.uiScale + radius: 14 * Theme.uiScale + color: Theme.surface + border.color: Theme.outline + border.width: 1 * Theme.uiScale + y: 2 + x: Settings.settings.showDock ? dockSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic } } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showCorners = !Settings.settings.showCorners; + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.showDock = !Settings.settings.showDock; + } } + } } } - } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 4 - - RowLayout { - spacing: 8 + ColumnLayout { + spacing: 8 * Theme.uiScale Layout.fillWidth: true + Layout.topMargin: 4 * Theme.uiScale - ColumnLayout { - spacing: 4 + RowLayout { + spacing: 8 * Theme.uiScale Layout.fillWidth: true - Text { - text: "Show Dock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display a dock at the bottom of the screen for quick access to applications" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap + ColumnLayout { + spacing: 4 * Theme.uiScale Layout.fillWidth: true + + Text { + text: "Dim Desktop" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Dim the desktop when panels or menus are open" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } - } - - Rectangle { - id: dockSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline - border.width: 2 - Rectangle { - id: dockThumb + id: dimSwitch - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showDock ? dockSwitch.width - width - 2 : 2 + width: 52 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale + color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline + border.width: 2 * Theme.uiScale + + Rectangle { + id: dimThumb + + width: 28 * Theme.uiScale + height: 28 * Theme.uiScale + radius: 14 * Theme.uiScale + color: Theme.surface + border.color: Theme.outline + border.width: 1 * Theme.uiScale + y: 2 + x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic } } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showDock = !Settings.settings.showDock; - } - } - - } - - } - - } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 4 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Dim Desktop" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Dim the desktop when panels or menus are open" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: dimSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: dimThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.dimPanels = !Settings.settings.dimPanels; } - } } - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.dimPanels = !Settings.settings.dimPanels; - } - } - } } @@ -364,10 +376,4 @@ ColumnLayout { } } - - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - } diff --git a/Widgets/SettingsWindow/Tabs/Misc.qml b/Widgets/SettingsWindow/Tabs/Misc.qml index a2a4f16..30208f8 100644 --- a/Widgets/SettingsWindow/Tabs/Misc.qml +++ b/Widgets/SettingsWindow/Tabs/Misc.qml @@ -4,134 +4,144 @@ import QtQuick.Layouts import qs.Settings import qs.Components -ColumnLayout { - id: root - spacing: 0 +ScrollView { anchors.fill: parent - anchors.margins: 0 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } - - + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ColumnLayout { - spacing: 4 - Layout.fillWidth: true + id: root + width: parent.availableWidth + spacing: 0 + anchors.top: parent.top + anchors.margins: 0 - Text { - text: "Media" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 } ColumnLayout { - spacing: 8 + spacing: 4 Layout.fillWidth: true Text { - text: "Visualizer Type" - font.pixelSize: 13 + text: "Media" + font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary + Layout.bottomMargin: 8 } - Text { - text: "Choose the style of the audio visualizer" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap + + ColumnLayout { + spacing: 8 Layout.fillWidth: true - Layout.bottomMargin: 4 - } - ComboBox { - id: visualizerTypeComboBox - Layout.fillWidth: true - Layout.preferredHeight: 40 - model: ["radial", "fire", "diamond"] - currentIndex: model.indexOf(Settings.settings.visualizerType) - - background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 - color: Theme.surfaceVariant - border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 - } - - contentItem: Text { - leftPadding: 12 - rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing - text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1) - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - indicator: Text { - x: visualizerTypeComboBox.width - width - 12 - y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2 - text: "arrow_drop_down" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 + Text { + text: "Visualizer Type" + font.pixelSize: 13 * Theme.uiScale + font.bold: true color: Theme.textPrimary } - popup: Popup { - y: visualizerTypeComboBox.height - width: visualizerTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 + Text { + text: "Choose the style of the audio visualizer" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + Layout.bottomMargin: 4 + } - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null - currentIndex: visualizerTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator {} - } + ComboBox { + id: visualizerTypeComboBox + Layout.fillWidth: true + Layout.preferredHeight: 40 + model: ["radial", "fire", "diamond"] + currentIndex: model.indexOf(Settings.settings.visualizerType) background: Rectangle { + implicitWidth: 120 + implicitHeight: 40 color: Theme.surfaceVariant - border.color: Theme.outline + border.color: visualizerTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 radius: 16 } - } - delegate: ItemDelegate { - width: visualizerTypeComboBox.width contentItem: Text { - text: modelData.charAt(0).toUpperCase() + modelData.slice(1) + leftPadding: 12 + rightPadding: visualizerTypeComboBox.indicator.width + visualizerTypeComboBox.spacing + text: visualizerTypeComboBox.displayText.charAt(0).toUpperCase() + visualizerTypeComboBox.displayText.slice(1) font.pixelSize: 13 color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - highlighted: visualizerTypeComboBox.highlightedIndex === index - background: Rectangle { - color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + indicator: Text { + x: visualizerTypeComboBox.width - width - 12 + y: visualizerTypeComboBox.topPadding + (visualizerTypeComboBox.availableHeight - height) / 2 + text: "arrow_drop_down" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 + color: Theme.textPrimary } - } - onActivated: { - Settings.settings.visualizerType = model[index]; + popup: Popup { + y: visualizerTypeComboBox.height + width: visualizerTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null + currentIndex: visualizerTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator {} + } + + background: Rectangle { + color: Theme.surfaceVariant + border.color: Theme.outline + border.width: 1 + radius: 16 + } + } + + delegate: ItemDelegate { + width: visualizerTypeComboBox.width + contentItem: Text { + text: modelData.charAt(0).toUpperCase() + modelData.slice(1) + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + highlighted: visualizerTypeComboBox.highlightedIndex === index + + background: Rectangle { + color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" + } + } + + onActivated: { + Settings.settings.visualizerType = model[index]; + } } } } - } - Item { - Layout.fillWidth: true - Layout.fillHeight: true + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } } } \ No newline at end of file diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index 13ee16a..8bf9dc6 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -6,213 +6,223 @@ import Quickshell.Bluetooth import qs.Components import qs.Settings -ColumnLayout { - id: root - - spacing: 24 - Component.onCompleted: { - Quickshell.execDetached(["nmcli", "-t", "-f", "WIFI", "radio"]); - } - +ScrollView { + anchors.fill: parent + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ColumnLayout { - spacing: 16 - Layout.fillWidth: true - - Text { - text: "Wi-Fi" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Enable Wi-Fi" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Turn Wi-Fi radio on or off" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: wifiSwitch - - property bool checked: Settings.settings.wifiEnabled - - width: 52 - height: 32 - radius: 16 - color: checked ? Theme.accentPrimary : Theme.surfaceVariant - border.color: checked ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wifiThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: wifiSwitch.checked ? wifiSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled; - Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"]); - } - } - - } - - } - - } - - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 0 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 16 - Layout.fillWidth: true - - Text { - text: "Bluetooth" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Enable Bluetooth" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Turn Bluetooth radio on or off" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: bluetoothSwitch - - property bool checked: Settings.settings.bluetoothEnabled - - width: 52 - height: 32 - radius: 16 - color: checked ? Theme.accentPrimary : Theme.surfaceVariant - border.color: checked ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: bluetoothThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: bluetoothSwitch.checked ? bluetoothSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - if (Bluetooth.defaultAdapter) { - Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled; - Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled; - if (Bluetooth.defaultAdapter.enabled) - Bluetooth.defaultAdapter.discovering = true; - - } - } - } - - } - - } - - } - - } - - Item { + id: root + width: parent.availableWidth Layout.fillWidth: true Layout.fillHeight: true - } + spacing: 24 + Component.onCompleted: { + Quickshell.execDetached(["nmcli", "-t", "-f", "WIFI", "radio"]); + } + ColumnLayout { + spacing: 16 + Layout.fillWidth: true + + Text { + text: "Wi-Fi" + font.pixelSize: 18 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Enable Wi-Fi" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Turn Wi-Fi radio on or off" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: wifiSwitch + + property bool checked: Settings.settings.wifiEnabled + + width: 52 + height: 32 + radius: 16 + color: checked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: checked ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wifiThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: wifiSwitch.checked ? wifiSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled; + Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"]); + } + } + + } + + } + + } + + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 0 + height: 1 + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 16 + Layout.fillWidth: true + + Text { + text: "Bluetooth" + font.pixelSize: 18 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Enable Bluetooth" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Turn Bluetooth radio on or off" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: bluetoothSwitch + + property bool checked: Settings.settings.bluetoothEnabled + + width: 52 + height: 32 + radius: 16 + color: checked ? Theme.accentPrimary : Theme.surfaceVariant + border.color: checked ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: bluetoothThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: bluetoothSwitch.checked ? bluetoothSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + if (Bluetooth.defaultAdapter) { + Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled; + Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled; + if (Bluetooth.defaultAdapter.enabled) + Bluetooth.defaultAdapter.discovering = true; + + } + } + } + + } + + } + + } + + } + + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } } diff --git a/Widgets/SettingsWindow/Tabs/Recording.qml b/Widgets/SettingsWindow/Tabs/Recording.qml index 1011538..34cb9ab 100644 --- a/Widgets/SettingsWindow/Tabs/Recording.qml +++ b/Widgets/SettingsWindow/Tabs/Recording.qml @@ -34,7 +34,7 @@ ColumnLayout { ColumnLayout { // Text { // text: "Screen Recording" - // font.pixelSize: 18 + // font.pixelSize: 18 * Theme.uiScale // font.bold: true // color: Theme.textPrimary // Layout.bottomMargin: 8 @@ -49,14 +49,14 @@ ColumnLayout { Text { text: "Output Directory" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Directory where screen recordings will be saved" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -78,7 +78,7 @@ ColumnLayout { anchors.topMargin: 6 anchors.bottomMargin: 6 text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -108,14 +108,14 @@ ColumnLayout { Text { text: "Frame Rate" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Target frame rate for screen recordings (default: 60)" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -144,7 +144,7 @@ ColumnLayout { contentItem: TextInput { text: frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale) - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary selectionColor: Theme.accentPrimary selectedTextColor: Theme.onAccent @@ -183,7 +183,7 @@ ColumnLayout { Text { text: "add" font.family: "Material Symbols Outlined" - font.pixelSize: 20 + font.pixelSize: 20 * Theme.uiScale color: Theme.textPrimary anchors.centerIn: parent } @@ -200,7 +200,7 @@ ColumnLayout { Text { text: "remove" font.family: "Material Symbols Outlined" - font.pixelSize: 20 + font.pixelSize: 20 * Theme.uiScale color: Theme.textPrimary anchors.centerIn: parent } @@ -218,14 +218,14 @@ ColumnLayout { Text { text: "Audio Source" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Audio source to capture during recording" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -265,7 +265,7 @@ ColumnLayout { return audioSourceComboBox.currentText; } } - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -276,7 +276,7 @@ ColumnLayout { y: audioSourceComboBox.topPadding + (audioSourceComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.textPrimary } @@ -323,7 +323,7 @@ ColumnLayout { return modelData; } } - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -346,14 +346,14 @@ ColumnLayout { Text { text: "Video Quality" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Higher quality results in larger file sizes" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -395,7 +395,7 @@ ColumnLayout { return qualityComboBox.currentText; } } - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -406,7 +406,7 @@ ColumnLayout { y: qualityComboBox.topPadding + (qualityComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.textPrimary } @@ -455,7 +455,7 @@ ColumnLayout { return modelData; } } - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -478,14 +478,14 @@ ColumnLayout { Text { text: "Video Codec" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Different codecs offer different compression and compatibility" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -514,7 +514,7 @@ ColumnLayout { leftPadding: 12 rightPadding: codecComboBox.indicator.width + codecComboBox.spacing text: codecComboBox.currentText.toUpperCase() - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -525,7 +525,7 @@ ColumnLayout { y: codecComboBox.topPadding + (codecComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.textPrimary } @@ -561,7 +561,7 @@ ColumnLayout { contentItem: Text { text: modelData.toUpperCase() - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -584,14 +584,14 @@ ColumnLayout { Text { text: "Audio Codec" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Opus is recommended for best performance and smallest audio size" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -620,7 +620,7 @@ ColumnLayout { leftPadding: 12 rightPadding: audioCodecComboBox.indicator.width + audioCodecComboBox.spacing text: audioCodecComboBox.currentText.toUpperCase() - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -631,7 +631,7 @@ ColumnLayout { y: audioCodecComboBox.topPadding + (audioCodecComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.textPrimary } @@ -667,7 +667,7 @@ ColumnLayout { contentItem: Text { text: modelData.toUpperCase() - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -690,14 +690,14 @@ ColumnLayout { Text { text: "Color Range" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Limited is recommended for better compatibility" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -726,7 +726,7 @@ ColumnLayout { leftPadding: 12 rightPadding: colorRangeComboBox.indicator.width + colorRangeComboBox.spacing text: colorRangeComboBox.currentText.charAt(0).toUpperCase() + colorRangeComboBox.currentText.slice(1) - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -737,7 +737,7 @@ ColumnLayout { y: colorRangeComboBox.topPadding + (colorRangeComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.textPrimary } @@ -773,7 +773,7 @@ ColumnLayout { contentItem: Text { text: modelData.charAt(0).toUpperCase() + modelData.slice(1) - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -804,14 +804,14 @@ ColumnLayout { Text { text: "Show Cursor" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Record mouse cursor in the video" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml index 497784d..75921b6 100644 --- a/Widgets/SettingsWindow/Tabs/TimeWeather.qml +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -5,258 +5,268 @@ import qs.Components import qs.Settings import qs.Widgets.SettingsWindow.Tabs.Components -ColumnLayout { - id: root - - spacing: 0 +ScrollView { anchors.fill: parent - anchors.margins: 0 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } - + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded + ColumnLayout { - spacing: 4 - Layout.fillWidth: true + id: root + width: parent.availableWidth + spacing: 0 + anchors.top: parent.top + anchors.margins: 0 - Text { - text: "Time" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - ColumnLayout { - spacing: 8 + Item { Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use 12 Hour Clock" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: use12HourClockSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: use12HourClockThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.use12HourClock = !Settings.settings.use12HourClock; - } - } - - } - - } - + Layout.preferredHeight: 0 } ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "US Style Date" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: reverseDayMonthSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: reverseDayMonthThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; - } - } - - } - - } - - } - - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Weather" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - ColumnLayout { - spacing: 8 + spacing: 4 Layout.fillWidth: true Text { - text: "City" - font.pixelSize: 13 + text: "Time" + font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary + Layout.bottomMargin: 8 } - Text { - text: "Your city name for weather information" - font.pixelSize: 12 - color: Theme.textSecondary + ColumnLayout { + spacing: 8 Layout.fillWidth: true - } + Layout.topMargin: 8 - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 + RowLayout { + spacing: 8 + Layout.fillWidth: true - TextInput { - id: cityInput + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use 12 Hour Clock" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.weatherCity - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - focus: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhNone - onTextChanged: { - Settings.settings.weatherCity = text; } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: { - cityInput.forceActiveFocus(); + Rectangle { + id: use12HourClockSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: use12HourClockThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.use12HourClock = !Settings.settings.use12HourClock; + } + } + + } + + } + + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "US Style Date" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: reverseDayMonthSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: reverseDayMonthThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; + } + } + + } + + } + + } + + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Weather" + font.pixelSize: 18 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "City" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Your city name for weather information" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: cityInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: cityInput + + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.weatherCity + font.pixelSize: 13 * Theme.uiScale + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + focus: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhNone + onTextChanged: { + Settings.settings.weatherCity = text; + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: { + cityInput.forceActiveFocus(); + } + } + } } @@ -280,14 +290,14 @@ ColumnLayout { Text { text: "Temperature Unit" - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: "Choose between Celsius and Fahrenheit" - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true @@ -308,5 +318,4 @@ ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true } - } diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index e2fe2c7..8956a68 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -5,857 +5,861 @@ import qs.Components import qs.Services import qs.Settings -ColumnLayout { - id: root - - spacing: 0 +ScrollView { anchors.fill: parent - anchors.margins: 0 + ColumnLayout { + id: root - ScrollView { - id: scrollView + spacing: 0 + anchors.fill: parent + anchors.margins: 0 - Layout.fillWidth: true - Layout.fillHeight: true - padding: 0 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded + ScrollView { + id: scrollView - ColumnLayout { - width: scrollView.availableWidth - spacing: 0 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } + Layout.fillWidth: true + Layout.fillHeight: true + padding: 0 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - spacing: 4 - Layout.fillWidth: true + width: scrollView.availableWidth + spacing: 0 - Text { - text: "Wallpaper Settings" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 } - // Wallpaper Settings Category ColumnLayout { - spacing: 8 + spacing: 4 Layout.fillWidth: true - Layout.topMargin: 8 - // Wallpaper Folder + Text { + text: "Wallpaper Settings" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Wallpaper Settings Category ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 + + // Wallpaper Folder + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Wallpaper Folder" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Path to your wallpaper folder" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: folderInput + + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "" + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.wallpaperFolder = text; + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: folderInput.forceActiveFocus() + } + + } + + } + + } + + } + + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Automation" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Random Wallpaper + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Random Wallpaper" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Automatically select random wallpapers from the folder" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: randomWallpaperSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: randomWallpaperThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; + } + } + + } + + } + + } + + // Use Wallpaper Theme + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use Wallpaper Theme" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Automatically adjust theme colors based on wallpaper" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: wallpaperThemeSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wallpaperThemeThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; + } + } + + } + + } + + } + + // Wallpaper Interval + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 Text { - text: "Wallpaper Folder" + text: "Wallpaper Interval" font.pixelSize: 13 font.bold: true color: Theme.textPrimary } Text { - text: "Path to your wallpaper folder" + text: "How often to change wallpapers automatically (in seconds)" font.pixelSize: 12 color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true } + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.wallpaperInterval + " seconds" + font.pixelSize: 13 + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + } + + Slider { + id: intervalSlider + + Layout.fillWidth: true + from: 10 + to: 900 + stepSize: 10 + value: Settings.settings.wallpaperInterval + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.wallpaperInterval = Math.round(value); + } + + background: Rectangle { + x: intervalSlider.leftPadding + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: intervalSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: intervalSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + + } + + handle: Rectangle { + x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + } + + } + + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "SWWW" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Use SWWW + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + RowLayout { spacing: 8 Layout.fillWidth: true + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use SWWW" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Use SWWW daemon for advanced wallpaper management" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: swwwSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: swwwThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useSWWW = !Settings.settings.useSWWW; + } + } + + } + + } + + } + + // SWWW Settings (only visible when useSWWW is enabled) + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + visible: Settings.settings.useSWWW + + // Resize Mode + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Resize Mode" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "How SWWW should resize wallpapers to fit the screen" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + Rectangle { Layout.fillWidth: true Layout.preferredHeight: 40 radius: 16 color: Theme.surfaceVariant - border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline border.width: 1 - TextInput { - id: folderInput + ComboBox { + id: resizeComboBox anchors.fill: parent anchors.leftMargin: 12 anchors.rightMargin: 12 anchors.topMargin: 6 anchors.bottomMargin: 6 - text: Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "" - font.family: Theme.fontFamily + model: ["no", "crop", "fit", "stretch"] + currentIndex: model.indexOf(Settings.settings.wallpaperResize) + onActivated: { + Settings.settings.wallpaperResize = model[index]; + } + + background: Rectangle { + color: "transparent" + } + + contentItem: Text { + text: resizeComboBox.displayText + font: resizeComboBox.font + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + popup: Popup { + y: resizeComboBox.height + width: resizeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null + currentIndex: resizeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surface + border.color: Theme.outline + border.width: 1 + radius: 8 + } + + } + + delegate: ItemDelegate { + width: resizeComboBox.width + highlighted: resizeComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData + color: Theme.textPrimary + font: resizeComboBox.font + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + background: Rectangle { + color: parent.highlighted ? Theme.accentPrimary : "transparent" + } + + } + + } + + } + + } + + // Transition Type + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Transition Type" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Animation type when switching between wallpapers" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + ComboBox { + id: transitionTypeComboBox + + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] + currentIndex: model.indexOf(Settings.settings.transitionType) + onActivated: { + Settings.settings.transitionType = model[index]; + } + + background: Rectangle { + color: "transparent" + } + + contentItem: Text { + text: transitionTypeComboBox.displayText + font: transitionTypeComboBox.font + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + popup: Popup { + y: transitionTypeComboBox.height + width: transitionTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null + currentIndex: transitionTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surface + border.color: Theme.outline + border.width: 1 + radius: 8 + } + + } + + delegate: ItemDelegate { + width: transitionTypeComboBox.width + highlighted: transitionTypeComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData + color: Theme.textPrimary + font: transitionTypeComboBox.font + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + background: Rectangle { + color: parent.highlighted ? Theme.accentPrimary : "transparent" + } + + } + + } + + } + + } + + // Transition FPS + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Transition FPS" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Frames per second for transition animations" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.transitionFps + " FPS" font.pixelSize: 13 color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.wallpaperFolder = text; - } } - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: folderInput.forceActiveFocus() + Item { + Layout.fillWidth: true } } - } + Slider { + id: fpsSlider - } - - } - - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Automation" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - // Random Wallpaper - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Random Wallpaper" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically select random wallpapers from the folder" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap Layout.fillWidth: true - } - - } - - Rectangle { - id: randomWallpaperSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; - } - } - - } - - } - - } - - // Use Wallpaper Theme - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use Wallpaper Theme" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically adjust theme colors based on wallpaper" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: wallpaperThemeSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wallpaperThemeThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; - } - } - - } - - } - - } - - // Wallpaper Interval - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Wallpaper Interval" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "How often to change wallpapers automatically (in seconds)" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - Text { - text: Settings.settings.wallpaperInterval + " seconds" - font.pixelSize: 13 - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - } - - Slider { - id: intervalSlider - - Layout.fillWidth: true - from: 10 - to: 900 - stepSize: 10 - value: Settings.settings.wallpaperInterval - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); - } - - background: Rectangle { - x: intervalSlider.leftPadding - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: intervalSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: intervalSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "SWWW" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - // Use SWWW - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Use SWWW daemon for advanced wallpaper management" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: swwwSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: swwwThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - - } - - } - - } - - // SWWW Settings (only visible when useSWWW is enabled) - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - visible: Settings.settings.useSWWW - - // Resize Mode - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Resize Mode" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "How SWWW should resize wallpapers to fit the screen" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - ComboBox { - id: resizeComboBox - - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - model: ["no", "crop", "fit", "stretch"] - currentIndex: model.indexOf(Settings.settings.wallpaperResize) - onActivated: { - Settings.settings.wallpaperResize = model[index]; + from: 30 + to: 500 + stepSize: 5 + value: Settings.settings.transitionFps + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionFps = Math.round(value); } background: Rectangle { - color: "transparent" - } + x: fpsSlider.leftPadding + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: fpsSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant - contentItem: Text { - text: resizeComboBox.displayText - font: resizeComboBox.font - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - popup: Popup { - y: resizeComboBox.height - width: resizeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null - currentIndex: resizeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surface - border.color: Theme.outline - border.width: 1 - radius: 8 + Rectangle { + width: fpsSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 } } - delegate: ItemDelegate { - width: resizeComboBox.width - highlighted: resizeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - color: Theme.textPrimary - font: resizeComboBox.font - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - background: Rectangle { - color: parent.highlighted ? Theme.accentPrimary : "transparent" - } - + handle: Rectangle { + x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 } } } - } - - // Transition Type - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Transition Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Animation type when switching between wallpapers" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap + // Transition Duration + ColumnLayout { + spacing: 8 Layout.fillWidth: true - } + Layout.topMargin: 8 - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 + Text { + text: "Transition Duration" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } - ComboBox { - id: transitionTypeComboBox + Text { + text: "Duration of transition animations in seconds" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] - currentIndex: model.indexOf(Settings.settings.transitionType) - onActivated: { - Settings.settings.transitionType = model[index]; + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.transitionDuration.toFixed(3) + " seconds" + font.pixelSize: 13 + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + } + + Slider { + id: durationSlider + + Layout.fillWidth: true + from: 0.25 + to: 10 + stepSize: 0.05 + value: Settings.settings.transitionDuration + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionDuration = value; } background: Rectangle { - color: "transparent" - } - - contentItem: Text { - text: transitionTypeComboBox.displayText - font: transitionTypeComboBox.font - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - popup: Popup { - y: transitionTypeComboBox.height - width: transitionTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null - currentIndex: transitionTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surface - border.color: Theme.outline - border.width: 1 - radius: 8 - } - - } - - delegate: ItemDelegate { - width: transitionTypeComboBox.width - highlighted: transitionTypeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - color: Theme.textPrimary - font: transitionTypeComboBox.font - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - background: Rectangle { - color: parent.highlighted ? Theme.accentPrimary : "transparent" - } - - } - - } - - } - - } - - // Transition FPS - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Transition FPS" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Frames per second for transition animations" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - Text { - text: Settings.settings.transitionFps + " FPS" - font.pixelSize: 13 - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - } - - Slider { - id: fpsSlider - - Layout.fillWidth: true - from: 30 - to: 500 - stepSize: 5 - value: Settings.settings.transitionFps - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionFps = Math.round(value); - } - - background: Rectangle { - x: fpsSlider.leftPadding - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: fpsSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: fpsSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary + x: durationSlider.leftPadding + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: durationSlider.availableWidth + height: implicitHeight radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: durationSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + } - } - - handle: Rectangle { - x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - - } - - } - - // Transition Duration - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Transition Duration" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Duration of transition animations in seconds" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - Text { - text: Settings.settings.transitionDuration.toFixed(3) + " seconds" - font.pixelSize: 13 - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - } - - Slider { - id: durationSlider - - Layout.fillWidth: true - from: 0.25 - to: 10 - stepSize: 0.05 - value: Settings.settings.transitionDuration - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionDuration = value; - } - - background: Rectangle { - x: durationSlider.leftPadding - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: durationSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: durationSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 + handle: Rectangle { + x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 } } - handle: Rectangle { - x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - } } diff --git a/Widgets/SidePanel/Button.qml b/Widgets/SidePanel/Button.qml index ba38128..94c9e65 100644 --- a/Widgets/SidePanel/Button.qml +++ b/Widgets/SidePanel/Button.qml @@ -44,7 +44,7 @@ Item { id: iconText text: "dashboard" font.family: isActive ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: sidebarPopup.visible ? Theme.accentPrimary : Theme.textPrimary anchors.centerIn: parent z: 1 diff --git a/Widgets/SidePanel/Music.qml b/Widgets/SidePanel/Music.qml index 6d24310..c51a546 100644 --- a/Widgets/SidePanel/Music.qml +++ b/Widgets/SidePanel/Music.qml @@ -8,15 +8,15 @@ import qs.Services Rectangle { id: musicCard - width: 360 - height: 250 + width: 360 * Theme.uiScale + height: 250 * Theme.uiScale color: "transparent" Rectangle { id: card anchors.fill: parent color: Theme.surface - radius: 18 + radius: 18 * Theme.uiScale // Show fallback UI if no player is available Item { @@ -26,12 +26,12 @@ Rectangle { ColumnLayout { anchors.centerIn: parent - spacing: 16 + spacing: 16 * Theme.uiScale Text { text: "music_note" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader + font.pixelSize: Theme.fontSizeHeader * Theme.uiScale color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) Layout.alignment: Qt.AlignHCenter } @@ -40,7 +40,7 @@ Rectangle { text: MusicManager.hasPlayer ? "No controllable player selected" : "No music player detected" color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.6) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale Layout.alignment: Qt.AlignHCenter } } @@ -49,45 +49,45 @@ Rectangle { // Main player UI ColumnLayout { anchors.fill: parent - anchors.margins: 18 - spacing: 12 + anchors.margins: 18 * Theme.uiScale + spacing: 12 * Theme.uiScale visible: !!MusicManager.currentPlayer // Player selector ComboBox { id: playerSelector Layout.fillWidth: true - Layout.preferredHeight: 40 + Layout.preferredHeight: 40 * Theme.uiScale visible: MusicManager.getAvailablePlayers().length > 1 model: MusicManager.getAvailablePlayers() textRole: "identity" currentIndex: MusicManager.selectedPlayerIndex background: Rectangle { - implicitWidth: 120 - implicitHeight: 40 + implicitWidth: 120 * Theme.uiScale + implicitHeight: 40 * Theme.uiScale color: Theme.surfaceVariant border.color: playerSelector.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - radius: 16 + border.width: 1 * Theme.uiScale + radius: 16 * Theme.uiScale } contentItem: Text { - leftPadding: 12 + leftPadding: 12 * Theme.uiScale rightPadding: playerSelector.indicator.width + playerSelector.spacing text: playerSelector.displayText - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } indicator: Text { - x: playerSelector.width - width - 12 + x: playerSelector.width - width - 12 * Theme.uiScale y: playerSelector.topPadding + (playerSelector.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 + font.pixelSize: 24 * Theme.uiScale color: Theme.textPrimary } @@ -95,7 +95,7 @@ Rectangle { y: playerSelector.height width: playerSelector.width implicitHeight: contentItem.implicitHeight - padding: 1 + padding: 1 * Theme.uiScale contentItem: ListView { clip: true @@ -109,8 +109,8 @@ Rectangle { background: Rectangle { color: Theme.surfaceVariant border.color: Theme.outline - border.width: 1 - radius: 16 + border.width: 1 * Theme.uiScale + radius: 16 * Theme.uiScale } } @@ -118,7 +118,7 @@ Rectangle { width: playerSelector.width contentItem: Text { text: modelData.identity - font.pixelSize: 13 + font.pixelSize: 13 * Theme.uiScale color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -138,14 +138,14 @@ Rectangle { // Album art with spectrum visualizer RowLayout { - spacing: 12 + spacing: 12 * Theme.uiScale Layout.fillWidth: true // Album art container with circular spectrum overlay Item { id: albumArtContainer - width: 96 - height: 96 // enough for spectrum and art (will adjust if needed) + width: 96 * Theme.uiScale + height: 96 * Theme.uiScale // enough for spectrum and art (will adjust if needed) Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter // Circular spectrum visualizer around album art @@ -153,36 +153,36 @@ Rectangle { id: spectrum values: MusicManager.cavaValues anchors.centerIn: parent - innerRadius: 30 // Position just outside 60x60 album art - outerRadius: 48 // Extend bars outward from album art + innerRadius: 30 * Theme.uiScale // Position just outside 60x60 album art + outerRadius: 48 * Theme.uiScale // Extend bars outward from album art fillColor: Theme.accentPrimary strokeColor: Theme.accentPrimary - strokeWidth: 0 + strokeWidth: 0 * Theme.uiScale z: 0 } // Album art image Rectangle { id: albumArtwork - width: 60 - height: 60 + width: 60 * Theme.uiScale + height: 60 * Theme.uiScale anchors.centerIn: parent - radius: 30 // circle + radius: 30 * Theme.uiScale // circle color: Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) - border.width: 1 + border.width: 1 * Theme.uiScale Image { id: albumArt anchors.fill: parent - anchors.margins: 2 + anchors.margins: 2 * Theme.uiScale fillMode: Image.PreserveAspectCrop smooth: true mipmap: true cache: false asynchronous: true - sourceSize.width: 60 - sourceSize.height: 60 + sourceSize.width: 60 * Theme.uiScale + sourceSize.height: 60 * Theme.uiScale source: MusicManager.trackArtUrl visible: source.toString() !== "" @@ -213,7 +213,7 @@ Rectangle { anchors.centerIn: parent text: "album" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: Theme.fontSizeBody * Theme.uiScale color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.4) visible: !albumArt.visible } @@ -223,13 +223,13 @@ Rectangle { // Track metadata ColumnLayout { Layout.fillWidth: true - spacing: 4 + spacing: 4 * Theme.uiScale Text { text: MusicManager.trackTitle color: Theme.textPrimary font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall + font.pixelSize: Theme.fontSizeSmall * Theme.uiScale font.bold: true elide: Text.ElideRight wrapMode: Text.Wrap @@ -241,7 +241,7 @@ Rectangle { text: MusicManager.trackArtist color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.8) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption + font.pixelSize: Theme.fontSizeCaption * Theme.uiScale elide: Text.ElideRight Layout.fillWidth: true } @@ -250,7 +250,7 @@ Rectangle { text: MusicManager.trackAlbum color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.6) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption + font.pixelSize: Theme.fontSizeCaption * Theme.uiScale elide: Text.ElideRight Layout.fillWidth: true } @@ -261,8 +261,8 @@ Rectangle { Rectangle { id: progressBarBackground width: parent.width - height: 6 - radius: 3 + height: 6 * Theme.uiScale + radius: 3 * Theme.uiScale color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.15) Layout.fillWidth: true @@ -290,12 +290,12 @@ Rectangle { // Interactive progress handle Rectangle { id: progressHandle - width: 12 - height: 12 - radius: 6 + width: 12 * Theme.uiScale + height: 12 * Theme.uiScale + radius: 6 * Theme.uiScale color: Theme.accentPrimary border.color: Qt.lighter(Theme.accentPrimary, 1.3) - border.width: 1 + border.width: 1 * Theme.uiScale x: Math.max(0, Math.min(parent.width - width, progressFill.width - width / 2)) anchors.verticalCenter: parent.verticalCenter @@ -334,18 +334,18 @@ Rectangle { // Media controls RowLayout { - spacing: 4 + spacing: 4 * Theme.uiScale Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter // Previous button Rectangle { - width: 28 - height: 28 - radius: 14 + width: 28 * Theme.uiScale + height: 28 * Theme.uiScale + radius: 14 * Theme.uiScale color: previousButton.containsMouse ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.2) : Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) - border.width: 1 + border.width: 1 * Theme.uiScale MouseArea { id: previousButton @@ -360,19 +360,19 @@ Rectangle { anchors.centerIn: parent text: "skip_previous" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeCaption + font.pixelSize: Theme.fontSizeCaption * Theme.uiScale color: previousButton.enabled ? Theme.accentPrimary : Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) } } // Play/Pause button Rectangle { - width: 36 - height: 36 - radius: 18 + width: 36 * Theme.uiScale + height: 36 * Theme.uiScale + radius: 18 * Theme.uiScale color: playButton.containsMouse ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.2) : Qt.darker(Theme.surface, 1.1) border.color: Theme.accentPrimary - border.width: 2 + border.width: 2 * Theme.uiScale MouseArea { id: playButton @@ -387,19 +387,19 @@ Rectangle { anchors.centerIn: parent text: MusicManager.isPlaying ? "pause" : "play_arrow" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody + font.pixelSize: Theme.fontSizeBody * Theme.uiScale color: playButton.enabled ? Theme.accentPrimary : Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) } } // Next button Rectangle { - width: 28 - height: 28 - radius: 14 + width: 28 * Theme.uiScale + height: 28 * Theme.uiScale + radius: 14 * Theme.uiScale color: nextButton.containsMouse ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.2) : Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) - border.width: 1 + border.width: 1 * Theme.uiScale MouseArea { id: nextButton @@ -414,7 +414,7 @@ Rectangle { anchors.centerIn: parent text: "skip_next" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeCaption + font.pixelSize: Theme.fontSizeCaption * Theme.uiScale color: nextButton.enabled ? Theme.accentPrimary : Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) } } diff --git a/Widgets/SidePanel/PanelPopup.qml b/Widgets/SidePanel/PanelPopup.qml index 6119be8..60867d9 100644 --- a/Widgets/SidePanel/PanelPopup.qml +++ b/Widgets/SidePanel/PanelPopup.qml @@ -45,11 +45,28 @@ PanelWithOverlay { property real slideOffset: width property bool isAnimating: false - property int leftPadding: 20 - property int bottomPadding: 20 + property int leftPadding: 20 * Theme.uiScale + property int bottomPadding: 20 * Theme.uiScale // Recording properties property bool isRecording: false + Process { + id: checkRecordingProcess + command: ["pgrep", "-f", "gpu-screen-recorder.*portal"] + onExited: function(exitCode, exitStatus) { + var isActuallyRecording = exitCode === 0 + if (isRecording && !isActuallyRecording) { + isRecording = isActuallyRecording + } + } + } + + function checkRecordingStatus() { + if (isRecording) { + checkRecordingProcess.running = true + } + } + function showAt() { if (!sidebarPopup.visible) { sidebarPopup.visible = true; @@ -73,11 +90,7 @@ PanelWithOverlay { if (shell && shell.settingsWindow && shell.settingsWindow.visible) shell.settingsWindow.visible = false; - if (wifiPanelLoader.active && wifiPanelLoader.item && wifiPanelLoader.item.visible) - wifiPanelLoader.item.visible = false; - if (bluetoothPanelLoader.active && bluetoothPanelLoader.item && bluetoothPanelLoader.item.visible) - bluetoothPanelLoader.item.visible = false; if (sidebarPopup.visible) { slideAnim.from = 0; @@ -119,8 +132,8 @@ PanelWithOverlay { quickAccessWidget.isRecording = false; } - implicitWidth: 500 - implicitHeight: 800 + implicitWidth: 500 * Theme.uiScale + implicitHeight: 700 * Theme.uiScale visible: parent.visible color: "transparent" anchors.top: parent.top @@ -173,7 +186,7 @@ PanelWithOverlay { x: sidebarPopupRect.leftPadding + sidebarPopupRect.slideOffset y: 0 color: Theme.backgroundPrimary - bottomLeftRadius: 20 + bottomLeftRadius: 20 * Theme.uiScale z: 0 Behavior on x { @@ -188,27 +201,7 @@ PanelWithOverlay { } - // LazyLoader for WifiPanel - LazyLoader { - id: wifiPanelLoader - loading: false - - component: WifiPanel { - } - - } - - // LazyLoader for BluetoothPanel - LazyLoader { - id: bluetoothPanelLoader - - loading: false - - component: BluetoothPanel { - } - - } // SettingsIcon component SettingsIcon { @@ -228,11 +221,12 @@ PanelWithOverlay { ColumnLayout { anchors.fill: parent - anchors.margins: 20 - spacing: 16 + anchors.margins: 20 * Theme.uiScale + spacing: 4 * Theme.uiScale PowerMenu { id: systemWidget + settingsModal: settingsModal Layout.alignment: Qt.AlignHCenter z: 3 @@ -240,14 +234,13 @@ PanelWithOverlay { Weather { id: weather - Layout.alignment: Qt.AlignHCenter z: 2 } // Music and System Monitor row RowLayout { - spacing: 12 + spacing: 12 * Theme.uiScale Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter @@ -257,123 +250,118 @@ PanelWithOverlay { SystemMonitor { id: systemMonitor - z: 2 } } - // Power profile, Wifi and Bluetooth row + // Power profile, Record and Wallpaper row RowLayout { - Layout.alignment: Qt.AlignLeft - Layout.preferredHeight: 80 - spacing: 16 + Layout.alignment: Qt.AlignVCenter + spacing: 10 * Theme.uiScale + Layout.preferredHeight: 80 * Theme.uiScale z: 3 PowerProfile { - Layout.alignment: Qt.AlignLeft - Layout.preferredHeight: 80 + Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: 80 * Theme.uiScale } - // Network card containing Wifi and Bluetooth + // Record and Wallpaper card Rectangle { - Layout.preferredHeight: 80 - Layout.preferredWidth: 140 + Layout.preferredHeight: 80 * Theme.uiScale + Layout.preferredWidth: 140 * Theme.uiScale Layout.fillWidth: false color: Theme.surface - radius: 18 + radius: 18 * Theme.uiScale Row { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - spacing: 20 + spacing: 20 * Theme.uiScale - // Wifi button + // Record button Rectangle { - id: wifiButton + id: recordButton - width: 36 - height: 36 - radius: 18 + width: 36 * Theme.uiScale + height: 36 * Theme.uiScale + radius: 18 * Theme.uiScale border.color: Theme.accentPrimary - border.width: 1 - color: wifiButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.width: 1 * Theme.uiScale + color: sidebarPopupRect.isRecording ? Theme.accentPrimary : + (recordButtonArea.containsMouse ? Theme.accentPrimary : "transparent") Text { anchors.centerIn: parent - text: "wifi" + text: "photo_camera" font.family: "Material Symbols Outlined" - font.pixelSize: 22 - color: wifiButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + font.pixelSize: 22 * Theme.uiScale + color: sidebarPopupRect.isRecording || recordButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } MouseArea { - id: wifiButtonArea + id: recordButtonArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - if (!wifiPanelLoader.active) - wifiPanelLoader.loading = true; - - if (wifiPanelLoader.item) - wifiPanelLoader.item.showAt(); - + if (sidebarPopupRect.isRecording) { + sidebarPopupRect.stopRecording(); + } else { + sidebarPopupRect.startRecording(); + } } } StyledTooltip { - text: "Wifi" - targetItem: wifiButtonArea - tooltipVisible: wifiButtonArea.containsMouse + text: sidebarPopupRect.isRecording ? "Stop Recording" : "Start Recording" + targetItem: recordButtonArea + tooltipVisible: recordButtonArea.containsMouse } } - // Bluetooth button + // Wallpaper button Rectangle { - id: bluetoothButton + id: wallpaperButton - width: 36 - height: 36 - radius: 18 + width: 36 * Theme.uiScale + height: 36 * Theme.uiScale + radius: 18 * Theme.uiScale border.color: Theme.accentPrimary - border.width: 1 - color: bluetoothButtonArea.containsMouse ? Theme.accentPrimary : "transparent" + border.width: 1 * Theme.uiScale + color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" Text { anchors.centerIn: parent - text: "bluetooth" + text: "image" font.family: "Material Symbols Outlined" - font.pixelSize: 22 - color: bluetoothButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary + font.pixelSize: 22 * Theme.uiScale + color: wallpaperButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter } MouseArea { - id: bluetoothButtonArea + id: wallpaperButtonArea anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - if (!bluetoothPanelLoader.active) - bluetoothPanelLoader.loading = true; - - if (bluetoothPanelLoader.item) - bluetoothPanelLoader.item.showAt(); - + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) + settingsModal.openSettings(6); } } StyledTooltip { - text: "Bluetooth" - targetItem: bluetoothButtonArea - tooltipVisible: bluetoothButtonArea.containsMouse + text: "Wallpaper" + targetItem: wallpaperButtonArea + tooltipVisible: wallpaperButtonArea.containsMouse } } @@ -384,43 +372,7 @@ PanelWithOverlay { } - Item { - Layout.fillHeight: true - } - // QuickAccess widget - QuickAccess { - // 6 is the wallpaper tab index - - id: quickAccessWidget - - Layout.alignment: Qt.AlignHCenter - Layout.topMargin: -16 - z: 2 - isRecording: sidebarPopupRect.isRecording - onRecordingRequested: { - sidebarPopupRect.startRecording(); - } - onStopRecordingRequested: { - sidebarPopupRect.stopRecording(); - } - onRecordingStateMismatch: function(actualState) { - isRecording = actualState; - quickAccessWidget.isRecording = actualState; - } - onSettingsRequested: { - // Use the SettingsModal's openSettings function - if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) - settingsModal.openSettings(); - - } - onWallpaperSelectorRequested: { - // Use the SettingsModal's openSettings function with wallpaper tab (index 6) - if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) - settingsModal.openSettings(6); - - } - } } @@ -448,7 +400,7 @@ PanelWithOverlay { size: 1.1 fillColor: Theme.backgroundPrimary anchors.top: parent.top - offsetX: -447 + sidebarPopupRect.slideOffset + offsetX: -464 + sidebarPopupRect.slideOffset offsetY: 0 Behavior on offsetX { diff --git a/Widgets/SidePanel/PowerMenu.qml b/Widgets/SidePanel/PowerMenu.qml index 12d6da9..f1c3564 100644 --- a/Widgets/SidePanel/PowerMenu.qml +++ b/Widgets/SidePanel/PowerMenu.qml @@ -17,6 +17,7 @@ Rectangle { property string uptimeText: "--:--" property bool panelVisible: false + property var settingsModal: null function logout() { if (WorkspaceManager.isNiri) @@ -43,8 +44,8 @@ Rectangle { uptimeProcess.running = true; } - width: 440 - height: 80 + width: 440 * Theme.uiScale + height: 80 * Theme.uiScale color: "transparent" anchors.horizontalCenterOffset: -2 onPanelVisibleChanged: { @@ -61,29 +62,29 @@ Rectangle { anchors.fill: parent color: Theme.surface - radius: 18 + radius: 18 * Theme.uiScale ColumnLayout { anchors.fill: parent - anchors.margins: 18 - spacing: 12 + anchors.margins: 18 * Theme.uiScale + spacing: 12 * Theme.uiScale RowLayout { Layout.fillWidth: true - spacing: 12 + spacing: 12 * Theme.uiScale Rectangle { - width: 48 - height: 48 - radius: 24 + width: 48 * Theme.uiScale + height: 48 * Theme.uiScale + radius: 24 * Theme.uiScale color: Theme.accentPrimary Rectangle { anchors.fill: parent color: "transparent" - radius: 24 + radius: 24 * Theme.uiScale border.color: Theme.accentPrimary - border.width: 2 + border.width: 2 * Theme.uiScale z: 2 } @@ -93,13 +94,13 @@ Rectangle { } ColumnLayout { - spacing: 4 + spacing: 4 * Theme.uiScale Layout.fillWidth: true Text { text: Quickshell.env("USER") font.family: Theme.fontFamily - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale font.bold: true color: Theme.textPrimary } @@ -107,7 +108,7 @@ Rectangle { Text { text: "System Uptime: " + uptimeText font.family: Theme.fontFamily - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary } @@ -117,21 +118,62 @@ Rectangle { Layout.fillWidth: true } + Rectangle { + id: settingsButton + + width: 32 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale + color: settingsButtonArea.containsMouse || settingsButtonArea.pressed ? Theme.accentPrimary : "transparent" + border.color: Theme.accentPrimary + border.width: 1 * Theme.uiScale + + Text { + anchors.centerIn: parent + anchors.horizontalCenterOffset: -1 + text: "settings" + font.family: "Material Symbols Outlined" + font.pixelSize: 16 * Theme.uiScale + color: settingsButtonArea.containsMouse || settingsButtonArea.pressed ? Theme.backgroundPrimary : Theme.accentPrimary + } + + MouseArea { + id: settingsButtonArea + + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: { + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) + settingsModal.openSettings(); + } + } + + StyledTooltip { + id: settingsTooltip + + text: "Settings" + targetItem: settingsButton + tooltipVisible: settingsButtonArea.containsMouse + } + + } + Rectangle { id: systemButton - width: 32 - height: 32 - radius: 16 + width: 32 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale color: systemButtonArea.containsMouse || systemButtonArea.pressed ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary - border.width: 1 + border.width: 1 * Theme.uiScale Text { anchors.centerIn: parent text: "power_settings_new" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: systemButtonArea.containsMouse || systemButtonArea.pressed ? Theme.backgroundPrimary : Theme.accentPrimary } @@ -169,18 +211,18 @@ Rectangle { anchors.right: systemButton.right Rectangle { - width: 160 - height: 220 + width: 160 * Theme.uiScale + height: 220 * Theme.uiScale color: Theme.surface - radius: 8 + radius: 8 * Theme.uiScale border.color: Theme.outline - border.width: 1 + border.width: 1 * Theme.uiScale visible: true z: 9999 anchors.top: parent.top anchors.right: parent.right - anchors.rightMargin: 32 - anchors.topMargin: systemButton.y + systemButton.height + 48 + anchors.rightMargin: 32 * Theme.uiScale + anchors.topMargin: systemButton.y + systemButton.height + 48 * Theme.uiScale // Prevent closing when clicking in the panel bg MouseArea { @@ -189,31 +231,31 @@ Rectangle { ColumnLayout { anchors.fill: parent - anchors.margins: 8 - spacing: 4 + anchors.margins: 8 * Theme.uiScale + spacing: 4 * Theme.uiScale Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 - radius: 6 + Layout.preferredHeight: 36 * Theme.uiScale + radius: 6 * Theme.uiScale color: lockButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 - spacing: 8 + anchors.margins: 12 * Theme.uiScale + spacing: 8 * Theme.uiScale Text { text: "lock_outline" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: lockButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Lock Screen" font.family: Theme.fontFamily - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: lockButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -236,25 +278,25 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 - radius: 6 + Layout.preferredHeight: 36 * Theme.uiScale + radius: 6 * Theme.uiScale color: suspendButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 - spacing: 8 + anchors.margins: 12 * Theme.uiScale + spacing: 8 * Theme.uiScale Text { text: "bedtime" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: suspendButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Suspend" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: suspendButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -277,26 +319,26 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 - radius: 6 + Layout.preferredHeight: 36 * Theme.uiScale + radius: 6 * Theme.uiScale color: rebootButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 - spacing: 8 + anchors.margins: 12 * Theme.uiScale + spacing: 8 * Theme.uiScale Text { text: "refresh" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: rebootButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Reboot" font.family: Theme.fontFamily - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: rebootButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -319,25 +361,25 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 - radius: 6 + Layout.preferredHeight: 36 * Theme.uiScale + radius: 6 * Theme.uiScale color: logoutButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 - spacing: 8 + anchors.margins: 12 * Theme.uiScale + spacing: 8 * Theme.uiScale Text { text: "exit_to_app" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: logoutButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Logout" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: logoutButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -360,25 +402,25 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 - radius: 6 + Layout.preferredHeight: 36 * Theme.uiScale + radius: 6 * Theme.uiScale color: shutdownButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 - spacing: 8 + anchors.margins: 12 * Theme.uiScale + spacing: 8 * Theme.uiScale Text { text: "power_settings_new" font.family: "Material Symbols Outlined" - font.pixelSize: 16 + font.pixelSize: 16 * Theme.uiScale color: shutdownButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Shutdown" - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale color: shutdownButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } diff --git a/Widgets/SidePanel/PowerProfile.qml b/Widgets/SidePanel/PowerProfile.qml index bdd9c49..775046e 100644 --- a/Widgets/SidePanel/PowerProfile.qml +++ b/Widgets/SidePanel/PowerProfile.qml @@ -7,22 +7,22 @@ import qs.Components Rectangle { id: card - width: 200 - height: 70 + width: 200 * Theme.uiScale + height: 70 * Theme.uiScale color: Theme.surface - radius: 18 + radius: 18 * Theme.uiScale Row { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - spacing: 20 + spacing: 20 * Theme.uiScale Rectangle { - width: 36; height: 36 - radius: 18 + width: 36 * Theme.uiScale; height: 36 * Theme.uiScale + radius: 18 * Theme.uiScale border.color: Theme.accentPrimary - border.width: 1 + border.width: 1 * Theme.uiScale color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Performance) ? Theme.accentPrimary : (perfMouseArea.containsMouse ? Theme.accentPrimary : "transparent") @@ -33,7 +33,7 @@ Rectangle { anchors.centerIn: parent text: "speed" font.family: "Material Symbols Outlined" - font.pixelSize: 22 + font.pixelSize: 22 * Theme.uiScale color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Performance) || perfMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary @@ -65,10 +65,10 @@ Rectangle { Rectangle { - width: 36; height: 36 - radius: 18 + width: 36 * Theme.uiScale; height: 36 * Theme.uiScale + radius: 18 * Theme.uiScale border.color: Theme.accentPrimary - border.width: 1 + border.width: 1 * Theme.uiScale color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Balanced) ? Theme.accentPrimary : (balMouseArea.containsMouse ? Theme.accentPrimary : "transparent") @@ -79,7 +79,7 @@ Rectangle { anchors.centerIn: parent text: "balance" font.family: "Material Symbols Outlined" - font.pixelSize: 22 + font.pixelSize: 22 * Theme.uiScale color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Balanced) || balMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary @@ -111,10 +111,10 @@ Rectangle { Rectangle { - width: 36; height: 36 - radius: 18 + width: 36 * Theme.uiScale; height: 36 * Theme.uiScale + radius: 18 * Theme.uiScale border.color: Theme.accentPrimary - border.width: 1 + border.width: 1 * Theme.uiScale color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.PowerSaver) ? Theme.accentPrimary : (saveMouseArea.containsMouse ? Theme.accentPrimary : "transparent") @@ -125,7 +125,7 @@ Rectangle { anchors.centerIn: parent text: "eco" font.family: "Material Symbols Outlined" - font.pixelSize: 22 + font.pixelSize: 22 * Theme.uiScale color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.PowerSaver) || saveMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary diff --git a/Widgets/SidePanel/SettingsIcon.qml b/Widgets/SidePanel/SettingsIcon.qml index e2a383c..866db59 100644 --- a/Widgets/SidePanel/SettingsIcon.qml +++ b/Widgets/SidePanel/SettingsIcon.qml @@ -10,8 +10,8 @@ import qs.Components PanelWindow { id: settingsModal - implicitWidth: 480 - implicitHeight: 780 + implicitWidth: 480 * Theme.uiScale + implicitHeight: 780 * Theme.uiScale visible: false color: "transparent" anchors.top: true diff --git a/Widgets/SidePanel/SettingsModal.qml b/Widgets/SidePanel/SettingsModal.qml index 98f88c0..792f76e 100644 --- a/Widgets/SidePanel/SettingsModal.qml +++ b/Widgets/SidePanel/SettingsModal.qml @@ -10,8 +10,8 @@ import qs.Components PanelWindow { id: settingsModal - implicitWidth: 480 - implicitHeight: 780 + implicitWidth: 480 * Theme.uiScale + implicitHeight: 780 * Theme.uiScale visible: false color: "transparent" anchors.top: true diff --git a/Widgets/SidePanel/SystemMonitor.qml b/Widgets/SidePanel/SystemMonitor.qml index 8b0e3ee..59fc877 100644 --- a/Widgets/SidePanel/SystemMonitor.qml +++ b/Widgets/SidePanel/SystemMonitor.qml @@ -8,8 +8,8 @@ import qs.Settings Rectangle { id: systemMonitor - width: 70 - height: 250 + width: 70 * Theme.uiScale + height: 250 * Theme.uiScale color: "transparent" // Track visibility state for panel integration @@ -19,26 +19,26 @@ Rectangle { id: card anchors.fill: parent color: Theme.surface - radius: 18 + radius: 18 * Theme.uiScale ColumnLayout { anchors.fill: parent - anchors.margins: 8 - spacing: 12 + anchors.margins: 8 * Theme.uiScale + spacing: 12 * Theme.uiScale Layout.alignment: Qt.AlignVCenter // CPU usage indicator with circular progress bar Item { - width: 50; height: 50 + width: 50 * Theme.uiScale; height: 50 * Theme.uiScale CircularProgressBar { id: cpuBar progress: Sysinfo.cpuUsage / 100 - size: 50 - strokeWidth: 4 + size: 50 * Theme.uiScale + strokeWidth: 4 * Theme.uiScale hasNotch: true notchIcon: "speed" - notchIconSize: 14 + notchIconSize: 14 * Theme.uiScale Layout.alignment: Qt.AlignHCenter } MouseArea { @@ -60,16 +60,16 @@ Rectangle { // CPU temperature indicator with circular progress bar Item { - width: 50; height: 50 + width: 50 * Theme.uiScale; height: 50 * Theme.uiScale CircularProgressBar { id: tempBar progress: Sysinfo.cpuTemp / 100 - size: 50 - strokeWidth: 4 + size: 50 * Theme.uiScale + strokeWidth: 4 * Theme.uiScale hasNotch: true units: "°C" notchIcon: "thermometer" - notchIconSize: 14 + notchIconSize: 14 * Theme.uiScale Layout.alignment: Qt.AlignHCenter } MouseArea { @@ -91,15 +91,15 @@ Rectangle { // Memory usage indicator with circular progress bar Item { - width: 50; height: 50 + width: 50 * Theme.uiScale; height: 50 * Theme.uiScale CircularProgressBar { id: memBar progress: Sysinfo.memoryUsagePer / 100 - size: 50 - strokeWidth: 4 + size: 50 * Theme.uiScale + strokeWidth: 4 * Theme.uiScale hasNotch: true notchIcon: "memory" - notchIconSize: 14 + notchIconSize: 14 * Theme.uiScale Layout.alignment: Qt.AlignHCenter } MouseArea { @@ -121,15 +121,15 @@ Rectangle { // Disk usage indicator with circular progress bar Item { - width: 50; height: 50 + width: 50 * Theme.uiScale; height: 50 * Theme.uiScale CircularProgressBar { id: diskBar progress: Sysinfo.diskUsage / 100 - size: 50 - strokeWidth: 4 + size: 50 * Theme.uiScale + strokeWidth: 4 * Theme.uiScale hasNotch: true notchIcon: "storage" - notchIconSize: 14 + notchIconSize: 14 * Theme.uiScale Layout.alignment: Qt.AlignHCenter } MouseArea { diff --git a/Widgets/SidePanel/Weather.qml b/Widgets/SidePanel/Weather.qml index 631d22a..1c77192 100644 --- a/Widgets/SidePanel/Weather.qml +++ b/Widgets/SidePanel/Weather.qml @@ -6,8 +6,8 @@ import "../../Helpers/Weather.js" as WeatherHelper Rectangle { id: weatherRoot - width: 440 - height: 180 + width: 440 * Theme.uiScale + height: 180 * Theme.uiScale color: "transparent" anchors.horizontalCenterOffset: -2 @@ -83,29 +83,29 @@ Rectangle { id: card anchors.fill: parent color: Theme.surface - radius: 18 + radius: 18 * Theme.uiScale ColumnLayout { anchors.fill: parent - anchors.margins: 18 - spacing: 12 + anchors.margins: 18 * Theme.uiScale + spacing: 12 * Theme.uiScale RowLayout { - spacing: 12 + spacing: 12 * Theme.uiScale Layout.fillWidth: true RowLayout { - spacing: 12 - Layout.preferredWidth: 140 + spacing: 12 * Theme.uiScale + Layout.preferredWidth: 140 * Theme.uiScale Text { id: weatherIcon text: isLoading ? "sync" : (weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud") font.family: "Material Symbols Outlined" - font.pixelSize: 28 + font.pixelSize: 28 * Theme.uiScale verticalAlignment: Text.AlignVCenter color: isLoading ? Theme.accentPrimary : Theme.accentPrimary Layout.alignment: Qt.AlignVCenter @@ -121,28 +121,28 @@ Rectangle { } ColumnLayout { - spacing: 2 + spacing: 2 * Theme.uiScale RowLayout { - spacing: 4 + spacing: 4 * Theme.uiScale Text { text: city font.family: Theme.fontFamily - font.pixelSize: 14 + font.pixelSize: 14 * Theme.uiScale font.bold: true color: Theme.textPrimary } Text { text: weatherData && weatherData.timezone_abbreviation ? `(${weatherData.timezone_abbreviation})` : "" font.family: Theme.fontFamily - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale color: Theme.textSecondary - leftPadding: 2 + leftPadding: 2 * Theme.uiScale } } Text { 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.pixelSize: 24 * Theme.uiScale font.bold: true color: Theme.textPrimary } @@ -157,16 +157,16 @@ Rectangle { Rectangle { width: parent.width - height: 1 + height: 1 * Theme.uiScale color: Qt.rgba(Theme.textSecondary.g, Theme.textSecondary.g, Theme.textSecondary.b, 0.12) Layout.fillWidth: true - Layout.topMargin: 2 - Layout.bottomMargin: 2 + Layout.topMargin: 2 * Theme.uiScale + Layout.bottomMargin: 2 * Theme.uiScale } RowLayout { - spacing: 12 + spacing: 12 * Theme.uiScale Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter visible: weatherData && weatherData.daily && weatherData.daily.time @@ -174,13 +174,13 @@ Rectangle { Repeater { model: weatherData && weatherData.daily && weatherData.daily.time ? 5 : 0 delegate: ColumnLayout { - spacing: 2 + spacing: 2 * Theme.uiScale Layout.alignment: Qt.AlignHCenter Text { text: Qt.formatDateTime(new Date(weatherData.daily.time[index]), "ddd") font.family: Theme.fontFamily - font.pixelSize: 12 + font.pixelSize: 12 * Theme.uiScale color: Theme.textSecondary horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter @@ -189,7 +189,7 @@ Rectangle { text: materialSymbolForCode(weatherData.daily.weathercode[index]) font.family: "Material Symbols Outlined" - font.pixelSize: 22 + font.pixelSize: 22 * Theme.uiScale color: Theme.accentPrimary horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter @@ -198,7 +198,7 @@ Rectangle { 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 + font.pixelSize: 12 * Theme.uiScale color: Theme.textPrimary horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter @@ -213,7 +213,7 @@ Rectangle { color: Theme.error visible: errorString !== "" font.family: Theme.fontFamily - font.pixelSize: 10 + font.pixelSize: 10 * Theme.uiScale horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter } diff --git a/shell.qml b/shell.qml index 8f44ca3..fbdc1c9 100644 --- a/shell.qml +++ b/shell.qml @@ -213,4 +213,18 @@ Scope { } } } + + Rectangle { + width: 200 * Theme.uiScale + height: 40 * Theme.uiScale + color: "#333" + anchors.top: parent.top + anchors.left: parent.left + Text { + text: "UI Scale Demo" + anchors.centerIn: parent + font.pixelSize: 16 * Theme.uiScale + color: "white" + } + } } \ No newline at end of file From bf0e06ef0351993d9383e005080481d1867cbff3 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 10:01:38 -0400 Subject: [PATCH 21/43] Removed dead code, semiBold icons in the Settings tabs --- Widgets/SettingsWindow/SettingsWindow.qml | 3 +-- .../Tabs/Components/WallpaperSelector.qml | 4 +++- Widgets/SettingsWindow/Tabs/General.qml | 2 -- shell.qml | 14 -------------- 4 files changed, 4 insertions(+), 19 deletions(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 625d579..a7de10d 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -341,8 +341,6 @@ PanelWithOverlay { // Wallpaper Selector Component WallpaperSelector { id: wallpaperSelector - - anchors.fill: parent } } @@ -441,6 +439,7 @@ PanelWithOverlay { Layout.alignment: Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter + font.variableAxes: { "wght": (Font.Normal + Font.Bold) / 2.0 } } Label { diff --git a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml index 9ccb6fb..6fb1fd9 100644 --- a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml @@ -16,7 +16,9 @@ Rectangle { wallpaperOverlay.visible = true; } - anchors.fill: parent + Layout.fillWidth: true + Layout.fillHeight: true + color: Theme.backgroundPrimary visible: false z: 1000 diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index ab9b03d..a600643 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -24,8 +24,6 @@ ScrollView { Layout.preferredHeight: 0 } - - ColumnLayout { spacing: 4 * Theme.uiScale Layout.fillWidth: true diff --git a/shell.qml b/shell.qml index fbdc1c9..8f44ca3 100644 --- a/shell.qml +++ b/shell.qml @@ -213,18 +213,4 @@ Scope { } } } - - Rectangle { - width: 200 * Theme.uiScale - height: 40 * Theme.uiScale - color: "#333" - anchors.top: parent.top - anchors.left: parent.left - Text { - text: "UI Scale Demo" - anchors.centerIn: parent - font.pixelSize: 16 * Theme.uiScale - color: "white" - } - } } \ No newline at end of file From bd4d28f02f59a9d119daaf15ae2877dfa4997cc7 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 17:03:29 +0200 Subject: [PATCH 22/43] Fix for QS crash on monitor wake, might reintroduce notifications turning into their own window though... we'll see --- shell.qml | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/shell.qml b/shell.qml index 8f44ca3..6761752 100644 --- a/shell.qml +++ b/shell.qml @@ -187,9 +187,12 @@ Scope { function onScreensChanged() { if (lockScreen.locked) { pendingReload = true; - } else { + } /*else { reloadTimer.restart(); - } + } */ + // ^commented out for now to fix QS crash on monitor wake. + // if it reintroduces the notification bug (https://github.com/Ly-sec/Noctalia/issues/32)... + // we need to find a different fix } } @@ -213,4 +216,18 @@ Scope { } } } + + Rectangle { + width: 200 * Theme.uiScale + height: 40 * Theme.uiScale + color: "#333" + anchors.top: parent.top + anchors.left: parent.left + Text { + text: "UI Scale Demo" + anchors.centerIn: parent + font.pixelSize: 16 * Theme.uiScale + color: "white" + } + } } \ No newline at end of file From 34148f4676a0fe9288c3a56d286c5fe2c98acbbf Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 11:04:39 -0400 Subject: [PATCH 23/43] New Toggle Component and Adapted the General Settings Tab --- Components/Toggle.qml | 88 +++++ Widgets/SettingsWindow/Tabs/General.qml | 433 ++++++------------------ 2 files changed, 199 insertions(+), 322 deletions(-) create mode 100644 Components/Toggle.qml diff --git a/Components/Toggle.qml b/Components/Toggle.qml new file mode 100644 index 0000000..d2d0ce8 --- /dev/null +++ b/Components/Toggle.qml @@ -0,0 +1,88 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Components +import qs.Settings + +ColumnLayout { + id: root + + property string label: "" + property string description: "" + property bool value: false + property var onToggled: function() { + } + + Layout.fillWidth: true + + RowLayout { + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 * Theme.uiScale + Layout.fillWidth: true + + Text { + text: label + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: description + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: switcher + + width: 52 * Theme.uiScale + height: 32 * Theme.uiScale + radius: 16 * Theme.uiScale + color: value ? Theme.accentPrimary : Theme.surfaceVariant + border.color: value ? Theme.accentPrimary : Theme.outline + border.width: 2 * Theme.uiScale + + Rectangle { + width: 28 * Theme.uiScale + height: 28 * Theme.uiScale + radius: 14 * Theme.uiScale + color: Theme.surface + border.color: Theme.outline + border.width: 1 * Theme.uiScale + y: 2 + x: value ? switcher.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + root.onToggled(); + } + } + + } + + } + + Rectangle { + height: 8 * Theme.uiScale + } + +} diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index a600643..6a064e2 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -4,115 +4,108 @@ import QtQuick.Layouts import qs.Components import qs.Settings -ScrollView { - anchors.fill: parent - padding: 0 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - - ColumnLayout { - id: root - width: parent.availableWidth - spacing: 0 * Theme.uiScale - anchors.top: parent.top - anchors.margins: 0 * Theme.uiScale +ColumnLayout { + id: root - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + ScrollView { + id: scrollView + + Layout.fillWidth: true + Layout.fillHeight: true + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - spacing: 4 * Theme.uiScale - Layout.fillWidth: true + width: scrollView.availableWidth + spacing: 0 Text { text: "Profile" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 8 + Layout.bottomMargin: 16 * Theme.uiScale } - ColumnLayout { - spacing: 2 * Theme.uiScale + Text { + text: "Profile Image" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 4 * Theme.uiScale + } + + Text { + text: "Your profile picture displayed in various places throughout the shell" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + Layout.bottomMargin: 4 + } + + RowLayout { + spacing: 8 * Theme.uiScale Layout.fillWidth: true - Text { - text: "Profile Image" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Your profile picture displayed in various places throughout the shell" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - Layout.bottomMargin: 4 - } - - RowLayout { - spacing: 8 * Theme.uiScale - Layout.fillWidth: true + Rectangle { + width: 48 * Theme.uiScale + height: 48 * Theme.uiScale + radius: 24 * Theme.uiScale Rectangle { - width: 48 * Theme.uiScale - height: 48 * Theme.uiScale + anchors.fill: parent + color: "transparent" radius: 24 * Theme.uiScale - - Rectangle { - anchors.fill: parent - color: "transparent" - radius: 24 * Theme.uiScale - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale - z: 2 - } - - Avatar { - } - + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 2 * Theme.uiScale + z: 2 } - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 * Theme.uiScale - radius: 16 * Theme.uiScale - color: Theme.surfaceVariant - border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 * Theme.uiScale + Avatar { + } - TextInput { - id: profileImageInput + } + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 * Theme.uiScale + radius: 16 * Theme.uiScale + color: Theme.surfaceVariant + border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 * Theme.uiScale + + TextInput { + id: profileImageInput + + anchors.fill: parent + anchors.leftMargin: 12 * Theme.uiScale + anchors.rightMargin: 12 * Theme.uiScale + anchors.topMargin: 6 * Theme.uiScale + anchors.bottomMargin: 6 * Theme.uiScale + text: Settings.settings.profileImage + font.pixelSize: 13 * Theme.uiScale + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.profileImage = text; + } + + MouseArea { anchors.fill: parent - anchors.leftMargin: 12 * Theme.uiScale - anchors.rightMargin: 12 * Theme.uiScale - anchors.topMargin: 6 * Theme.uiScale - anchors.bottomMargin: 6 * Theme.uiScale - text: Settings.settings.profileImage - font.pixelSize: 13 * Theme.uiScale - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.profileImage = text; - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: profileImageInput.forceActiveFocus() - } - + cursorShape: Qt.IBeamCursor + onClicked: profileImageInput.forceActiveFocus() } } @@ -121,257 +114,53 @@ ScrollView { } - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 * Theme.uiScale - Layout.bottomMargin: 18 * Theme.uiScale - height: 1 * Theme.uiScale - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 * Theme.uiScale - Layout.fillWidth: true + // Separator + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 * Theme.uiScale + Layout.bottomMargin: 18 * Theme.uiScale + height: 1 * Theme.uiScale + color: Theme.outline + opacity: 0.3 + } Text { text: "User Interface" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 8 + Layout.bottomMargin: 16 * Theme.uiScale } - ColumnLayout { - spacing: 4 * Theme.uiScale - Layout.fillWidth: true - - RowLayout { - spacing: 8 * Theme.uiScale - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 * Theme.uiScale - Layout.fillWidth: true - - Text { - text: "Show Corners" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display rounded corners" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: cornersSwitch - - width: 52 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale - color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showCorners ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale - - Rectangle { - id: cornersThumb - - width: 28 * Theme.uiScale - height: 28 * Theme.uiScale - radius: 14 * Theme.uiScale - color: Theme.surface - border.color: Theme.outline - border.width: 1 * Theme.uiScale - y: 2 - x: Settings.settings.showCorners ? cornersSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showCorners = !Settings.settings.showCorners; - } - } - - } - + Toggle { + label: "Show Corners" + description: "Display rounded corners" + value: Settings.settings.showCorners + onToggled: function() { + Settings.settings.showCorners = !Settings.settings.showCorners; } - } - ColumnLayout { - spacing: 8 * Theme.uiScale - Layout.fillWidth: true - Layout.topMargin: 4 * Theme.uiScale - - RowLayout { - spacing: 8 * Theme.uiScale - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 * Theme.uiScale - Layout.fillWidth: true - - Text { - text: "Show Dock" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display a dock at the bottom of the screen for quick access to applications" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: dockSwitch - - width: 52 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale - color: Settings.settings.showDock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showDock ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale - - Rectangle { - id: dockThumb - - width: 28 * Theme.uiScale - height: 28 * Theme.uiScale - radius: 14 * Theme.uiScale - color: Theme.surface - border.color: Theme.outline - border.width: 1 * Theme.uiScale - y: 2 - x: Settings.settings.showDock ? dockSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showDock = !Settings.settings.showDock; - } - } - - } - + Toggle { + label: "Show Dock" + description: "Display a dock at the bottom of the screen for quick access to applications" + value: Settings.settings.showDock + onToggled: function() { + Settings.settings.showDock = !Settings.settings.showDock; } - } - ColumnLayout { - spacing: 8 * Theme.uiScale - Layout.fillWidth: true - Layout.topMargin: 4 * Theme.uiScale - - RowLayout { - spacing: 8 * Theme.uiScale - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 * Theme.uiScale - Layout.fillWidth: true - - Text { - text: "Dim Desktop" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Dim the desktop when panels or menus are open" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: dimSwitch - - width: 52 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale - color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.dimPanels ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale - - Rectangle { - id: dimThumb - - width: 28 * Theme.uiScale - height: 28 * Theme.uiScale - radius: 14 * Theme.uiScale - color: Theme.surface - border.color: Theme.outline - border.width: 1 * Theme.uiScale - y: 2 - x: Settings.settings.dimPanels ? dimSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.dimPanels = !Settings.settings.dimPanels; - } - } - - } - + Toggle { + label: "Dim Desktop" + description: "Dim the desktop when panels or menus are open" + value: Settings.settings.dimPanels + onToggled: function() { + Settings.settings.dimPanels = !Settings.settings.dimPanels; } - } + } } } -} + From 19431fa6a2dc92c746b9deff45af6a4f9dcf4e40 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 11:21:17 -0400 Subject: [PATCH 24/43] Settings: cleaned Bar tabs --- Components/{Toggle.qml => ToggleOption.qml} | 0 Widgets/SettingsWindow/Tabs/Bar.qml | 406 +++----------------- Widgets/SettingsWindow/Tabs/General.qml | 12 +- 3 files changed, 57 insertions(+), 361 deletions(-) rename Components/{Toggle.qml => ToggleOption.qml} (100%) diff --git a/Components/Toggle.qml b/Components/ToggleOption.qml similarity index 100% rename from Components/Toggle.qml rename to Components/ToggleOption.qml diff --git a/Widgets/SettingsWindow/Tabs/Bar.qml b/Widgets/SettingsWindow/Tabs/Bar.qml index 874591e..d0966fe 100644 --- a/Widgets/SettingsWindow/Tabs/Bar.qml +++ b/Widgets/SettingsWindow/Tabs/Bar.qml @@ -1,390 +1,86 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import qs.Settings import qs.Components +import qs.Settings -ScrollView { +ColumnLayout { + id: root + + spacing: 0 anchors.fill: parent - padding: 0 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - - ColumnLayout { - id: root - width: parent.availableWidth - spacing: 0 + anchors.margins: 0 + + ScrollView { + id: scrollView + Layout.fillWidth: true Layout.fillHeight: true - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } - + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - spacing: 4 - Layout.fillWidth: true + width: scrollView.availableWidth + spacing: 0 Text { text: "Elements" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 8 + Layout.bottomMargin: 16 * Theme.uiScale } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Active Window Icon" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display the icon of the currently focused window in the bar" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: activeWindowIconSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showActiveWindowIcon ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: activeWindowIconThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showActiveWindowIcon ? activeWindowIconSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; - } - } - } + ToggleOption { + label: "Show Active Window" + description: "Display the title of the currently focused window below the bar" + value: Settings.settings.showActiveWindow + onToggled: function() { + Settings.settings.showActiveWindow = !Settings.settings.showActiveWindow; } } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Active Window" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display the title of the currently focused window below the bar" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: activeWindowSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showActiveWindow ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: activeWindowThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showActiveWindow ? activeWindowSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showActiveWindow = !Settings.settings.showActiveWindow; - } - } - } + ToggleOption { + label: "Show Active Window Icon" + description: "Display the icon of the currently focused window" + value: Settings.settings.showActiveWindowIcon + onToggled: function() { + Settings.settings.showActiveWindowIcon = !Settings.settings.showActiveWindowIcon; } } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show System Info" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display system information (CPU, RAM, etc.) in the bar" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: systemInfoSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showSystemInfoInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: systemInfoThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showSystemInfoInBar ? systemInfoSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; - } - } - } + ToggleOption { + label: "Show System Info" + description: "Display system information (CPU, RAM, Temperature)" + value: Settings.settings.showSystemInfoInBar + onToggled: function() { + Settings.settings.showSystemInfoInBar = !Settings.settings.showSystemInfoInBar; } } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Taskbar" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display a taskbar showing currently open windows" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: taskbarSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showTaskbar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: taskbarThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showTaskbar ? taskbarSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showTaskbar = !Settings.settings.showTaskbar; - } - } - } + ToggleOption { + label: "Show Taskbar" + description: "Display a taskbar showing currently open windows" + value: Settings.settings.showTaskbar + onToggled: function() { + Settings.settings.showTaskbar = !Settings.settings.showTaskbar; } } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Media" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display media controls and information in the bar" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - } - - Rectangle { - id: mediaSwitch - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showMediaInBar ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: mediaThumb - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showMediaInBar ? mediaSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; - } - } - } + ToggleOption { + label: "Show Media" + description: "Display media controls and information" + value: Settings.settings.showMediaInBar + onToggled: function() { + Settings.settings.showMediaInBar = !Settings.settings.showMediaInBar; } } + } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } } -} \ No newline at end of file + +} diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index 6a064e2..c6058fc 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -132,7 +132,7 @@ ColumnLayout { Layout.bottomMargin: 16 * Theme.uiScale } - Toggle { + ToggleOption { label: "Show Corners" description: "Display rounded corners" value: Settings.settings.showCorners @@ -141,16 +141,16 @@ ColumnLayout { } } - Toggle { + ToggleOption { label: "Show Dock" description: "Display a dock at the bottom of the screen for quick access to applications" - value: Settings.settings.showDock + value: Settings.settings.showDock onToggled: function() { - Settings.settings.showDock = !Settings.settings.showDock; + Settings.settings.showDock = !Settings.settings.showDock; } } - Toggle { + ToggleOption { label: "Dim Desktop" description: "Dim the desktop when panels or menus are open" value: Settings.settings.dimPanels @@ -158,9 +158,9 @@ ColumnLayout { Settings.settings.dimPanels = !Settings.settings.dimPanels; } } - } } } +} From a7e40e643bcf6a4b50b53ddbf2b47b0fb9a96b71 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 11:37:13 -0400 Subject: [PATCH 25/43] Settings: cleanup of TimeWeather and ScreenRecorder --- Widgets/SettingsWindow/SettingsWindow.qml | 2 +- .../Tabs/Components/UnitSelector.qml | 4 +- .../{Recording.qml => ScreenRecorder.qml} | 83 +----- Widgets/SettingsWindow/Tabs/TimeWeather.qml | 275 +++++------------- 4 files changed, 76 insertions(+), 288 deletions(-) rename Widgets/SettingsWindow/Tabs/{Recording.qml => ScreenRecorder.qml} (92%) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index a7de10d..5462e18 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -100,7 +100,7 @@ PanelWithOverlay { Component { id: recordingSettings - Recording { + ScreenRecorder { } } diff --git a/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml index f4263ee..60b3124 100644 --- a/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml @@ -1,8 +1,9 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import qs.Settings import qs.Components +import qs.Settings + Rectangle { id: root @@ -14,7 +15,6 @@ Rectangle { border.width: 1 * Theme.uiScale property bool useFahrenheit: Settings.settings.useFahrenheit - Rectangle { id: slider diff --git a/Widgets/SettingsWindow/Tabs/Recording.qml b/Widgets/SettingsWindow/Tabs/ScreenRecorder.qml similarity index 92% rename from Widgets/SettingsWindow/Tabs/Recording.qml rename to Widgets/SettingsWindow/Tabs/ScreenRecorder.qml index 34cb9ab..ccb9250 100644 --- a/Widgets/SettingsWindow/Tabs/Recording.qml +++ b/Widgets/SettingsWindow/Tabs/ScreenRecorder.qml @@ -16,7 +16,7 @@ ColumnLayout { Layout.fillWidth: true Layout.fillHeight: true - padding: 0 + padding: 16 rightPadding: 12 clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff @@ -687,6 +687,7 @@ ColumnLayout { spacing: 8 Layout.fillWidth: true Layout.topMargin: 8 + Layout.bottomMargin: 16 Text { text: "Color Range" @@ -789,82 +790,14 @@ ColumnLayout { } - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Show Cursor" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Record mouse cursor in the video" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: cursorSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.showCursor ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: cursorThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.showCursor ? cursorSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.showCursor = !Settings.settings.showCursor; - } - } - - } - + ToggleOption { + label: "Show Cursor" + description: "Record mouse cursor in the video" + value: Settings.settings.showCursor + onToggled: function() { + Settings.settings.showCursor = !Settings.settings.showCursor; } - } - } Item { diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml index 75921b6..3ae3205 100644 --- a/Widgets/SettingsWindow/Tabs/TimeWeather.qml +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -5,216 +5,75 @@ import qs.Components import qs.Settings import qs.Widgets.SettingsWindow.Tabs.Components -ScrollView { - anchors.fill: parent - padding: 0 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - - ColumnLayout { - id: root - width: parent.availableWidth - spacing: 0 - anchors.top: parent.top - anchors.margins: 0 +ColumnLayout { + id: root - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + + ScrollView { + id: scrollView + + Layout.fillWidth: true + Layout.fillHeight: true + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - spacing: 4 - Layout.fillWidth: true + width: scrollView.availableWidth + spacing: 0 Text { text: "Time" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 8 + Layout.bottomMargin: 16 * Theme.uiScale } - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use 12 Hour Clock" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: use12HourClockSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.use12HourClock ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: use12HourClockThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.use12HourClock ? use12HourClockSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.use12HourClock = !Settings.settings.use12HourClock; - } - } - - } - + ToggleOption { + label: "Use 12 Hour Clock" + description: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format" + value: Settings.settings.use12HourClock + onToggled: function() { + Settings.settings.use12HourClock = !Settings.settings.use12HourClock; } - } - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "US Style Date" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: reverseDayMonthSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.reverseDayMonth ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: reverseDayMonthThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.reverseDayMonth ? reverseDayMonthSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; - } - } - - } - + ToggleOption { + label: "US Style Date" + description: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY" + value: Settings.settings.reverseDayMonth + onToggled: function() { + Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth; } - } - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } Text { text: "Weather" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 8 + Layout.bottomMargin: 16 * Theme.uiScale } ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.bottomMargin: 8 * Theme.uiScale Text { text: "City" @@ -273,39 +132,39 @@ ScrollView { } - } - - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { + ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 - ColumnLayout { - spacing: 4 + RowLayout { + spacing: 8 Layout.fillWidth: true - Text { - text: "Temperature Unit" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Choose between Celsius and Fahrenheit" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap + ColumnLayout { + spacing: 4 Layout.fillWidth: true + + Text { + text: "Temperature Unit" + font.pixelSize: 13 * Theme.uiScale + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Choose between Celsius and Fahrenheit" + font.pixelSize: 12 * Theme.uiScale + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + } - } + UnitSelector { + } - UnitSelector { } } @@ -314,8 +173,4 @@ ScrollView { } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } } From 09706ccb28b7f998057811ee4b8b59c35c31d76d Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 11:43:56 -0400 Subject: [PATCH 26/43] Settings: cleanup Network tab --- Widgets/SettingsWindow/Tabs/Network.qml | 232 +++++------------------- 1 file changed, 45 insertions(+), 187 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index 8bf9dc6..5bc582c 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -6,223 +6,81 @@ import Quickshell.Bluetooth import qs.Components import qs.Settings -ScrollView { +ColumnLayout { + id: root + + spacing: 0 anchors.fill: parent - padding: 0 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - - ColumnLayout { - id: root - width: parent.availableWidth + anchors.margins: 0 + + ScrollView { + id: scrollView + Layout.fillWidth: true Layout.fillHeight: true - spacing: 24 - Component.onCompleted: { - Quickshell.execDetached(["nmcli", "-t", "-f", "WIFI", "radio"]); - } + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - spacing: 16 - Layout.fillWidth: true + width: scrollView.availableWidth + spacing: 0 Text { text: "Wi-Fi" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary + Layout.bottomMargin: 16 * Theme.uiScale } - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Enable Wi-Fi" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Turn Wi-Fi radio on or off" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: wifiSwitch - - property bool checked: Settings.settings.wifiEnabled - - width: 52 - height: 32 - radius: 16 - color: checked ? Theme.accentPrimary : Theme.surfaceVariant - border.color: checked ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wifiThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: wifiSwitch.checked ? wifiSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled; - Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"]); - } - } - - } - + ToggleOption { + label: "Enable Wi-Fi" + description: "Turn Wi-Fi radio on or off" + value: Settings.settings.wifiEnabled + onToggled: function() { + Settings.settings.wifiEnabled = !Settings.settings.wifiEnabled; + Quickshell.execDetached(["nmcli", "radio", "wifi", Settings.settings.wifiEnabled ? "on" : "off"]); } - } - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 0 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 16 - Layout.fillWidth: true + // Separator + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } Text { text: "Bluetooth" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary + Layout.bottomMargin: 16 * Theme.uiScale } - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Enable Bluetooth" - font.pixelSize: 13 * Theme.uiScale - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Turn Bluetooth radio on or off" - font.pixelSize: 12 * Theme.uiScale - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } + ToggleOption { + label: "Enable Bluetooth" + description: "Turn Bluetooth radio on or off" + value: Settings.settings.bluetoothEnabled + onToggled: function() { + if (Bluetooth.defaultAdapter) { + Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled; + Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled; + if (Bluetooth.defaultAdapter.enabled) + Bluetooth.defaultAdapter.discovering = true; } - - Rectangle { - id: bluetoothSwitch - - property bool checked: Settings.settings.bluetoothEnabled - - width: 52 - height: 32 - radius: 16 - color: checked ? Theme.accentPrimary : Theme.surfaceVariant - border.color: checked ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: bluetoothThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: bluetoothSwitch.checked ? bluetoothSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - if (Bluetooth.defaultAdapter) { - Settings.settings.bluetoothEnabled = !Settings.settings.bluetoothEnabled; - Bluetooth.defaultAdapter.enabled = Settings.settings.bluetoothEnabled; - if (Bluetooth.defaultAdapter.enabled) - Bluetooth.defaultAdapter.discovering = true; - - } - } - } - - } - } - } } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } } + } From 8b7d5d8b6a04ba408efc9d4f9ccae4a3e178bc9f Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 11:53:04 -0400 Subject: [PATCH 27/43] Settings: cleanup Display --- Widgets/SettingsWindow/Tabs/Display.qml | 78 ++++++++++++------------- 1 file changed, 38 insertions(+), 40 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/Display.qml b/Widgets/SettingsWindow/Tabs/Display.qml index bfe5aa6..b71b98b 100644 --- a/Widgets/SettingsWindow/Tabs/Display.qml +++ b/Widgets/SettingsWindow/Tabs/Display.qml @@ -2,60 +2,58 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import Quickshell -import qs.Settings import qs.Components +import qs.Settings -ScrollView { +ColumnLayout { + id: root + + // Get list of available monitors/screens + property var monitors: Quickshell.screens || [] + + // Sorted monitors by name + property var sortedMonitors: { + let sorted = [...monitors]; + sorted.sort((a, b) => { + let nameA = a.name || "Unknown"; + let nameB = b.name || "Unknown"; + return nameA.localeCompare(nameB); + }); + return sorted; + } + + spacing: 0 anchors.fill: parent - padding: 0 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - - ColumnLayout { - id: root - width: parent.availableWidth - spacing: 0 - anchors.top: parent.top - anchors.margins: 0 + anchors.margins: 0 - // Get list of available monitors/screens - property var monitors: Quickshell.screens || [] - - // Sorted monitors by name - property var sortedMonitors: { - let sorted = [...monitors]; - sorted.sort((a, b) => { - let nameA = a.name || "Unknown"; - let nameB = b.name || "Unknown"; - return nameA.localeCompare(nameB); - }); - return sorted; - } - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } + ScrollView { + id: scrollView + Layout.fillWidth: true + Layout.fillHeight: true + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - spacing: 12 - Layout.fillWidth: true + width: scrollView.availableWidth + spacing: 0 Text { text: "Monitor Selection" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 8 + Layout.bottomMargin: 16 * Theme.uiScale } - ColumnLayout { spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 + Layout.bottomMargin: 8 RowLayout { spacing: 8 @@ -159,6 +157,7 @@ ScrollView { spacing: 8 Layout.fillWidth: true Layout.topMargin: 8 + Layout.bottomMargin: 8 RowLayout { spacing: 8 @@ -265,6 +264,7 @@ ScrollView { spacing: 8 Layout.fillWidth: true Layout.topMargin: 8 + Layout.bottomMargin: 8 RowLayout { spacing: 8 @@ -365,11 +365,9 @@ ScrollView { } } } + } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } } + } \ No newline at end of file From 03043b283f1ec6e85bf61747860654e367f11e20 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 12:00:14 -0400 Subject: [PATCH 28/43] Settings: cleanup Misc --- Widgets/SettingsWindow/Tabs/Misc.qml | 67 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 33 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/Misc.qml b/Widgets/SettingsWindow/Tabs/Misc.qml index 30208f8..861d93d 100644 --- a/Widgets/SettingsWindow/Tabs/Misc.qml +++ b/Widgets/SettingsWindow/Tabs/Misc.qml @@ -1,43 +1,39 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts -import qs.Settings import qs.Components +import qs.Settings -ScrollView { +ColumnLayout { + id: root + + spacing: 0 anchors.fill: parent - padding: 0 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded - - ColumnLayout { - id: root - width: parent.availableWidth - spacing: 0 - anchors.top: parent.top - anchors.margins: 0 + anchors.margins: 0 - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 - } + ScrollView { + id: scrollView + Layout.fillWidth: true + Layout.fillHeight: true + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - spacing: 4 - Layout.fillWidth: true + width: scrollView.availableWidth + spacing: 0 Text { text: "Media" font.pixelSize: 18 * Theme.uiScale font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 8 + Layout.bottomMargin: 16 * Theme.uiScale } - ColumnLayout { spacing: 8 Layout.fillWidth: true @@ -60,10 +56,14 @@ ScrollView { ComboBox { id: visualizerTypeComboBox + Layout.fillWidth: true Layout.preferredHeight: 40 model: ["radial", "fire", "diamond"] currentIndex: model.indexOf(Settings.settings.visualizerType) + onActivated: { + Settings.settings.visualizerType = model[index]; + } background: Rectangle { implicitWidth: 120 @@ -97,7 +97,7 @@ ScrollView { y: visualizerTypeComboBox.height width: visualizerTypeComboBox.width implicitHeight: contentItem.implicitHeight - padding: 1 + padding: 8 contentItem: ListView { clip: true @@ -105,7 +105,9 @@ ScrollView { model: visualizerTypeComboBox.popup.visible ? visualizerTypeComboBox.delegateModel : null currentIndex: visualizerTypeComboBox.highlightedIndex - ScrollIndicator.vertical: ScrollIndicator {} + ScrollIndicator.vertical: ScrollIndicator { + } + } background: Rectangle { @@ -114,10 +116,13 @@ ScrollView { border.width: 1 radius: 16 } + } delegate: ItemDelegate { width: visualizerTypeComboBox.width + highlighted: visualizerTypeComboBox.highlightedIndex === index + contentItem: Text { text: modelData.charAt(0).toUpperCase() + modelData.slice(1) font.pixelSize: 13 @@ -125,23 +130,19 @@ ScrollView { verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } - highlighted: visualizerTypeComboBox.highlightedIndex === index background: Rectangle { color: highlighted ? Theme.accentPrimary.toString().replace(/#/, "#1A") : "transparent" } + } - onActivated: { - Settings.settings.visualizerType = model[index]; - } } + } + } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } } -} \ No newline at end of file + +} From 922a8183d2fd2e1b44d1bfa71fe0ed88f9e7eb1a Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 12:50:23 -0400 Subject: [PATCH 29/43] Settings: cleanup About Gridview could need a little love --- Widgets/SettingsWindow/Tabs/About.qml | 237 +++++++++++++------------- 1 file changed, 115 insertions(+), 122 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/About.qml b/Widgets/SettingsWindow/Tabs/About.qml index bb9bca7..2e60129 100644 --- a/Widgets/SettingsWindow/Tabs/About.qml +++ b/Widgets/SettingsWindow/Tabs/About.qml @@ -7,7 +7,7 @@ import Quickshell.Io import qs.Components import qs.Settings -Item { +ColumnLayout { id: root property string latestVersion: "Unknown" @@ -44,6 +44,10 @@ Item { }); } + spacing: 0 + anchors.fill: parent + anchors.margins: 0 + Process { id: currentVersionProcess @@ -148,31 +152,35 @@ Item { } ScrollView { - anchors.fill: parent - padding: 0 + id: scrollView + + Layout.fillWidth: true + Layout.fillHeight: true + padding: 16 rightPadding: 12 clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff ScrollBar.vertical.policy: ScrollBar.AsNeeded ColumnLayout { - id: mainLayout - - width: parent.availableWidth - anchors.top: parent.top - spacing: 8 - - Item { - Layout.fillWidth: true - Layout.preferredHeight: 16 - } + width: scrollView.availableWidth + spacing: 0 Text { - text: "Noctalia" + text: "Noctalia: quiet by design" font.pixelSize: 24 * Theme.uiScale font.bold: true color: Theme.textPrimary Layout.alignment: Qt.AlignCenter + Layout.bottomMargin: 8 * Theme.uiScale + } + + Text { + text: "It may just be another quickshell setup but it won't get in your way." + font.pixelSize: 14 * Theme.uiScale + color: Theme.textSecondary + Layout.alignment: Qt.AlignCenter + Layout.bottomMargin: 16 * Theme.uiScale } GridLayout { @@ -273,14 +281,7 @@ Item { } - Text { - text: "Description something something <.< I hate writing text..." - font.pixelSize: 14 * Theme.uiScale - color: Theme.textSecondary - Layout.alignment: Qt.AlignCenter - Layout.topMargin: 24 - } - + // Separator Rectangle { Layout.fillWidth: true Layout.topMargin: 26 @@ -292,8 +293,10 @@ Item { ColumnLayout { Layout.fillWidth: true + Layout.fillHeight: true Layout.leftMargin: 32 Layout.rightMargin: 32 + Layout.alignment: Qt.AlignCenter spacing: 16 RowLayout { @@ -315,124 +318,114 @@ Item { } - ScrollView { - Layout.fillWidth: true - Layout.preferredHeight: 300 - clip: true + GridView { + id: contributorsGrid - Item { - anchors.fill: parent + Layout.leftMargin: 32 + Layout.rightMargin: 32 + Layout.alignment: Qt.AlignCenter + width: 200 * 3 + height: 300 + cellWidth: 200 + cellHeight: 100 + model: root.contributors - GridView { - id: contributorsGrid + delegate: Rectangle { + width: contributorsGrid.cellWidth - 8 + height: contributorsGrid.cellHeight - 4 + radius: 20 + color: contributorArea.containsMouse ? Theme.highlight : "transparent" - anchors.centerIn: parent - width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200) - height: parent.height - cellWidth: 200 - cellHeight: 100 - model: root.contributors + RowLayout { + anchors.fill: parent + anchors.margins: 8 + spacing: 12 - delegate: Rectangle { - width: contributorsGrid.cellWidth - 4 - height: contributorsGrid.cellHeight - 10 - radius: 20 - color: contributorArea.containsMouse ? Theme.highlight : "transparent" + Item { + Layout.alignment: Qt.AlignVCenter + Layout.preferredWidth: 40 + Layout.preferredHeight: 40 + + Image { + id: avatarImage - RowLayout { anchors.fill: parent - anchors.margins: 8 - spacing: 12 + source: modelData.avatar_url || "" + sourceSize: Qt.size(80, 80) + visible: false + mipmap: true + smooth: true + asynchronous: true + fillMode: Image.PreserveAspectCrop + cache: true + } - Item { - Layout.alignment: Qt.AlignVCenter - Layout.preferredWidth: 40 - Layout.preferredHeight: 40 + MultiEffect { + anchors.fill: parent + source: avatarImage + maskEnabled: true + maskSource: mask + } - Image { - id: avatarImage + Item { + id: mask - anchors.fill: parent - source: modelData.avatar_url || "" - sourceSize: Qt.size(80, 80) - visible: false - mipmap: true - smooth: true - asynchronous: true - fillMode: Image.PreserveAspectCrop - cache: true - } - - MultiEffect { - anchors.fill: parent - source: avatarImage - maskEnabled: true - maskSource: mask - } - - Item { - id: mask - - anchors.fill: parent - layer.enabled: true - visible: false - - Rectangle { - anchors.fill: parent - radius: avatarImage.width / 2 - } - - } - - Text { - anchors.centerIn: parent - text: "person" - font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale - color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary - visible: !avatarImage.source || avatarImage.status !== Image.Ready - } - - } - - ColumnLayout { - spacing: 4 - Layout.alignment: Qt.AlignVCenter - Layout.fillWidth: true - - Text { - text: modelData.login || "Unknown" - font.pixelSize: 13 * Theme.uiScale - color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary - elide: Text.ElideRight - Layout.fillWidth: true - } - - Text { - text: (modelData.contributions || 0) + " commits" - font.pixelSize: 11 * Theme.uiScale - color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary - } + anchors.fill: parent + layer.enabled: true + visible: false + Rectangle { + anchors.fill: parent + radius: avatarImage.width / 2 } } - MouseArea { - id: contributorArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: { - if (modelData.html_url) - Quickshell.execDetached(["xdg-open", modelData.html_url]); - - } + Text { + anchors.centerIn: parent + text: "person" + font.family: "Material Symbols Outlined" + font.pixelSize: 24 * Theme.uiScale + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary + visible: !avatarImage.source || avatarImage.status !== Image.Ready } } + ColumnLayout { + spacing: 4 + Layout.alignment: Qt.AlignVCenter + Layout.fillWidth: true + + Text { + text: modelData.login || "Unknown" + font.pixelSize: 13 * Theme.uiScale + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary + elide: Text.ElideRight + Layout.fillWidth: true + } + + Text { + text: (modelData.contributions || 0) + " commits" + font.pixelSize: 11 * Theme.uiScale + color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary + } + + } + + } + + MouseArea { + id: contributorArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + if (modelData.html_url) + Quickshell.execDetached(["xdg-open", modelData.html_url]); + + } } } From 1d409531a41c462d4915a8418921a07ed54a0e86 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 12:51:48 -0400 Subject: [PATCH 30/43] Removed dead code in sheel.qml --- shell.qml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/shell.qml b/shell.qml index 6761752..e5c3f0e 100644 --- a/shell.qml +++ b/shell.qml @@ -217,17 +217,4 @@ Scope { } } - Rectangle { - width: 200 * Theme.uiScale - height: 40 * Theme.uiScale - color: "#333" - anchors.top: parent.top - anchors.left: parent.left - Text { - text: "UI Scale Demo" - anchors.centerIn: parent - font.pixelSize: 16 * Theme.uiScale - color: "white" - } - } } \ No newline at end of file From 2a897060a7b4b587dee1889b3628e0cf59ea8c5b Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 12:54:33 -0400 Subject: [PATCH 31/43] Removed QuickAccess Widget which is no longer used --- Widgets/SidePanel/PanelPopup.qml | 10 -- Widgets/SidePanel/QuickAccess.qml | 200 ------------------------------ 2 files changed, 210 deletions(-) delete mode 100644 Widgets/SidePanel/QuickAccess.qml diff --git a/Widgets/SidePanel/PanelPopup.qml b/Widgets/SidePanel/PanelPopup.qml index 60867d9..d62d31b 100644 --- a/Widgets/SidePanel/PanelPopup.qml +++ b/Widgets/SidePanel/PanelPopup.qml @@ -79,10 +79,6 @@ PanelWithOverlay { if (systemWidget) systemWidget.panelVisible = true; - - if (quickAccessWidget) - quickAccessWidget.panelVisible = true; - } } @@ -116,7 +112,6 @@ PanelWithOverlay { var command = "gpu-screen-recorder -w portal" + " -f " + Settings.settings.recordingFrameRate + " -a default_output" + " -k " + Settings.settings.recordingCodec + " -ac " + Settings.settings.audioCodec + " -q " + Settings.settings.recordingQuality + " -cursor " + (Settings.settings.showCursor ? "yes" : "no") + " -cr " + Settings.settings.colorRange + " -o " + outputPath; Quickshell.execDetached(["sh", "-c", command]); isRecording = true; - quickAccessWidget.isRecording = true; } // Stop recording using Quickshell.execDetached @@ -129,7 +124,6 @@ PanelWithOverlay { cleanupTimer.destroy(); }); isRecording = false; - quickAccessWidget.isRecording = false; } implicitWidth: 500 * Theme.uiScale @@ -165,10 +159,6 @@ PanelWithOverlay { if (systemWidget) systemWidget.panelVisible = false; - - if (quickAccessWidget) - quickAccessWidget.panelVisible = false; - } sidebarPopupRect.isAnimating = false; } diff --git a/Widgets/SidePanel/QuickAccess.qml b/Widgets/SidePanel/QuickAccess.qml deleted file mode 100644 index 60b9a57..0000000 --- a/Widgets/SidePanel/QuickAccess.qml +++ /dev/null @@ -1,200 +0,0 @@ -import QtQuick -import QtQuick.Layouts -import QtQuick.Controls -import QtQuick.Effects -import Quickshell -import Quickshell.Io -import qs.Settings - -Rectangle { - id: quickAccessWidget - width: 440 - height: 80 - color: "transparent" - anchors.horizontalCenterOffset: -2 - - required property bool isRecording - - signal recordingRequested() - signal stopRecordingRequested() - signal recordingStateMismatch(bool actualState) - signal settingsRequested() - signal wallpaperRequested() - signal wallpaperSelectorRequested() - - Rectangle { - id: card - anchors.fill: parent - color: Theme.surface - radius: 18 - - RowLayout { - anchors.fill: parent - anchors.margins: 18 - spacing: 12 - - - Rectangle { - id: settingsButton - Layout.fillWidth: true - Layout.preferredHeight: 44 - radius: 12 - color: settingsButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - - Text { - text: "settings" - font.family: settingsButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 - color: settingsButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - } - - Text { - text: "Settings" - font.family: Theme.fontFamily - font.pixelSize: 14 - font.bold: true - color: settingsButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary - Layout.fillWidth: true - } - } - - MouseArea { - id: settingsButtonArea - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - settingsRequested() - } - } - } - - - Rectangle { - id: recorderButton - Layout.fillWidth: true - Layout.preferredHeight: 44 - radius: 12 - color: isRecording ? Theme.accentPrimary : - (recorderButtonArea.containsMouse ? Theme.accentPrimary : "transparent") - border.color: Theme.accentPrimary - border.width: 1 - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - - Text { - text: isRecording ? "radio_button_checked" : "radio_button_unchecked" - font.family: (isRecording || recorderButtonArea.containsMouse) ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 - color: isRecording || recorderButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - } - - Text { - text: isRecording ? "End" : "Record" - font.family: Theme.fontFamily - font.pixelSize: 14 - font.bold: true - color: isRecording || recorderButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary - Layout.fillWidth: true - } - } - - MouseArea { - id: recorderButtonArea - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - if (isRecording) { - stopRecordingRequested() - } else { - recordingRequested() - } - } - } - } - - - Rectangle { - id: wallpaperButton - Layout.fillWidth: true - Layout.preferredHeight: 44 - radius: 12 - color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" - border.color: Theme.accentPrimary - border.width: 1 - - RowLayout { - anchors.fill: parent - anchors.margins: 12 - spacing: 8 - - Text { - text: "image" - font.family: "Material Symbols Outlined" - font.pixelSize: 16 - color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary - } - - Text { - text: "Wallpaper" - font.family: Theme.fontFamily - font.pixelSize: 14 - font.bold: true - color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary - Layout.fillWidth: true - } - } - - MouseArea { - id: wallpaperButtonArea - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - hoverEnabled: true - onClicked: { - wallpaperSelectorRequested() - } - } - } - } - } - - - property bool panelVisible: false - - - Timer { - interval: 2000 - repeat: true - running: panelVisible - onTriggered: checkRecordingStatus() - } - - function checkRecordingStatus() { - if (isRecording) { - checkRecordingProcess.running = true - } - } - - - Process { - id: checkRecordingProcess - command: ["pgrep", "-f", "gpu-screen-recorder.*portal"] - onExited: function(exitCode, exitStatus) { - var isActuallyRecording = exitCode === 0 - if (isRecording && !isActuallyRecording) { - recordingStateMismatch(isActuallyRecording) - } - } - } -} \ No newline at end of file From dc77bc667a65d670303c048a2af7609c58556b6b Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 12:58:18 -0400 Subject: [PATCH 32/43] Dismiss SidePanel when opening settings / recording screen or select a wallpaper --- Widgets/SidePanel/PanelPopup.qml | 6 +++++- Widgets/SidePanel/SettingsIcon.qml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Widgets/SidePanel/PanelPopup.qml b/Widgets/SidePanel/PanelPopup.qml index d62d31b..1a69956 100644 --- a/Widgets/SidePanel/PanelPopup.qml +++ b/Widgets/SidePanel/PanelPopup.qml @@ -301,8 +301,10 @@ PanelWithOverlay { onClicked: { if (sidebarPopupRect.isRecording) { sidebarPopupRect.stopRecording(); + sidebarPopup.dismiss(); } else { sidebarPopupRect.startRecording(); + sidebarPopup.dismiss(); } } } @@ -343,8 +345,10 @@ PanelWithOverlay { hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: { - if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) + if (typeof settingsModal !== 'undefined' && settingsModal && settingsModal.openSettings) { settingsModal.openSettings(6); + sidebarPopup.dismiss(); + } } } diff --git a/Widgets/SidePanel/SettingsIcon.qml b/Widgets/SidePanel/SettingsIcon.qml index 866db59..dc12ac2 100644 --- a/Widgets/SidePanel/SettingsIcon.qml +++ b/Widgets/SidePanel/SettingsIcon.qml @@ -57,6 +57,7 @@ PanelWindow { } }); } + sidebarPopup.dismiss(); } else if (settingsWindow.visible) { // Close and destroy window var windowToDestroy = settingsWindow; From 0992237411899979f3f110ae941c6cd8a62e441b Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 19:20:07 +0200 Subject: [PATCH 33/43] Fix wallpaper selector height --- Widgets/SettingsWindow/SettingsWindow.qml | 8 ++++---- .../SettingsWindow/Tabs/Components/WallpaperSelector.qml | 3 --- shell.qml | 3 --- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 5462e18..efb2e3c 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -317,7 +317,7 @@ PanelWithOverlay { } - ColumnLayout { + Item { id: settingsContainer anchors { @@ -332,15 +332,15 @@ PanelWithOverlay { Loader { id: settingsLoader - Layout.fillWidth: true - Layout.fillHeight: true + anchors.fill: parent sourceComponent: generalSettings active: true } - // Wallpaper Selector Component + // Wallpaper Selector Component - positioned as overlay WallpaperSelector { id: wallpaperSelector + anchors.fill: parent } } diff --git a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml index 6fb1fd9..c70f783 100644 --- a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml @@ -16,9 +16,6 @@ Rectangle { wallpaperOverlay.visible = true; } - Layout.fillWidth: true - Layout.fillHeight: true - color: Theme.backgroundPrimary visible: false z: 1000 diff --git a/shell.qml b/shell.qml index e5c3f0e..e79abc0 100644 --- a/shell.qml +++ b/shell.qml @@ -13,9 +13,6 @@ import qs.Widgets.SettingsWindow import qs.Settings import qs.Helpers -import "./Helpers/IdleInhibitor.qml" -import "./Helpers/IPCHandlers.qml" - Scope { id: root From 59f2e9d71f702f57846168951dfe377860bc77f2 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 13:34:14 -0400 Subject: [PATCH 34/43] Settings: fix padding in wallpaper tabs --- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index 8956a68..2debf46 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -19,7 +19,7 @@ ScrollView { Layout.fillWidth: true Layout.fillHeight: true - padding: 0 + padding: 16 rightPadding: 12 clip: true ScrollBar.horizontal.policy: ScrollBar.AlwaysOff From 677bf8fbe162e2eca695f49adc2e62be215e8702 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 13:37:45 -0400 Subject: [PATCH 35/43] Settings: wallpaper, removed double ScrollView --- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 1517 ++++++++++----------- 1 file changed, 754 insertions(+), 763 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index 2debf46..228ff38 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -5,313 +5,710 @@ import qs.Components import qs.Services import qs.Settings -ScrollView { +ColumnLayout { + id: root + + spacing: 0 anchors.fill: parent - ColumnLayout { - id: root + anchors.margins: 0 - spacing: 0 - anchors.fill: parent - anchors.margins: 0 + ScrollView { + id: scrollView - ScrollView { - id: scrollView + Layout.fillWidth: true + Layout.fillHeight: true + padding: 16 + rightPadding: 12 + clip: true + ScrollBar.horizontal.policy: ScrollBar.AlwaysOff + ScrollBar.vertical.policy: ScrollBar.AsNeeded - Layout.fillWidth: true - Layout.fillHeight: true - padding: 16 - rightPadding: 12 - clip: true - ScrollBar.horizontal.policy: ScrollBar.AlwaysOff - ScrollBar.vertical.policy: ScrollBar.AsNeeded + ColumnLayout { + width: scrollView.availableWidth + spacing: 0 + + Item { + Layout.fillWidth: true + Layout.preferredHeight: 0 + } ColumnLayout { - width: scrollView.availableWidth - spacing: 0 + spacing: 4 + Layout.fillWidth: true - Item { - Layout.fillWidth: true - Layout.preferredHeight: 0 + Text { + text: "Wallpaper Settings" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 } + // Wallpaper Settings Category ColumnLayout { - spacing: 4 + spacing: 8 Layout.fillWidth: true + Layout.topMargin: 8 - Text { - text: "Wallpaper Settings" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - // Wallpaper Settings Category + // Wallpaper Folder ColumnLayout { spacing: 8 Layout.fillWidth: true - Layout.topMargin: 8 - - // Wallpaper Folder - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Wallpaper Folder" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Path to your wallpaper folder" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - TextInput { - id: folderInput - - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - text: Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "" - font.family: Theme.fontFamily - font.pixelSize: 13 - color: Theme.textPrimary - verticalAlignment: TextInput.AlignVCenter - clip: true - selectByMouse: true - activeFocusOnTab: true - inputMethodHints: Qt.ImhUrlCharactersOnly - onTextChanged: { - Settings.settings.wallpaperFolder = text; - } - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.IBeamCursor - onClicked: folderInput.forceActiveFocus() - } - - } - - } - - } - - } - - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Automation" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - // Random Wallpaper - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Random Wallpaper" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically select random wallpapers from the folder" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: randomWallpaperSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; - } - } - - } - - } - - } - - // Use Wallpaper Theme - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use Wallpaper Theme" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically adjust theme colors based on wallpaper" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: wallpaperThemeSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wallpaperThemeThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; - } - } - - } - - } - - } - - // Wallpaper Interval - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 Text { - text: "Wallpaper Interval" + text: "Wallpaper Folder" font.pixelSize: 13 font.bold: true color: Theme.textPrimary } Text { - text: "How often to change wallpapers automatically (in seconds)" + text: "Path to your wallpaper folder" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: folderInput.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + TextInput { + id: folderInput + + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + text: Settings.settings.wallpaperFolder !== undefined ? Settings.settings.wallpaperFolder : "" + font.family: Theme.fontFamily + font.pixelSize: 13 + color: Theme.textPrimary + verticalAlignment: TextInput.AlignVCenter + clip: true + selectByMouse: true + activeFocusOnTab: true + inputMethodHints: Qt.ImhUrlCharactersOnly + onTextChanged: { + Settings.settings.wallpaperFolder = text; + } + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.IBeamCursor + onClicked: folderInput.forceActiveFocus() + } + + } + + } + + } + + } + + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Automation" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Random Wallpaper + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Random Wallpaper" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Automatically select random wallpapers from the folder" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: randomWallpaperSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: randomWallpaperThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; + } + } + + } + + } + + } + + // Use Wallpaper Theme + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use Wallpaper Theme" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Automatically adjust theme colors based on wallpaper" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: wallpaperThemeSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: wallpaperThemeThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; + } + } + + } + + } + + } + + // Wallpaper Interval + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Wallpaper Interval" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "How often to change wallpapers automatically (in seconds)" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + RowLayout { + Layout.fillWidth: true + + Text { + text: Settings.settings.wallpaperInterval + " seconds" + font.pixelSize: 13 + color: Theme.textPrimary + } + + Item { + Layout.fillWidth: true + } + + } + + Slider { + id: intervalSlider + + Layout.fillWidth: true + from: 10 + to: 900 + stepSize: 10 + value: Settings.settings.wallpaperInterval + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.wallpaperInterval = Math.round(value); + } + + background: Rectangle { + x: intervalSlider.leftPadding + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: intervalSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant + + Rectangle { + width: intervalSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary + radius: 2 + } + + } + + handle: Rectangle { + x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) + y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 + } + + } + + } + + } + + Rectangle { + Layout.fillWidth: true + Layout.topMargin: 26 + Layout.bottomMargin: 18 + height: 1 + color: Theme.outline + opacity: 0.3 + } + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "SWWW" + font.pixelSize: 18 + font.bold: true + color: Theme.textPrimary + Layout.bottomMargin: 8 + } + + // Use SWWW + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + RowLayout { + spacing: 8 + Layout.fillWidth: true + + ColumnLayout { + spacing: 4 + Layout.fillWidth: true + + Text { + text: "Use SWWW" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Use SWWW daemon for advanced wallpaper management" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + } + + Rectangle { + id: swwwSwitch + + width: 52 + height: 32 + radius: 16 + color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant + border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline + border.width: 2 + + Rectangle { + id: swwwThumb + + width: 28 + height: 28 + radius: 14 + color: Theme.surface + border.color: Theme.outline + border.width: 1 + y: 2 + x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 + + Behavior on x { + NumberAnimation { + duration: 200 + easing.type: Easing.OutCubic + } + + } + + } + + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Settings.settings.useSWWW = !Settings.settings.useSWWW; + } + } + + } + + } + + } + + // SWWW Settings (only visible when useSWWW is enabled) + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + visible: Settings.settings.useSWWW + + // Resize Mode + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + + Text { + text: "Resize Mode" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "How SWWW should resize wallpapers to fit the screen" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + ComboBox { + id: resizeComboBox + + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + model: ["no", "crop", "fit", "stretch"] + currentIndex: model.indexOf(Settings.settings.wallpaperResize) + onActivated: { + Settings.settings.wallpaperResize = model[index]; + } + + background: Rectangle { + color: "transparent" + } + + contentItem: Text { + text: resizeComboBox.displayText + font: resizeComboBox.font + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + popup: Popup { + y: resizeComboBox.height + width: resizeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null + currentIndex: resizeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surface + border.color: Theme.outline + border.width: 1 + radius: 8 + } + + } + + delegate: ItemDelegate { + width: resizeComboBox.width + highlighted: resizeComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData + color: Theme.textPrimary + font: resizeComboBox.font + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + background: Rectangle { + color: parent.highlighted ? Theme.accentPrimary : "transparent" + } + + } + + } + + } + + } + + // Transition Type + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Transition Type" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Animation type when switching between wallpapers" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: 40 + radius: 16 + color: Theme.surfaceVariant + border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline + border.width: 1 + + ComboBox { + id: transitionTypeComboBox + + anchors.fill: parent + anchors.leftMargin: 12 + anchors.rightMargin: 12 + anchors.topMargin: 6 + anchors.bottomMargin: 6 + model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] + currentIndex: model.indexOf(Settings.settings.transitionType) + onActivated: { + Settings.settings.transitionType = model[index]; + } + + background: Rectangle { + color: "transparent" + } + + contentItem: Text { + text: transitionTypeComboBox.displayText + font: transitionTypeComboBox.font + color: Theme.textPrimary + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + popup: Popup { + y: transitionTypeComboBox.height + width: transitionTypeComboBox.width + implicitHeight: contentItem.implicitHeight + padding: 1 + + contentItem: ListView { + clip: true + implicitHeight: contentHeight + model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null + currentIndex: transitionTypeComboBox.highlightedIndex + + ScrollIndicator.vertical: ScrollIndicator { + } + + } + + background: Rectangle { + color: Theme.surface + border.color: Theme.outline + border.width: 1 + radius: 8 + } + + } + + delegate: ItemDelegate { + width: transitionTypeComboBox.width + highlighted: transitionTypeComboBox.highlightedIndex === index + + contentItem: Text { + text: modelData + color: Theme.textPrimary + font: transitionTypeComboBox.font + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignLeft + } + + background: Rectangle { + color: parent.highlighted ? Theme.accentPrimary : "transparent" + } + + } + + } + + } + + } + + // Transition FPS + ColumnLayout { + spacing: 8 + Layout.fillWidth: true + Layout.topMargin: 8 + + Text { + text: "Transition FPS" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Frames per second for transition animations" font.pixelSize: 12 color: Theme.textSecondary wrapMode: Text.WordWrap @@ -322,7 +719,7 @@ ScrollView { Layout.fillWidth: true Text { - text: Settings.settings.wallpaperInterval + " seconds" + text: Settings.settings.transitionFps + " FPS" font.pixelSize: 13 color: Theme.textPrimary } @@ -334,30 +731,30 @@ ScrollView { } Slider { - id: intervalSlider + id: fpsSlider Layout.fillWidth: true - from: 10 - to: 900 - stepSize: 10 - value: Settings.settings.wallpaperInterval + from: 30 + to: 500 + stepSize: 5 + value: Settings.settings.transitionFps snapMode: Slider.SnapAlways onMoved: { - Settings.settings.wallpaperInterval = Math.round(value); + Settings.settings.transitionFps = Math.round(value); } background: Rectangle { - x: intervalSlider.leftPadding - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + x: fpsSlider.leftPadding + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 implicitWidth: 200 implicitHeight: 4 - width: intervalSlider.availableWidth + width: fpsSlider.availableWidth height: implicitHeight radius: 2 color: Theme.surfaceVariant Rectangle { - width: intervalSlider.visualPosition * parent.width + width: fpsSlider.visualPosition * parent.width height: parent.height color: Theme.accentPrimary radius: 2 @@ -366,12 +763,12 @@ ScrollView { } handle: Rectangle { - x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width) - y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2 + x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) + y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 implicitWidth: 20 implicitHeight: 20 radius: 10 - color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface + color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface border.color: Theme.accentPrimary border.width: 2 } @@ -380,484 +777,83 @@ ScrollView { } - } - - Rectangle { - Layout.fillWidth: true - Layout.topMargin: 26 - Layout.bottomMargin: 18 - height: 1 - color: Theme.outline - opacity: 0.3 - } - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "SWWW" - font.pixelSize: 18 - font.bold: true - color: Theme.textPrimary - Layout.bottomMargin: 8 - } - - // Use SWWW + // Transition Duration ColumnLayout { spacing: 8 Layout.fillWidth: true Layout.topMargin: 8 + Text { + text: "Transition Duration" + font.pixelSize: 13 + font.bold: true + color: Theme.textPrimary + } + + Text { + text: "Duration of transition animations in seconds" + font.pixelSize: 12 + color: Theme.textSecondary + wrapMode: Text.WordWrap + Layout.fillWidth: true + } + RowLayout { - spacing: 8 Layout.fillWidth: true - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Use SWWW daemon for advanced wallpaper management" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - + Text { + text: Settings.settings.transitionDuration.toFixed(3) + " seconds" + font.pixelSize: 13 + color: Theme.textPrimary } - Rectangle { - id: swwwSwitch + Item { + Layout.fillWidth: true + } - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 + } + + Slider { + id: durationSlider + + Layout.fillWidth: true + from: 0.25 + to: 10 + stepSize: 0.05 + value: Settings.settings.transitionDuration + snapMode: Slider.SnapAlways + onMoved: { + Settings.settings.transitionDuration = value; + } + + background: Rectangle { + x: durationSlider.leftPadding + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 200 + implicitHeight: 4 + width: durationSlider.availableWidth + height: implicitHeight + radius: 2 + color: Theme.surfaceVariant Rectangle { - id: swwwThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - - } - - } - - } - - // SWWW Settings (only visible when useSWWW is enabled) - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - visible: Settings.settings.useSWWW - - // Resize Mode - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - - Text { - text: "Resize Mode" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "How SWWW should resize wallpapers to fit the screen" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: resizeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - ComboBox { - id: resizeComboBox - - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - model: ["no", "crop", "fit", "stretch"] - currentIndex: model.indexOf(Settings.settings.wallpaperResize) - onActivated: { - Settings.settings.wallpaperResize = model[index]; - } - - background: Rectangle { - color: "transparent" - } - - contentItem: Text { - text: resizeComboBox.displayText - font: resizeComboBox.font - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - popup: Popup { - y: resizeComboBox.height - width: resizeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: resizeComboBox.popup.visible ? resizeComboBox.delegateModel : null - currentIndex: resizeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surface - border.color: Theme.outline - border.width: 1 - radius: 8 - } - - } - - delegate: ItemDelegate { - width: resizeComboBox.width - highlighted: resizeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - color: Theme.textPrimary - font: resizeComboBox.font - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - background: Rectangle { - color: parent.highlighted ? Theme.accentPrimary : "transparent" - } - - } - - } - - } - - } - - // Transition Type - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Transition Type" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Animation type when switching between wallpapers" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - Rectangle { - Layout.fillWidth: true - Layout.preferredHeight: 40 - radius: 16 - color: Theme.surfaceVariant - border.color: transitionTypeComboBox.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 - - ComboBox { - id: transitionTypeComboBox - - anchors.fill: parent - anchors.leftMargin: 12 - anchors.rightMargin: 12 - anchors.topMargin: 6 - anchors.bottomMargin: 6 - model: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] - currentIndex: model.indexOf(Settings.settings.transitionType) - onActivated: { - Settings.settings.transitionType = model[index]; - } - - background: Rectangle { - color: "transparent" - } - - contentItem: Text { - text: transitionTypeComboBox.displayText - font: transitionTypeComboBox.font - color: Theme.textPrimary - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - popup: Popup { - y: transitionTypeComboBox.height - width: transitionTypeComboBox.width - implicitHeight: contentItem.implicitHeight - padding: 1 - - contentItem: ListView { - clip: true - implicitHeight: contentHeight - model: transitionTypeComboBox.popup.visible ? transitionTypeComboBox.delegateModel : null - currentIndex: transitionTypeComboBox.highlightedIndex - - ScrollIndicator.vertical: ScrollIndicator { - } - - } - - background: Rectangle { - color: Theme.surface - border.color: Theme.outline - border.width: 1 - radius: 8 - } - - } - - delegate: ItemDelegate { - width: transitionTypeComboBox.width - highlighted: transitionTypeComboBox.highlightedIndex === index - - contentItem: Text { - text: modelData - color: Theme.textPrimary - font: transitionTypeComboBox.font - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignLeft - } - - background: Rectangle { - color: parent.highlighted ? Theme.accentPrimary : "transparent" - } - - } - - } - - } - - } - - // Transition FPS - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Transition FPS" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Frames per second for transition animations" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - Text { - text: Settings.settings.transitionFps + " FPS" - font.pixelSize: 13 - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - } - - Slider { - id: fpsSlider - - Layout.fillWidth: true - from: 30 - to: 500 - stepSize: 5 - value: Settings.settings.transitionFps - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionFps = Math.round(value); - } - - background: Rectangle { - x: fpsSlider.leftPadding - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: fpsSlider.availableWidth - height: implicitHeight + width: durationSlider.visualPosition * parent.width + height: parent.height + color: Theme.accentPrimary radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: fpsSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: fpsSlider.leftPadding + fpsSlider.visualPosition * (fpsSlider.availableWidth - width) - y: fpsSlider.topPadding + fpsSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: fpsSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 } } - } - - // Transition Duration - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - Text { - text: "Transition Duration" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Duration of transition animations in seconds" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - RowLayout { - Layout.fillWidth: true - - Text { - text: Settings.settings.transitionDuration.toFixed(3) + " seconds" - font.pixelSize: 13 - color: Theme.textPrimary - } - - Item { - Layout.fillWidth: true - } - - } - - Slider { - id: durationSlider - - Layout.fillWidth: true - from: 0.25 - to: 10 - stepSize: 0.05 - value: Settings.settings.transitionDuration - snapMode: Slider.SnapAlways - onMoved: { - Settings.settings.transitionDuration = value; - } - - background: Rectangle { - x: durationSlider.leftPadding - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 200 - implicitHeight: 4 - width: durationSlider.availableWidth - height: implicitHeight - radius: 2 - color: Theme.surfaceVariant - - Rectangle { - width: durationSlider.visualPosition * parent.width - height: parent.height - color: Theme.accentPrimary - radius: 2 - } - - } - - handle: Rectangle { - x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) - y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 - implicitWidth: 20 - implicitHeight: 20 - radius: 10 - color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface - border.color: Theme.accentPrimary - border.width: 2 - } - + handle: Rectangle { + x: durationSlider.leftPadding + durationSlider.visualPosition * (durationSlider.availableWidth - width) + y: durationSlider.topPadding + durationSlider.availableHeight / 2 - height / 2 + implicitWidth: 20 + implicitHeight: 20 + radius: 10 + color: durationSlider.pressed ? Theme.surfaceVariant : Theme.surface + border.color: Theme.accentPrimary + border.width: 2 } } @@ -872,9 +868,4 @@ ScrollView { } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } - } From 8a7c3b754c27a3ac0ba5e5d516fee410739fa075 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 19:38:10 +0200 Subject: [PATCH 36/43] Remove system tray animation, small notification popup fix --- Bar/Modules/SystemTray.qml | 29 +++------------------- Widgets/Notification/NotificationPopup.qml | 13 +++++++--- 2 files changed, 13 insertions(+), 29 deletions(-) diff --git a/Bar/Modules/SystemTray.qml b/Bar/Modules/SystemTray.qml index 50afb04..177871d 100644 --- a/Bar/Modules/SystemTray.qml +++ b/Bar/Modules/SystemTray.qml @@ -23,27 +23,11 @@ Row { delegate: Item { width: 24 * Theme.uiScale height: 24 * Theme.uiScale - // Hide Spotify icon, or adjust to your liking - visible: modelData && modelData.id !== "spotify" + + visible: modelData property bool isHovered: trayMouseArea.containsMouse - // Hover scale animation - scale: isHovered ? 1.15 : 1.0 - Behavior on scale { - NumberAnimation { - duration: 150 - easing.type: Easing.OutCubic - } - } - - // Subtle rotation on hover - rotation: isHovered ? 5 : 0 - Behavior on rotation { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - } + // No animations - static display Rectangle { anchors.centerIn: parent @@ -74,13 +58,6 @@ Row { return icon; } opacity: status === Image.Ready ? 1 : 0 - - Behavior on opacity { - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic - } - } Component.onCompleted: {} } } diff --git a/Widgets/Notification/NotificationPopup.qml b/Widgets/Notification/NotificationPopup.qml index e62bd24..a89b948 100644 --- a/Widgets/Notification/NotificationPopup.qml +++ b/Widgets/Notification/NotificationPopup.qml @@ -165,7 +165,7 @@ Item { x: appeared ? 0 : width opacity: dismissed ? 0 : 1 - height: dismissed ? 0 : contentRow.height + 20 + height: dismissed ? 0 : Math.max(contentRow.height, 60) + 20 Row { id: contentRow @@ -328,7 +328,7 @@ Item { NumberAnimation { target: notificationDelegate property: "height" - to: contentRow.height + 20 + to: Math.max(contentRow.height, 60) + 20 duration: 150 } NumberAnimation { @@ -345,7 +345,14 @@ Item { opacity = 0; height = 0; x = width; - appearAnimation.start(); + // Small delay to ensure contentRow has proper height + Timer { + interval: 10 + repeat: false + onTriggered: { + appearAnimation.start(); + } + } for (let i = 0; i < notificationModel.count; i++) { if (notificationModel.get(i).id === notificationDelegate.id) { var oldItem = notificationModel.get(i); From 3148dc62a0f39e7d37e18e8d3de42294796ed03a Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 19:51:24 +0200 Subject: [PATCH 37/43] Fix NotificationPopup.qml --- Widgets/Notification/NotificationPopup.qml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/Widgets/Notification/NotificationPopup.qml b/Widgets/Notification/NotificationPopup.qml index a89b948..761fd4f 100644 --- a/Widgets/Notification/NotificationPopup.qml +++ b/Widgets/Notification/NotificationPopup.qml @@ -340,19 +340,22 @@ Item { } } + Timer { + id: appearTimer + interval: 10 + repeat: false + onTriggered: { + appearAnimation.start(); + } + } + Component.onCompleted: { if (!appeared) { opacity = 0; height = 0; x = width; // Small delay to ensure contentRow has proper height - Timer { - interval: 10 - repeat: false - onTriggered: { - appearAnimation.start(); - } - } + appearTimer.start(); for (let i = 0; i < notificationModel.count; i++) { if (notificationModel.get(i).id === notificationDelegate.id) { var oldItem = notificationModel.get(i); From 763fc8f29a4a65b3b74fdc194b14f5cf608fd627 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 19:56:25 +0200 Subject: [PATCH 38/43] Use ToggleOption for Wallpaper.qml --- Widgets/SettingsWindow/Tabs/Wallpaper.qml | 237 ++-------------------- 1 file changed, 18 insertions(+), 219 deletions(-) diff --git a/Widgets/SettingsWindow/Tabs/Wallpaper.qml b/Widgets/SettingsWindow/Tabs/Wallpaper.qml index 228ff38..583f1d1 100644 --- a/Widgets/SettingsWindow/Tabs/Wallpaper.qml +++ b/Widgets/SettingsWindow/Tabs/Wallpaper.qml @@ -142,157 +142,23 @@ ColumnLayout { } // Random Wallpaper - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Random Wallpaper" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically select random wallpapers from the folder" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: randomWallpaperSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: randomWallpaperThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; - } - } - - } - + ToggleOption { + label: "Random Wallpaper" + description: "Automatically select random wallpapers from the folder" + value: Settings.settings.randomWallpaper + onToggled: function() { + Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper; } - } // Use Wallpaper Theme - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use Wallpaper Theme" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Automatically adjust theme colors based on wallpaper" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: wallpaperThemeSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: wallpaperThemeThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; - } - } - - } - + ToggleOption { + label: "Use Wallpaper Theme" + description: "Automatically adjust theme colors based on wallpaper" + value: Settings.settings.useWallpaperTheme + onToggled: function() { + Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme; } - } // Wallpaper Interval @@ -402,80 +268,13 @@ ColumnLayout { } // Use SWWW - ColumnLayout { - spacing: 8 - Layout.fillWidth: true - Layout.topMargin: 8 - - RowLayout { - spacing: 8 - Layout.fillWidth: true - - ColumnLayout { - spacing: 4 - Layout.fillWidth: true - - Text { - text: "Use SWWW" - font.pixelSize: 13 - font.bold: true - color: Theme.textPrimary - } - - Text { - text: "Use SWWW daemon for advanced wallpaper management" - font.pixelSize: 12 - color: Theme.textSecondary - wrapMode: Text.WordWrap - Layout.fillWidth: true - } - - } - - Rectangle { - id: swwwSwitch - - width: 52 - height: 32 - radius: 16 - color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.surfaceVariant - border.color: Settings.settings.useSWWW ? Theme.accentPrimary : Theme.outline - border.width: 2 - - Rectangle { - id: swwwThumb - - width: 28 - height: 28 - radius: 14 - color: Theme.surface - border.color: Theme.outline - border.width: 1 - y: 2 - x: Settings.settings.useSWWW ? swwwSwitch.width - width - 2 : 2 - - Behavior on x { - NumberAnimation { - duration: 200 - easing.type: Easing.OutCubic - } - - } - - } - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Settings.settings.useSWWW = !Settings.settings.useSWWW; - } - } - - } - + ToggleOption { + label: "Use SWWW" + description: "Use SWWW daemon for advanced wallpaper management" + value: Settings.settings.useSWWW + onToggled: function() { + Settings.settings.useSWWW = !Settings.settings.useSWWW; } - } // SWWW Settings (only visible when useSWWW is enabled) From cb74b6e5d53ce163e8d553b6339f7d1f768b95bf Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 14:09:18 -0400 Subject: [PATCH 39/43] Scaling: Replaced all Theme.uiScale by Theme.scale(Screen) so stuff scale accordingly to the Screen used by the Item/component --- Bar/Bar.qml | 10 +- Bar/Modules/ActiveWindow.qml | 4 +- Bar/Modules/Applauncher.qml | 14 +- Bar/Modules/AudioDeviceSelector.qml | 20 +-- Bar/Modules/Bluetooth.qml | 14 +- Bar/Modules/Calendar.qml | 6 +- Bar/Modules/ClockWidget.qml | 2 +- Bar/Modules/CustomTrayMenu.qml | 8 +- Bar/Modules/Media.qml | 22 ++-- Bar/Modules/SettingsButton.qml | 2 +- Bar/Modules/SystemInfo.qml | 12 +- Bar/Modules/SystemTray.qml | 16 +-- Bar/Modules/Taskbar.qml | 2 +- Bar/Modules/Wifi.qml | 24 ++-- Bar/Modules/Workspace.qml | 18 +-- Components/Avatar.qml | 2 +- Components/CircularProgressBar.qml | 4 +- Components/CircularSpectrum.qml | 8 +- Components/Corners.qml | 6 +- Components/IconButton.qml | 4 +- Components/PillIndicator.qml | 8 +- Components/Spinner.qml | 2 +- Components/StyledTooltip.qml | 14 +- Components/Tabs.qml | 14 +- Components/ToggleOption.qml | 24 ++-- Settings/Theme.qml | 10 +- Widgets/Dock.qml | 4 +- Widgets/LockScreen/BatteryCharge.qml | 4 +- Widgets/LockScreen/LockScreen.qml | 90 ++++++------- Widgets/Notification/NotificationIcon.qml | 2 +- Widgets/SettingsWindow/SettingsWindow.qml | 86 ++++++------ Widgets/SettingsWindow/Tabs/About.qml | 30 ++--- Widgets/SettingsWindow/Tabs/Bar.qml | 4 +- .../Tabs/Components/UnitSelector.qml | 18 +-- .../Tabs/Components/WallpaperSelector.qml | 16 +-- Widgets/SettingsWindow/Tabs/Display.qml | 28 ++-- Widgets/SettingsWindow/Tabs/General.qml | 48 +++---- Widgets/SettingsWindow/Tabs/Misc.qml | 8 +- Widgets/SettingsWindow/Tabs/Network.qml | 8 +- .../SettingsWindow/Tabs/ScreenRecorder.qml | 68 +++++----- Widgets/SettingsWindow/Tabs/TimeWeather.qml | 20 +-- Widgets/SidePanel/Button.qml | 2 +- Widgets/SidePanel/Music.qml | 122 ++++++++--------- Widgets/SidePanel/PanelPopup.qml | 50 +++---- Widgets/SidePanel/PowerMenu.qml | 124 +++++++++--------- Widgets/SidePanel/PowerProfile.qml | 32 ++--- Widgets/SidePanel/SettingsIcon.qml | 4 +- Widgets/SidePanel/SettingsModal.qml | 4 +- Widgets/SidePanel/SystemMonitor.qml | 42 +++--- Widgets/SidePanel/Weather.qml | 48 +++---- 50 files changed, 564 insertions(+), 568 deletions(-) diff --git a/Bar/Bar.qml b/Bar/Bar.qml index b542def..48c18cb 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -48,7 +48,7 @@ Scope { id: barBackground width: parent.width - height: 36 * Theme.uiScale + height: 36 * Theme.scale(Screen) color: Theme.backgroundPrimary anchors.top: parent.top anchors.left: parent.left @@ -59,8 +59,8 @@ Scope { anchors.verticalCenter: barBackground.verticalCenter anchors.left: barBackground.left - anchors.leftMargin: 18 * Theme.uiScale - spacing: 12 * Theme.uiScale + anchors.leftMargin: 18 * Theme.scale(Screen) + spacing: 12 * Theme.scale(Screen) SystemInfo { anchors.verticalCenter: parent.verticalCenter @@ -93,8 +93,8 @@ Scope { anchors.verticalCenter: barBackground.verticalCenter anchors.right: barBackground.right - anchors.rightMargin: 18 * Theme.uiScale - spacing: 12 * Theme.uiScale + anchors.rightMargin: 18 * Theme.scale(Screen) + spacing: 12 * Theme.scale(Screen) SystemTray { id: systemTrayModule diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index 206de29..fca39d5 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -17,7 +17,7 @@ PanelWindow { visible: Settings.settings.showActiveWindow && !activeWindowWrapper.finallyHidden implicitHeight: activeWindowTitleContainer.height implicitWidth: 0 - property int barHeight: 36 * Theme.uiScale + property int barHeight: 36 * Theme.scale(Screen) color: "transparent" function getIcon() { @@ -127,7 +127,7 @@ PanelWindow { Text { id: activeWindowTitle text: ToplevelManager?.activeToplevel?.title && ToplevelManager?.activeToplevel?.title.length > 60 ? ToplevelManager?.activeToplevel?.title.substring(0, 60) + "..." : ToplevelManager?.activeToplevel?.title || "" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary anchors.left: icon.right anchors.leftMargin: Settings.settings.showActiveWindowIcon ? 4 : 6 diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index d62e300..174364b 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -532,7 +532,7 @@ PanelWithOverlay { Text { text: "search" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader * Theme.uiScale + font.pixelSize: Theme.fontSizeHeader * Theme.scale(Screen) color: searchField.activeFocus ? Theme.accentPrimary : Theme.textSecondary verticalAlignment: Text.AlignVCenter Layout.alignment: Qt.AlignVCenter @@ -545,7 +545,7 @@ PanelWithOverlay { placeholderTextColor: Theme.textSecondary background: null font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeBody * Theme.uiScale + font.pixelSize: Theme.fontSizeBody * Theme.scale(Screen) Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter onTextChanged: root.updateFilter() @@ -689,7 +689,7 @@ PanelWithOverlay { visible: !modelData.isCalculator && !modelData.isClipboard && !modelData.isCommand && !parent.iconLoaded && modelData.type !== 'image' text: "broken_image" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader * Theme.uiScale + font.pixelSize: Theme.fontSizeHeader * Theme.scale(Screen) color: Theme.accentPrimary } } @@ -702,7 +702,7 @@ PanelWithOverlay { text: modelData.name color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textPrimary) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) font.bold: hovered || isSelected verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -716,7 +716,7 @@ PanelWithOverlay { (modelData.comment || modelData.genericName || "No description available") color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textSecondary : Theme.textSecondary) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption * Theme.uiScale + font.pixelSize: Theme.fontSizeCaption * Theme.scale(Screen) font.italic: !(modelData.comment || modelData.genericName) opacity: modelData.isClipboard ? 0.8 : modelData.isCommand ? 0.9 : ((modelData.comment || modelData.genericName) ? 1.0 : 0.6) elide: Text.ElideRight @@ -734,7 +734,7 @@ PanelWithOverlay { Text { text: modelData.isCalculator ? "content_copy" : "chevron_right" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody * Theme.uiScale + font.pixelSize: Theme.fontSizeBody * Theme.scale(Screen) color: (hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textSecondary) @@ -818,7 +818,7 @@ PanelWithOverlay { anchors.centerIn: parent text: "star" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) color: (parent.MouseArea.containsMouse || hovered || isSelected) ? Theme.onAccent : (appLauncherPanel.isPinned(modelData) ? Theme.textPrimary : Theme.textDisabled) diff --git a/Bar/Modules/AudioDeviceSelector.qml b/Bar/Modules/AudioDeviceSelector.qml index 590a82c..3d45d92 100644 --- a/Bar/Modules/AudioDeviceSelector.qml +++ b/Bar/Modules/AudioDeviceSelector.qml @@ -155,7 +155,7 @@ PanelWithOverlay { Text { text: "volume_up" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: (Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary Layout.alignment: Qt.AlignVCenter } @@ -168,7 +168,7 @@ PanelWithOverlay { Text { text: modelData.nickname || modelData.description || modelData.name font.bold: true - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: (Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary elide: Text.ElideRight maximumLineCount: 1 @@ -177,7 +177,7 @@ PanelWithOverlay { Text { text: modelData.description !== modelData.nickname ? modelData.description : "" - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) color: Theme.textSecondary elide: Text.ElideRight maximumLineCount: 1 @@ -200,7 +200,7 @@ PanelWithOverlay { anchors.centerIn: parent text: "Set" color: Theme.onAccent - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) font.bold: true } @@ -216,7 +216,7 @@ PanelWithOverlay { text: "(Current)" visible: Pipewire.defaultAudioSink && Pipewire.defaultAudioSink.id === modelData.id color: Theme.accentPrimary - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) Layout.alignment: Qt.AlignVCenter } @@ -267,7 +267,7 @@ PanelWithOverlay { Text { text: "mic" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: (Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary Layout.alignment: Qt.AlignVCenter } @@ -280,7 +280,7 @@ PanelWithOverlay { Text { text: modelData.nickname || modelData.description || modelData.name font.bold: true - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: (Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id) ? Theme.accentPrimary : Theme.textPrimary elide: Text.ElideRight maximumLineCount: 1 @@ -289,7 +289,7 @@ PanelWithOverlay { Text { text: modelData.description !== modelData.nickname ? modelData.description : "" - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) color: Theme.textSecondary elide: Text.ElideRight maximumLineCount: 1 @@ -312,7 +312,7 @@ PanelWithOverlay { anchors.centerIn: parent text: "Set" color: Theme.onAccent - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) font.bold: true } @@ -328,7 +328,7 @@ PanelWithOverlay { text: "(Current)" visible: Pipewire.defaultAudioSource && Pipewire.defaultAudioSource.id === modelData.id color: Theme.accentPrimary - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) Layout.alignment: Qt.AlignVCenter } diff --git a/Bar/Modules/Bluetooth.qml b/Bar/Modules/Bluetooth.qml index fda61ec..0c080dc 100644 --- a/Bar/Modules/Bluetooth.qml +++ b/Bar/Modules/Bluetooth.qml @@ -45,7 +45,7 @@ Item { } } font.family: mouseAreaBluetooth.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: mouseAreaBluetooth.containsMouse ? Theme.accentPrimary : Theme.textPrimary } @@ -125,13 +125,13 @@ Item { Text { text: "bluetooth" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.accentPrimary } Text { text: "Bluetooth Devices" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary Layout.fillWidth: true @@ -180,7 +180,7 @@ Item { Text { text: modelData.connected ? "bluetooth" : "bluetooth_disabled" font.family: "Material Symbols Outlined" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) } @@ -199,7 +199,7 @@ Item { return deviceName; } color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) elide: Text.ElideRight Layout.fillWidth: true } @@ -217,7 +217,7 @@ Item { } } color: deviceMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 * Theme.uiScale + font.pixelSize: 11 * Theme.scale(Screen) elide: Text.ElideRight Layout.fillWidth: true } @@ -265,7 +265,7 @@ Item { Text { text: "Scanning for devices..." - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary } diff --git a/Bar/Modules/Calendar.qml b/Bar/Modules/Calendar.qml index baf7f9f..47757fa 100644 --- a/Bar/Modules/Calendar.qml +++ b/Bar/Modules/Calendar.qml @@ -52,7 +52,7 @@ PanelWithOverlay { text: calendar.title color: Theme.textPrimary opacity: 0.7 - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.family: Theme.fontFamily font.bold: true } @@ -78,7 +78,7 @@ PanelWithOverlay { text: shortName color: Theme.textPrimary opacity: 0.8 - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.family: Theme.fontFamily font.bold: true horizontalAlignment: Text.AlignHCenter @@ -161,7 +161,7 @@ PanelWithOverlay { text: model.day color: model.today ? Theme.onAccent : Theme.textPrimary opacity: model.month === calendar.month ? (mouseArea2.containsMouse ? 1 : 0.7) : 0.3 - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.family: Theme.fontFamily font.bold: model.today ? true : false } diff --git a/Bar/Modules/ClockWidget.qml b/Bar/Modules/ClockWidget.qml index a539f45..bff0d35 100644 --- a/Bar/Modules/ClockWidget.qml +++ b/Bar/Modules/ClockWidget.qml @@ -15,7 +15,7 @@ Rectangle { text: Time.time font.family: Theme.fontFamily font.weight: Font.Bold - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) color: Theme.textPrimary anchors.centerIn: parent } diff --git a/Bar/Modules/CustomTrayMenu.qml b/Bar/Modules/CustomTrayMenu.qml index 9a994e3..e575648 100644 --- a/Bar/Modules/CustomTrayMenu.qml +++ b/Bar/Modules/CustomTrayMenu.qml @@ -128,7 +128,7 @@ PopupWindow { color: (modelData?.enabled ?? true) ? bg.hoverTextColor : Theme.textDisabled; text: modelData?.text ?? ""; font.family: Theme.fontFamily; - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale; + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen); verticalAlignment: Text.AlignVCenter; elide: Text.ElideRight; } @@ -145,7 +145,7 @@ PopupWindow { // Material Symbols Outlined chevron right for submenu text: modelData?.hasChildren ? "menu" : ""; font.family: "Material Symbols Outlined"; - font.pixelSize: 18 * Theme.uiScale; + font.pixelSize: 18 * Theme.scale(Screen); verticalAlignment: Text.AlignVCenter; visible: modelData?.hasChildren ?? false; color: Theme.textPrimary; @@ -362,7 +362,7 @@ PopupWindow { color: (modelData?.enabled ?? true) ? bg.hoverTextColor : Theme.textDisabled; text: modelData?.text ?? ""; font.family: Theme.fontFamily; - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale; + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen); verticalAlignment: Text.AlignVCenter; elide: Text.ElideRight; } @@ -378,7 +378,7 @@ PopupWindow { Text { text: modelData?.hasChildren ? "\uE5CC" : ""; font.family: "Material Symbols Outlined"; - font.pixelSize: 18 * Theme.uiScale; + font.pixelSize: 18 * Theme.scale(Screen); verticalAlignment: Text.AlignVCenter; visible: modelData?.hasChildren ?? false; color: Theme.textPrimary; diff --git a/Bar/Modules/Media.qml b/Bar/Modules/Media.qml index 1aa46ab..d9da9f3 100644 --- a/Bar/Modules/Media.qml +++ b/Bar/Modules/Media.qml @@ -10,7 +10,7 @@ import qs.Components Item { id: mediaControl width: visible ? mediaRow.width : 0 - height: 36 * Theme.uiScale + height: 36 * Theme.scale(Screen) visible: Settings.settings.showMediaInBar && MusicManager.currentPlayer RowLayout { @@ -20,8 +20,8 @@ Item { Item { id: albumArtContainer - width: 24 * Theme.uiScale - height: 24 * Theme.uiScale + width: 24 * Theme.scale(Screen) + height: 24 * Theme.scale(Screen) Layout.alignment: Qt.AlignVCenter // Circular spectrum visualizer @@ -29,8 +29,8 @@ Item { id: spectrum values: MusicManager.cavaValues anchors.centerIn: parent - innerRadius: 10 * Theme.uiScale - outerRadius: 18 * Theme.uiScale + innerRadius: 10 * Theme.scale(Screen) + outerRadius: 18 * Theme.scale(Screen) fillColor: Theme.accentPrimary strokeColor: Theme.accentPrimary strokeWidth: 0 @@ -40,10 +40,10 @@ Item { // Album art image Rectangle { id: albumArtwork - width: 20 * Theme.uiScale - height: 20 * Theme.uiScale + width: 20 * Theme.scale(Screen) + height: 20 * Theme.scale(Screen) anchors.centerIn: parent - radius: 12 * Theme.uiScale // circle + radius: 12 * Theme.scale(Screen) // circle color: Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) border.width: 1 @@ -79,7 +79,7 @@ Item { anchors.centerIn: parent text: "music_note" font.family: "Material Symbols Outlined" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.4) visible: !albumArt.visible } @@ -96,7 +96,7 @@ Item { anchors.centerIn: parent text: MusicManager.isPlaying ? "pause" : "play_arrow" font.family: "Material Symbols Outlined" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: "white" } } @@ -117,7 +117,7 @@ Item { text: MusicManager.trackTitle + " - " + MusicManager.trackArtist color: Theme.textPrimary font.family: Theme.fontFamily - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) elide: Text.ElideRight Layout.maximumWidth: 300 Layout.alignment: Qt.AlignVCenter diff --git a/Bar/Modules/SettingsButton.qml b/Bar/Modules/SettingsButton.qml index 97f46c5..c832bd8 100644 --- a/Bar/Modules/SettingsButton.qml +++ b/Bar/Modules/SettingsButton.qml @@ -21,7 +21,7 @@ Item { anchors.centerIn: parent text: "settings" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: mouseArea.containsMouse ? Theme.accentPrimary : Theme.textPrimary } diff --git a/Bar/Modules/SystemInfo.qml b/Bar/Modules/SystemInfo.qml index 3361f94..bc87c29 100644 --- a/Bar/Modules/SystemInfo.qml +++ b/Bar/Modules/SystemInfo.qml @@ -17,7 +17,7 @@ Row { Text { id: cpuUsageIcon font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody * Theme.uiScale + font.pixelSize: Theme.fontSizeBody * Theme.scale(Screen) text: "speed" verticalAlignment: Text.AlignVCenter anchors.verticalCenter: parent.verticalCenter @@ -27,7 +27,7 @@ Row { Text { id: cpuUsageText font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) color: Theme.textPrimary text: Sysinfo.cpuUsageStr anchors.verticalCenter: parent.verticalCenter @@ -41,7 +41,7 @@ Row { spacing: 3 Text { font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody * Theme.uiScale + font.pixelSize: Theme.fontSizeBody * Theme.scale(Screen) text: "thermometer" verticalAlignment: Text.AlignVCenter anchors.verticalCenter: parent.verticalCenter @@ -50,7 +50,7 @@ Row { Text { font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) color: Theme.textPrimary text: Sysinfo.cpuTempStr anchors.verticalCenter: parent.verticalCenter @@ -64,7 +64,7 @@ Row { spacing: 3 Text { font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody * Theme.uiScale + font.pixelSize: Theme.fontSizeBody * Theme.scale(Screen) text: "memory" color: Theme.accentPrimary verticalAlignment: Text.AlignVCenter @@ -73,7 +73,7 @@ Row { Text { font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) color: Theme.textPrimary text: Sysinfo.memoryUsageStr anchors.verticalCenter: parent.verticalCenter diff --git a/Bar/Modules/SystemTray.qml b/Bar/Modules/SystemTray.qml index 177871d..da6e04c 100644 --- a/Bar/Modules/SystemTray.qml +++ b/Bar/Modules/SystemTray.qml @@ -21,8 +21,8 @@ Row { Repeater { model: systemTray.items delegate: Item { - width: 24 * Theme.uiScale - height: 24 * Theme.uiScale + width: 24 * Theme.scale(Screen) + height: 24 * Theme.scale(Screen) visible: modelData property bool isHovered: trayMouseArea.containsMouse @@ -31,17 +31,17 @@ Row { Rectangle { anchors.centerIn: parent - width: 16 * Theme.uiScale - height: 16 * Theme.uiScale - radius: 6 * Theme.uiScale + width: 16 * Theme.scale(Screen) + height: 16 * Theme.scale(Screen) + radius: 6 * Theme.scale(Screen) color: "transparent" clip: true IconImage { id: trayIcon anchors.centerIn: parent - width: 16 * Theme.uiScale - height: 16 * Theme.uiScale + width: 16 * Theme.scale(Screen) + height: 16 * Theme.scale(Screen) smooth: false asynchronous: true backer.fillMode: Image.PreserveAspectFit @@ -99,7 +99,7 @@ Row { if (modelData.hasMenu && modelData.menu && trayMenu) { // Anchor the menu to the tray icon item (parent) and position it below the icon const menuX = (width / 2) - (trayMenu.width / 2); - const menuY = height + 20 * Theme.uiScale; + const menuY = height + 20 * Theme.scale(Screen); trayMenu.menu = modelData.menu; trayMenu.showAt(parent, menuX, menuY); } else diff --git a/Bar/Modules/Taskbar.qml b/Bar/Modules/Taskbar.qml index 1440d51..249f4c0 100644 --- a/Bar/Modules/Taskbar.qml +++ b/Bar/Modules/Taskbar.qml @@ -83,7 +83,7 @@ Item { visible: !appIcon.visible text: appButton.appId ? appButton.appId.charAt(0).toUpperCase() : "?" font.family: Theme.fontFamily - font.pixelSize: Math.max(10, Settings.settings.taskbarIconSize * 0.4375 * Theme.uiScale) + font.pixelSize: Math.max(10, Settings.settings.taskbarIconSize * 0.4375 * Theme.scale(Screen)) font.bold: true color: appButton.isActive ? Theme.onAccent : Theme.textPrimary } diff --git a/Bar/Modules/Wifi.qml b/Bar/Modules/Wifi.qml index cae7100..b2c783e 100644 --- a/Bar/Modules/Wifi.qml +++ b/Bar/Modules/Wifi.qml @@ -51,7 +51,7 @@ Item { return connected ? network.signalIcon(parent.currentSignal) : "wifi_off" } font.family: mouseAreaWifi.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: mouseAreaWifi.containsMouse ? Theme.accentPrimary : Theme.textPrimary } @@ -120,13 +120,13 @@ Item { Text { text: "wifi" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.accentPrimary } Text { text: "WiFi Networks" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary Layout.fillWidth: true @@ -183,7 +183,7 @@ Item { Text { text: network.signalIcon(modelData.signal) font.family: "Material Symbols Outlined" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) } @@ -194,7 +194,7 @@ Item { Text { text: modelData.ssid || "Unknown Network" color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textPrimary) - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) elide: Text.ElideRight Layout.fillWidth: true } @@ -202,7 +202,7 @@ Item { Text { text: modelData.security && modelData.security !== "--" ? modelData.security : "Open" color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : (modelData.connected ? Theme.accentPrimary : Theme.textSecondary) - font.pixelSize: 11 * Theme.uiScale + font.pixelSize: 11 * Theme.scale(Screen) elide: Text.ElideRight Layout.fillWidth: true } @@ -211,7 +211,7 @@ Item { visible: network.connectStatusSsid === modelData.ssid && network.connectStatus === "error" && network.connectError.length > 0 text: network.connectError color: Theme.error - font.pixelSize: 11 * Theme.uiScale + font.pixelSize: 11 * Theme.scale(Screen) elide: Text.ElideRight Layout.fillWidth: true } @@ -234,7 +234,7 @@ Item { visible: network.connectStatus === "success" && !network.connectingSsid text: "check_circle" font.family: "Material Symbols Outlined" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: "#43a047" anchors.centerIn: parent } @@ -243,7 +243,7 @@ Item { visible: network.connectStatus === "error" && !network.connectingSsid text: "error" font.family: "Material Symbols Outlined" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: Theme.error anchors.centerIn: parent } @@ -253,7 +253,7 @@ Item { visible: modelData.connected text: "connected" color: networkMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary - font.pixelSize: 11 * Theme.uiScale + font.pixelSize: 11 * Theme.scale(Screen) } } @@ -309,7 +309,7 @@ Item { anchors.fill: parent anchors.margins: 12 text: passwordInput - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -364,7 +364,7 @@ Item { anchors.centerIn: parent text: "Connect" color: Theme.backgroundPrimary - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) font.bold: true } } diff --git a/Bar/Modules/Workspace.qml b/Bar/Modules/Workspace.qml index b9825b6..6198985 100644 --- a/Bar/Modules/Workspace.qml +++ b/Bar/Modules/Workspace.qml @@ -40,7 +40,7 @@ Item { return total; } - height: 36 * Theme.uiScale + height: 36 * Theme.scale(Screen) Component.onCompleted: { localWorkspaces.clear(); @@ -115,14 +115,14 @@ Item { Rectangle { id: workspaceBackground - width: parent.width - 15 * Theme.uiScale - height: 26 * Theme.uiScale + width: parent.width - 15 * Theme.scale(Screen) + height: 26 * Theme.scale(Screen) anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - radius: 12 * Theme.uiScale + radius: 12 * Theme.scale(Screen) color: Theme.surfaceVariant border.color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.1) - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) layer.enabled: true layer.effect: MultiEffect { shadowColor: "black" @@ -145,7 +145,7 @@ Item { model: localWorkspaces Item { id: workspacePillContainer - height: 12 * Theme.uiScale + height: 12 * Theme.scale(Screen) width: { if (model.isFocused) return 44; @@ -245,12 +245,12 @@ Item { Rectangle { id: pillBurst anchors.centerIn: workspacePillContainer - width: workspacePillContainer.width + 18 * root.masterProgress * Theme.uiScale - height: workspacePillContainer.height + 18 * root.masterProgress * Theme.uiScale + width: workspacePillContainer.width + 18 * root.masterProgress * Theme.scale(Screen) + height: workspacePillContainer.height + 18 * root.masterProgress * Theme.scale(Screen) radius: width / 2 color: "transparent" border.color: root.effectColor - border.width: (2 + 6 * (1.0 - root.masterProgress)) * Theme.uiScale + border.width: (2 + 6 * (1.0 - root.masterProgress)) * Theme.scale(Screen) opacity: root.effectsActive && model.isFocused ? (1.0 - root.masterProgress) * 0.7 : 0 visible: root.effectsActive && model.isFocused z: 1 diff --git a/Components/Avatar.qml b/Components/Avatar.qml index b2788aa..b2c29a2 100644 --- a/Components/Avatar.qml +++ b/Components/Avatar.qml @@ -43,7 +43,7 @@ Item { anchors.centerIn: parent text: "person" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.onAccent visible: Settings.settings.profileImage === undefined || Settings.settings.profileImage === "" z: 0 diff --git a/Components/CircularProgressBar.qml b/Components/CircularProgressBar.qml index 61ef17f..a38bb1b 100644 --- a/Components/CircularProgressBar.qml +++ b/Components/CircularProgressBar.qml @@ -9,11 +9,11 @@ Rectangle { property int size: 80 property color backgroundColor: Theme.surfaceVariant property color progressColor: Theme.accentPrimary - property int strokeWidth: 6 * Theme.uiScale + property int strokeWidth: 6 * Theme.scale(Screen) property bool showText: true property string units: "%" property string text: Math.round(progress * 100) + units - property int textSize: 10 * Theme.uiScale + property int textSize: 10 * Theme.scale(Screen) property color textColor: Theme.textPrimary // Notch properties diff --git a/Components/CircularSpectrum.qml b/Components/CircularSpectrum.qml index 635f550..7931fd4 100644 --- a/Components/CircularSpectrum.qml +++ b/Components/CircularSpectrum.qml @@ -4,11 +4,11 @@ import qs.Settings Item { id: root - property int innerRadius: 34 * Theme.uiScale - property int outerRadius: 48 * Theme.uiScale + property int innerRadius: 34 * Theme.scale(Screen) + property int outerRadius: 48 * Theme.scale(Screen) property color fillColor: "#fff" property color strokeColor: "#fff" - property int strokeWidth: 0 * Theme.uiScale + property int strokeWidth: 0 * Theme.scale(Screen) property var values: [] property int usableOuter: 48 @@ -24,7 +24,7 @@ Item { Rectangle { property real value: root.values[index] property real angle: (index / root.values.length) * 360 - width: Math.max(2 * Theme.uiScale, (root.innerRadius * 2 * Math.PI) / root.values.length - 4 * Theme.uiScale) + width: Math.max(2 * Theme.scale(Screen), (root.innerRadius * 2 * Math.PI) / root.values.length - 4 * Theme.scale(Screen)) height: Settings.settings.visualizerType === "diamond" ? value * 2 * (usableOuter - root.innerRadius) : value * (usableOuter - root.innerRadius) radius: width / 2 color: root.fillColor diff --git a/Components/Corners.qml b/Components/Corners.qml index c8ac17e..716b30c 100644 --- a/Components/Corners.qml +++ b/Components/Corners.qml @@ -6,11 +6,11 @@ Shape { id: root property string position: "topleft" // Corner position: topleft/topright/bottomleft/bottomright - property real size: 1.0 * Theme.uiScale // Scale multiplier for entire corner + property real size: 1.0 * Theme.scale(Screen) // Scale multiplier for entire corner property int concaveWidth: 100 * size property int concaveHeight: 60 * size - property int offsetX: -20 * Theme.uiScale - property int offsetY: -20 * Theme.uiScale + property int offsetX: -20 * Theme.scale(Screen) + property int offsetY: -20 * Theme.scale(Screen) property color fillColor: Theme.accentPrimary property int arcRadius: 20 * size diff --git a/Components/IconButton.qml b/Components/IconButton.qml index 6dc4b1a..93d623d 100644 --- a/Components/IconButton.qml +++ b/Components/IconButton.qml @@ -19,7 +19,7 @@ MouseArea { Rectangle { anchors.fill: parent - radius: 8 * Theme.uiScale + radius: 8 * Theme.scale(Screen) color: root.hovering ? Theme.accentPrimary : "transparent" } Text { @@ -27,7 +27,7 @@ MouseArea { anchors.centerIn: parent text: root.icon font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: root.hovering ? Theme.onAccent : Theme.textPrimary horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter diff --git a/Components/PillIndicator.qml b/Components/PillIndicator.qml index addf5ec..2e6d307 100644 --- a/Components/PillIndicator.qml +++ b/Components/PillIndicator.qml @@ -13,8 +13,8 @@ Item { property color iconCircleColor: Theme.accentPrimary property color iconTextColor: Theme.backgroundPrimary property color collapsedIconColor: Theme.textPrimary - property int pillHeight: 22 * Theme.uiScale - property int iconSize: 22 * Theme.uiScale + property int pillHeight: 22 * Theme.scale(Screen) + property int iconSize: 22 * Theme.scale(Screen) property int pillPaddingHorizontal: 14 property bool autoHide: false @@ -47,7 +47,7 @@ Item { id: textItem anchors.centerIn: parent text: revealPill.text - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) font.family: Theme.fontFamily font.weight: Font.Bold color: textColor @@ -89,7 +89,7 @@ Item { Text { anchors.centerIn: parent font.family: showPill ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) text: revealPill.icon color: showPill ? iconTextColor : collapsedIconColor } diff --git a/Components/Spinner.qml b/Components/Spinner.qml index c902e17..73c8104 100644 --- a/Components/Spinner.qml +++ b/Components/Spinner.qml @@ -7,7 +7,7 @@ Item { property bool running: false property color color: "white" property int size: 16 - property int strokeWidth: 2 * Theme.uiScale + property int strokeWidth: 2 * Theme.scale(Screen) property int duration: 1000 implicitWidth: size diff --git a/Components/StyledTooltip.qml b/Components/StyledTooltip.qml index 90b6948..ecf7ffd 100644 --- a/Components/StyledTooltip.qml +++ b/Components/StyledTooltip.qml @@ -33,8 +33,8 @@ Window { } function _showNow() { - width = Math.max(50 * Theme.uiScale, tooltipText.implicitWidth + 24 * Theme.uiScale) - height = Math.max(50 * Theme.uiScale, tooltipText.implicitHeight + 16 * Theme.uiScale) + width = Math.max(50 * Theme.scale(Screen), tooltipText.implicitWidth + 24 * Theme.scale(Screen)) + height = Math.max(50 * Theme.scale(Screen), tooltipText.implicitHeight + 16 * Theme.scale(Screen)) if (!targetItem) return; @@ -75,10 +75,10 @@ Window { Rectangle { anchors.fill: parent - radius: 20 * Theme.uiScale + radius: 20 * Theme.scale(Screen) color: Theme.backgroundTertiary || "#222" border.color: Theme.outline || "#444" - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) opacity: 0.97 z: 1 } @@ -88,7 +88,7 @@ Window { text: tooltipWindow.text color: Theme.textPrimary font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -105,7 +105,7 @@ Window { } onTextChanged: { - width = Math.max(minimumWidth * Theme.uiScale, tooltipText.implicitWidth + 24 * Theme.uiScale); - height = Math.max(minimumHeight * Theme.uiScale, tooltipText.implicitHeight + 16 * Theme.uiScale); + width = Math.max(minimumWidth * Theme.scale(Screen), tooltipText.implicitWidth + 24 * Theme.scale(Screen)); + height = Math.max(minimumHeight * Theme.scale(Screen), tooltipText.implicitHeight + 16 * Theme.scale(Screen)); } } \ No newline at end of file diff --git a/Components/Tabs.qml b/Components/Tabs.qml index 319d2fe..0f7f74a 100644 --- a/Components/Tabs.qml +++ b/Components/Tabs.qml @@ -17,8 +17,8 @@ Item { model: root.tabsModel delegate: Rectangle { id: tabWrapper - implicitHeight: tab.height * Theme.uiScale - implicitWidth: 56 * Theme.uiScale + implicitHeight: tab.height * Theme.scale(Screen) + implicitWidth: 56 * Theme.scale(Screen) color: "transparent" property bool hovered: false @@ -48,7 +48,7 @@ Item { Text { text: modelData.icon font.family: "Material Symbols Outlined" - font.pixelSize: 22 * Theme.uiScale + font.pixelSize: 22 * Theme.scale(Screen) color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : tabWrapper.hovered ? (Theme ? Theme.accentPrimary : "#7C3AED") : (Theme ? Theme.textSecondary : "#444") Layout.alignment: Qt.AlignCenter } @@ -56,7 +56,7 @@ Item { // Label Text { text: modelData.label - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) 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 @@ -64,9 +64,9 @@ Item { // Underline for active tab Rectangle { - width: 24 * Theme.uiScale - height: 2 * Theme.uiScale - radius: 1 * Theme.uiScale + width: 24 * Theme.scale(Screen) + height: 2 * Theme.scale(Screen) + radius: 1 * Theme.scale(Screen) color: index === root.currentIndex ? (Theme ? Theme.accentPrimary : "#7C3AED") : "transparent" Layout.alignment: Qt.AlignCenter } diff --git a/Components/ToggleOption.qml b/Components/ToggleOption.qml index d2d0ce8..e99c727 100644 --- a/Components/ToggleOption.qml +++ b/Components/ToggleOption.qml @@ -19,19 +19,19 @@ ColumnLayout { Layout.fillWidth: true ColumnLayout { - spacing: 4 * Theme.uiScale + spacing: 4 * Theme.scale(Screen) Layout.fillWidth: true Text { text: label - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: description - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true @@ -42,20 +42,20 @@ ColumnLayout { Rectangle { id: switcher - width: 52 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale + width: 52 * Theme.scale(Screen) + height: 32 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) color: value ? Theme.accentPrimary : Theme.surfaceVariant border.color: value ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale + border.width: 2 * Theme.scale(Screen) Rectangle { - width: 28 * Theme.uiScale - height: 28 * Theme.uiScale - radius: 14 * Theme.uiScale + width: 28 * Theme.scale(Screen) + height: 28 * Theme.scale(Screen) + radius: 14 * Theme.scale(Screen) color: Theme.surface border.color: Theme.outline - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) y: 2 x: value ? switcher.width - width - 2 : 2 @@ -82,7 +82,7 @@ ColumnLayout { } Rectangle { - height: 8 * Theme.uiScale + height: 8 * Theme.scale(Screen) } } diff --git a/Settings/Theme.qml b/Settings/Theme.qml index 531018c..2ffe87d 100644 --- a/Settings/Theme.qml +++ b/Settings/Theme.qml @@ -15,10 +15,9 @@ Singleton { readonly property real scalingDampening: 0.2 // Automatic scaling based on screen width - function screenWidthRatio() { - // Get the primary screen width - if (Quickshell.screens && Quickshell.screens.length > 0) { - var rawRatio = Quickshell.screens[0].width / designScreenWidth + function scale(currentScreen) { + if (currentScreen !== undefined) { + var rawRatio = currentScreen.width / designScreenWidth // Apply dampening to reduce scaling for higher resolutions return Math.min(2.0, 1.0 + (rawRatio - 1.0) * scalingDampening) } @@ -117,9 +116,6 @@ Singleton { property color shadow: applyOpacity(themeData.shadow, "B3") property color overlay: applyOpacity(themeData.overlay, "66") - // Global UI scale factor - automatically calculated based on screen width - property real uiScale: screenWidthRatio() - // Font Properties property string fontFamily: "Roboto" // Family for all text diff --git a/Widgets/Dock.qml b/Widgets/Dock.qml index c2375da..578a97a 100644 --- a/Widgets/Dock.qml +++ b/Widgets/Dock.qml @@ -302,7 +302,7 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter text: "close" font.family: "Material Symbols Outlined" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: Theme.textPrimary } @@ -310,7 +310,7 @@ PanelWindow { anchors.verticalCenter: parent.verticalCenter text: "Close" font.family: Theme.fontFamily - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: Theme.textPrimary } } diff --git a/Widgets/LockScreen/BatteryCharge.qml b/Widgets/LockScreen/BatteryCharge.qml index 93100ac..ad00d45 100644 --- a/Widgets/LockScreen/BatteryCharge.qml +++ b/Widgets/LockScreen/BatteryCharge.qml @@ -58,7 +58,7 @@ Item { Text { text: batteryIcon() font.family: "Material Symbols Outlined" - font.pixelSize: 28 * Theme.uiScale + font.pixelSize: 28 * Theme.scale(Screen) color: charging ? Theme.accentPrimary : Theme.textSecondary verticalAlignment: Text.AlignVBottom } @@ -66,7 +66,7 @@ Item { Text { text: Math.round(percent) + "%" font.family: Theme.fontFamily - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: Theme.textSecondary verticalAlignment: Text.AlignVBottom } diff --git a/Widgets/LockScreen/LockScreen.qml b/Widgets/LockScreen/LockScreen.qml index fde716e..b6ab0a7 100644 --- a/Widgets/LockScreen/LockScreen.qml +++ b/Widgets/LockScreen/LockScreen.qml @@ -152,21 +152,21 @@ WlSessionLock { ColumnLayout { anchors.centerIn: parent spacing: 30 - width: Math.min(parent.width * 0.8, 400 * Theme.uiScale) + width: Math.min(parent.width * 0.8, 400 * Theme.scale(Screen)) Rectangle { Layout.alignment: Qt.AlignHCenter - width: 80 * Theme.uiScale - height: 80 * Theme.uiScale - radius: 40 * Theme.uiScale + width: 80 * Theme.scale(Screen) + height: 80 * Theme.scale(Screen) + radius: 40 * Theme.scale(Screen) color: Theme.accentPrimary Rectangle { anchors.fill: parent color: "transparent" - radius: 40 * Theme.uiScale + radius: 40 * Theme.scale(Screen) border.color: Theme.accentPrimary - border.width: 3 * Theme.uiScale + border.width: 3 * Theme.scale(Screen) z: 2 } @@ -183,28 +183,28 @@ WlSessionLock { Layout.alignment: Qt.AlignHCenter text: Quickshell.env("USER") font.family: Theme.fontFamily - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) font.weight: Font.Medium color: Theme.textPrimary } Rectangle { Layout.fillWidth: true - height: 50 * Theme.uiScale - radius: 25 * Theme.uiScale + height: 50 * Theme.scale(Screen) + radius: 25 * Theme.scale(Screen) color: Theme.surface opacity: passwordInput.activeFocus ? 0.8 : 0.3 border.color: passwordInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale + border.width: 2 * Theme.scale(Screen) TextInput { id: passwordInput anchors.fill: parent - anchors.margins: 15 * Theme.uiScale + anchors.margins: 15 * Theme.scale(Screen) verticalAlignment: TextInput.AlignVCenter horizontalAlignment: TextInput.AlignHCenter font.family: Theme.fontFamily - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: Theme.textPrimary echoMode: TextInput.Password passwordCharacter: "●" @@ -218,7 +218,7 @@ WlSessionLock { text: "Enter password..." color: Theme.textSecondary font.family: Theme.fontFamily - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) visible: !passwordInput.text && !passwordInput.activeFocus } @@ -238,9 +238,9 @@ WlSessionLock { id: errorMessageRect Layout.alignment: Qt.AlignHCenter width: parent.width * 0.8 - height: 44 * Theme.uiScale + height: 44 * Theme.scale(Screen) color: Theme.overlay - radius: 20 * Theme.uiScale + radius: 20 * Theme.scale(Screen) visible: lock.errorMessage !== "" Text { @@ -248,7 +248,7 @@ WlSessionLock { text: lock.errorMessage color: Theme.error font.family: Theme.fontFamily - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) opacity: 1 visible: lock.errorMessage !== "" } @@ -256,13 +256,13 @@ WlSessionLock { Rectangle { Layout.alignment: Qt.AlignHCenter - width: 120 * Theme.uiScale - height: 44 * Theme.uiScale - radius: 20 * Theme.uiScale + width: 120 * Theme.scale(Screen) + height: 44 * Theme.scale(Screen) + radius: 20 * Theme.scale(Screen) opacity: unlockButtonArea.containsMouse ? 0.8 : 0.5 color: unlockButtonArea.containsMouse ? Theme.accentPrimary : Theme.surface border.color: Theme.accentPrimary - border.width: 2 * Theme.uiScale + border.width: 2 * Theme.scale(Screen) enabled: !lock.authenticating Text { @@ -270,7 +270,7 @@ WlSessionLock { anchors.centerIn: parent text: lock.authenticating ? "..." : "Unlock" font.family: Theme.fontFamily - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) font.bold: true color: unlockButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } @@ -324,12 +324,12 @@ WlSessionLock { } Rectangle { - width: infoColumn.width + 32 * Theme.uiScale - height: infoColumn.height + 8 * Theme.uiScale + width: infoColumn.width + 32 * Theme.scale(Screen) + height: infoColumn.height + 8 * Theme.scale(Screen) color: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" anchors.horizontalCenter: parent.horizontalCenter - bottomLeftRadius: 20 * Theme.uiScale - bottomRightRadius: 20 * Theme.uiScale + bottomLeftRadius: 20 * Theme.scale(Screen) + bottomRightRadius: 20 * Theme.scale(Screen) ColumnLayout { id: infoColumn @@ -343,7 +343,7 @@ WlSessionLock { id: timeText text: Qt.formatDateTime(new Date(), "HH:mm") font.family: Theme.fontFamily - font.pixelSize: 48 * Theme.uiScale + font.pixelSize: 48 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary horizontalAlignment: Text.AlignHCenter @@ -353,7 +353,7 @@ WlSessionLock { id: dateText text: Qt.formatDateTime(new Date(), "dddd, MMMM d") font.family: Theme.fontFamily - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: Theme.textSecondary opacity: 0.8 horizontalAlignment: Text.AlignHCenter @@ -369,7 +369,7 @@ WlSessionLock { Text { text: weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud" font.family: "Material Symbols Outlined" - font.pixelSize: 28 * Theme.uiScale + font.pixelSize: 28 * Theme.scale(Screen) color: Theme.accentPrimary verticalAlignment: Text.AlignVCenter } @@ -377,7 +377,7 @@ WlSessionLock { Text { 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: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: Theme.textSecondary verticalAlignment: Text.AlignVCenter } @@ -388,7 +388,7 @@ WlSessionLock { color: Theme.error visible: weatherError !== "" font.family: Theme.fontFamily - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter } @@ -430,12 +430,12 @@ WlSessionLock { spacing: 12 Rectangle { - width: 48 * Theme.uiScale - height: 48 * Theme.uiScale - radius: 24 * Theme.uiScale + width: 48 * Theme.scale(Screen) + height: 48 * Theme.scale(Screen) + radius: 24 * Theme.scale(Screen) color: shutdownArea.containsMouse ? Theme.error : "transparent" border.color: Theme.error - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) MouseArea { id: shutdownArea @@ -450,18 +450,18 @@ WlSessionLock { anchors.centerIn: parent text: "power_settings_new" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: shutdownArea.containsMouse ? Theme.onAccent : Theme.error } } Rectangle { - width: 48 * Theme.uiScale - height: 48 * Theme.uiScale - radius: 24 * Theme.uiScale + width: 48 * Theme.scale(Screen) + height: 48 * Theme.scale(Screen) + radius: 24 * Theme.scale(Screen) color: rebootArea.containsMouse ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) MouseArea { id: rebootArea @@ -476,18 +476,18 @@ WlSessionLock { anchors.centerIn: parent text: "refresh" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: rebootArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } } Rectangle { - width: 48 * Theme.uiScale - height: 48 * Theme.uiScale - radius: 24 * Theme.uiScale + width: 48 * Theme.scale(Screen) + height: 48 * Theme.scale(Screen) + radius: 24 * Theme.scale(Screen) color: logoutArea.containsMouse ? Theme.accentSecondary : "transparent" border.color: Theme.accentSecondary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) MouseArea { id: logoutArea @@ -502,7 +502,7 @@ WlSessionLock { anchors.centerIn: parent text: "exit_to_app" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: logoutArea.containsMouse ? Theme.onAccent : Theme.accentSecondary } } diff --git a/Widgets/Notification/NotificationIcon.qml b/Widgets/Notification/NotificationIcon.qml index 586d977..dfc75a4 100644 --- a/Widgets/Notification/NotificationIcon.qml +++ b/Widgets/Notification/NotificationIcon.qml @@ -42,7 +42,7 @@ Item { } } font.family: mouseAreaBell.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) font.weight: { if (shell && shell.notificationHistoryWin && shell.notificationHistoryWin.hasUnread) { return Font.Bold; diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index efb2e3c..593c386 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -148,8 +148,8 @@ PanelWithOverlay { Rectangle { id: settingsWindowRect - implicitWidth: Quickshell.screens.length > 0 ? Math.min(Quickshell.screens[0].width * 2 / 3, 1200) * Theme.uiScale : 600 * Theme.uiScale - implicitHeight: Quickshell.screens.length > 0 ? Math.min(Quickshell.screens[0].height * 2 / 3, 800) * Theme.uiScale : 400 * Theme.uiScale + implicitWidth: Quickshell.screens.length > 0 ? Math.min(Quickshell.screens[0].width * 2 / 3, 1200) * Theme.scale(Screen) : 600 * Theme.scale(Screen) + implicitHeight: Quickshell.screens.length > 0 ? Math.min(Quickshell.screens[0].height * 2 / 3, 800) * Theme.scale(Screen) : 400 * Theme.scale(Screen) visible: parent.visible color: "transparent" // Center the settings window on screen @@ -166,9 +166,9 @@ PanelWithOverlay { color: Theme.backgroundPrimary anchors.fill: parent - radius: 20 * Theme.uiScale + radius: 20 * Theme.scale(Screen) border.color: Theme.outline - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) MultiEffect { source: background @@ -188,8 +188,8 @@ PanelWithOverlay { clip: true color: Theme.backgroundPrimary - topRightRadius: 20 * Theme.uiScale - bottomRightRadius: 20 * Theme.uiScale + topRightRadius: 20 * Theme.scale(Screen) + bottomRightRadius: 20 * Theme.scale(Screen) anchors { left: tabs.right @@ -202,7 +202,7 @@ PanelWithOverlay { Rectangle { id: headerArea - height: 48 * Theme.uiScale + height: 48 * Theme.scale(Screen) color: "transparent" anchors { @@ -214,13 +214,13 @@ PanelWithOverlay { RowLayout { anchors.fill: parent - spacing: 12 * Theme.uiScale + spacing: 12 * Theme.scale(Screen) Text { id: tabName text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : activeTabIndex === 1 ? "Bar" : activeTabIndex === 2 ? "Time & Weather" : activeTabIndex === 3 ? "Screen Recorder" : activeTabIndex === 4 ? "Network" : activeTabIndex === 5 ? "Display" : activeTabIndex === 6 ? "Wallpaper" : activeTabIndex === 7 ? "Misc" : activeTabIndex === 8 ? "About" : "General") - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary Layout.fillWidth: true @@ -228,29 +228,29 @@ PanelWithOverlay { // Wallpaper Selection Button (only visible on Wallpaper tab) Rectangle { - width: 160 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale + width: 160 * Theme.scale(Screen) + height: 32 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) visible: activeTabIndex === 6 // Wallpaper tab index Row { anchors.centerIn: parent - spacing: 6 * Theme.uiScale + spacing: 6 * Theme.scale(Screen) Text { text: "image" font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary anchors.verticalCenter: parent.verticalCenter } Text { text: "Select Wallpaper" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary anchors.verticalCenter: parent.verticalCenter @@ -273,18 +273,18 @@ PanelWithOverlay { } Rectangle { - width: 32 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale + width: 32 * Theme.scale(Screen) + height: 32 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) Text { anchors.centerIn: parent text: "close" font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary } @@ -304,7 +304,7 @@ PanelWithOverlay { } Rectangle { - height: 1 * Theme.uiScale + height: 1 * Theme.scale(Screen) color: Theme.outline opacity: 0.3 @@ -353,16 +353,16 @@ PanelWithOverlay { color: Theme.surface width: parent.width * 0.25 height: settingsWindowRect.height - topLeftRadius: 20 * Theme.uiScale - bottomLeftRadius: 20 * Theme.uiScale + topLeftRadius: 20 * Theme.scale(Screen) + bottomLeftRadius: 20 * Theme.scale(Screen) border.color: Theme.outline - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) Column { width: parent.width - spacing: 0 * Theme.uiScale - topPadding: 8 * Theme.uiScale - bottomPadding: 8 * Theme.uiScale + spacing: 0 * Theme.scale(Screen) + topPadding: 8 * Theme.scale(Screen) + bottomPadding: 8 * Theme.scale(Screen) Repeater { id: repeater @@ -398,21 +398,21 @@ PanelWithOverlay { delegate: Rectangle { width: tabs.width - height: 48 * Theme.uiScale + height: 48 * Theme.scale(Screen) color: "transparent" RowLayout { anchors.fill: parent - spacing: 8 * Theme.uiScale + spacing: 8 * Theme.scale(Screen) Rectangle { id: activeIndicator - Layout.leftMargin: 8 * Theme.uiScale - Layout.preferredWidth: 3 * Theme.uiScale - Layout.preferredHeight: 24 * Theme.uiScale + Layout.leftMargin: 8 * Theme.scale(Screen) + Layout.preferredWidth: 3 * Theme.scale(Screen) + Layout.preferredHeight: 24 * Theme.scale(Screen) Layout.alignment: Qt.AlignVCenter - radius: 2 * Theme.uiScale + radius: 2 * Theme.scale(Screen) color: Theme.accentPrimary opacity: index === activeTabIndex ? 1 : 0 @@ -430,12 +430,12 @@ PanelWithOverlay { text: modelData.icon font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary opacity: index === activeTabIndex ? 1 : 0.8 - Layout.leftMargin: 20 * Theme.uiScale - Layout.preferredWidth: 24 * Theme.uiScale - Layout.preferredHeight: 24 * Theme.uiScale + Layout.leftMargin: 20 * Theme.scale(Screen) + Layout.preferredWidth: 24 * Theme.scale(Screen) + Layout.preferredHeight: 24 * Theme.scale(Screen) Layout.alignment: Qt.AlignVCenter horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter @@ -446,14 +446,14 @@ PanelWithOverlay { id: label text: modelData.text - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: index === activeTabIndex ? Theme.accentPrimary : (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary) font.weight: index === activeTabIndex ? Font.DemiBold : (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal) Layout.fillWidth: true - Layout.preferredHeight: 24 * Theme.uiScale + Layout.preferredHeight: 24 * Theme.scale(Screen) Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter - Layout.leftMargin: 4 * Theme.uiScale - Layout.rightMargin: 16 * Theme.uiScale + Layout.leftMargin: 4 * Theme.scale(Screen) + Layout.rightMargin: 16 * Theme.scale(Screen) verticalAlignment: Text.AlignVCenter } @@ -473,7 +473,7 @@ PanelWithOverlay { Rectangle { width: parent.width - height: 1 * Theme.uiScale + height: 1 * Theme.scale(Screen) color: Theme.outline opacity: 0.6 visible: index < (repeater.count - 1) diff --git a/Widgets/SettingsWindow/Tabs/About.qml b/Widgets/SettingsWindow/Tabs/About.qml index 2e60129..a1b3244 100644 --- a/Widgets/SettingsWindow/Tabs/About.qml +++ b/Widgets/SettingsWindow/Tabs/About.qml @@ -168,19 +168,19 @@ ColumnLayout { Text { text: "Noctalia: quiet by design" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary Layout.alignment: Qt.AlignCenter - Layout.bottomMargin: 8 * Theme.uiScale + Layout.bottomMargin: 8 * Theme.scale(Screen) } Text { text: "It may just be another quickshell setup but it won't get in your way." - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: Theme.textSecondary Layout.alignment: Qt.AlignCenter - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } GridLayout { @@ -191,28 +191,28 @@ ColumnLayout { Text { text: "Latest Version:" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: Theme.textSecondary Layout.alignment: Qt.AlignRight } Text { text: root.latestVersion - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: Theme.textPrimary font.bold: true } Text { text: "Installed Version:" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: Theme.textSecondary Layout.alignment: Qt.AlignRight } Text { text: root.currentVersion - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: Theme.textPrimary font.bold: true } @@ -254,7 +254,7 @@ ColumnLayout { Text { text: "system_update" font.family: "Material Symbols Outlined" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary } @@ -262,7 +262,7 @@ ColumnLayout { id: updateText text: "Download latest release" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary } @@ -305,14 +305,14 @@ ColumnLayout { Text { text: "Contributors" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "(" + root.contributors.length + ")" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: Theme.textSecondary } @@ -385,7 +385,7 @@ ColumnLayout { anchors.centerIn: parent text: "person" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary visible: !avatarImage.source || avatarImage.status !== Image.Ready } @@ -399,7 +399,7 @@ ColumnLayout { Text { text: modelData.login || "Unknown" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary elide: Text.ElideRight Layout.fillWidth: true @@ -407,7 +407,7 @@ ColumnLayout { Text { text: (modelData.contributions || 0) + " commits" - font.pixelSize: 11 * Theme.uiScale + font.pixelSize: 11 * Theme.scale(Screen) color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary } diff --git a/Widgets/SettingsWindow/Tabs/Bar.qml b/Widgets/SettingsWindow/Tabs/Bar.qml index d0966fe..f37af0f 100644 --- a/Widgets/SettingsWindow/Tabs/Bar.qml +++ b/Widgets/SettingsWindow/Tabs/Bar.qml @@ -28,10 +28,10 @@ ColumnLayout { Text { text: "Elements" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ToggleOption { diff --git a/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml index 60b3124..5ef776d 100644 --- a/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/UnitSelector.qml @@ -7,20 +7,20 @@ import qs.Settings Rectangle { id: root - width: 64 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale + width: 64 * Theme.scale(Screen) + height: 32 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) color: Theme.surfaceVariant border.color: Theme.outline - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) property bool useFahrenheit: Settings.settings.useFahrenheit Rectangle { id: slider - width: parent.width / 2 - 4 * Theme.uiScale - height: parent.height - 4 * Theme.uiScale - radius: 14 * Theme.uiScale + width: parent.width / 2 - 4 * Theme.scale(Screen) + height: parent.height - 4 * Theme.scale(Screen) + radius: 14 * Theme.scale(Screen) color: Theme.accentPrimary x: 2 + (useFahrenheit ? parent.width / 2 : 0) y: 2 @@ -46,7 +46,7 @@ Rectangle { Text { anchors.centerIn: parent text: "°C" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: !useFahrenheit color: !useFahrenheit ? Theme.onAccent : Theme.textPrimary @@ -74,7 +74,7 @@ Rectangle { Text { anchors.centerIn: parent text: "°F" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: useFahrenheit color: useFahrenheit ? Theme.onAccent : Theme.textPrimary diff --git a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml index c70f783..ab28d4e 100644 --- a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml @@ -57,7 +57,7 @@ Rectangle { id: wallpaperGrid anchors.fill: parent - cellWidth: Math.max(120 * Theme.uiScale, (parent.width / 3) - 12 * Theme.uiScale) + cellWidth: Math.max(120 * Theme.scale(Screen), (parent.width / 3) - 12 * Theme.scale(Screen)) cellHeight: cellWidth * 0.6 model: WallpaperManager.wallpaperList cacheBuffer: 64 @@ -67,8 +67,8 @@ Rectangle { bottomMargin: 8 delegate: Item { - width: wallpaperGrid.cellWidth - 8 * Theme.uiScale - height: wallpaperGrid.cellHeight - 8 * Theme.uiScale + width: wallpaperGrid.cellWidth - 8 * Theme.scale(Screen) + height: wallpaperGrid.cellHeight - 8 * Theme.scale(Screen) Rectangle { id: wallpaperItem @@ -76,9 +76,9 @@ Rectangle { anchors.fill: parent anchors.margins: 3 color: Theme.surface - radius: 12 * Theme.uiScale + radius: 12 * Theme.scale(Screen) border.color: Settings.settings.currentWallpaper === modelData ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale + border.width: 2 * Theme.scale(Screen) Image { id: wallpaperImage @@ -91,8 +91,8 @@ Rectangle { cache: true smooth: true mipmap: true - sourceSize.width: Math.min(width, 480 * Theme.uiScale) - sourceSize.height: Math.min(height, 270 * Theme.uiScale) + sourceSize.width: Math.min(width, 480 * Theme.scale(Screen)) + sourceSize.height: Math.min(height, 270 * Theme.scale(Screen)) opacity: (wallpaperImage.status == Image.Ready) ? 1 : 0 // Apply circular mask for rounded corners layer.enabled: true @@ -122,7 +122,7 @@ Rectangle { Rectangle { width: wallpaperImage.width height: wallpaperImage.height - radius: 12 * Theme.uiScale + radius: 12 * Theme.scale(Screen) } } diff --git a/Widgets/SettingsWindow/Tabs/Display.qml b/Widgets/SettingsWindow/Tabs/Display.qml index b71b98b..85e2623 100644 --- a/Widgets/SettingsWindow/Tabs/Display.qml +++ b/Widgets/SettingsWindow/Tabs/Display.qml @@ -43,10 +43,10 @@ ColumnLayout { Text { text: "Monitor Selection" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ColumnLayout { @@ -65,14 +65,14 @@ ColumnLayout { Text { text: "Bar Monitors" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Select which monitors to display the top panel/bar on" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true @@ -112,14 +112,14 @@ ColumnLayout { Text { text: barCheckbox.isChecked ? "check" : "" font.family: "Material Symbols Outlined" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: barCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary visible: barCheckbox.isChecked } Text { text: modelData.name || "Unknown" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: barCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary } } @@ -169,14 +169,14 @@ ColumnLayout { Text { text: "Dock Monitors" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Select which monitors to display the application dock on" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true @@ -216,14 +216,14 @@ ColumnLayout { Text { text: dockCheckbox.isChecked ? "check" : "" font.family: "Material Symbols Outlined" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary visible: dockCheckbox.isChecked } Text { text: modelData.name || "Unknown" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: dockCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary } } @@ -276,14 +276,14 @@ ColumnLayout { Text { text: "Notification Monitors" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Select which monitors to display system notifications on" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true @@ -323,14 +323,14 @@ ColumnLayout { Text { text: notificationCheckbox.isChecked ? "check" : "" font.family: "Material Symbols Outlined" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textSecondary visible: notificationCheckbox.isChecked } Text { text: modelData.name || "Unknown" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: notificationCheckbox.isChecked ? Theme.onAccent : Theme.textPrimary } } diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index c6058fc..5cbc059 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -28,23 +28,23 @@ ColumnLayout { Text { text: "Profile" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } Text { text: "Profile Image" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 4 * Theme.uiScale + Layout.bottomMargin: 4 * Theme.scale(Screen) } Text { text: "Your profile picture displayed in various places throughout the shell" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true @@ -52,20 +52,20 @@ ColumnLayout { } RowLayout { - spacing: 8 * Theme.uiScale + spacing: 8 * Theme.scale(Screen) Layout.fillWidth: true Rectangle { - width: 48 * Theme.uiScale - height: 48 * Theme.uiScale - radius: 24 * Theme.uiScale + width: 48 * Theme.scale(Screen) + height: 48 * Theme.scale(Screen) + radius: 24 * Theme.scale(Screen) Rectangle { anchors.fill: parent color: "transparent" - radius: 24 * Theme.uiScale + radius: 24 * Theme.scale(Screen) border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 2 * Theme.uiScale + border.width: 2 * Theme.scale(Screen) z: 2 } @@ -76,22 +76,22 @@ ColumnLayout { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 40 * Theme.uiScale - radius: 16 * Theme.uiScale + Layout.preferredHeight: 40 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) color: Theme.surfaceVariant border.color: profileImageInput.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) TextInput { id: profileImageInput anchors.fill: parent - anchors.leftMargin: 12 * Theme.uiScale - anchors.rightMargin: 12 * Theme.uiScale - anchors.topMargin: 6 * Theme.uiScale - anchors.bottomMargin: 6 * Theme.uiScale + anchors.leftMargin: 12 * Theme.scale(Screen) + anchors.rightMargin: 12 * Theme.scale(Screen) + anchors.topMargin: 6 * Theme.scale(Screen) + anchors.bottomMargin: 6 * Theme.scale(Screen) text: Settings.settings.profileImage - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -117,19 +117,19 @@ ColumnLayout { // Separator Rectangle { Layout.fillWidth: true - Layout.topMargin: 26 * Theme.uiScale - Layout.bottomMargin: 18 * Theme.uiScale - height: 1 * Theme.uiScale + Layout.topMargin: 26 * Theme.scale(Screen) + Layout.bottomMargin: 18 * Theme.scale(Screen) + height: 1 * Theme.scale(Screen) color: Theme.outline opacity: 0.3 } Text { text: "User Interface" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ToggleOption { diff --git a/Widgets/SettingsWindow/Tabs/Misc.qml b/Widgets/SettingsWindow/Tabs/Misc.qml index 861d93d..4281e25 100644 --- a/Widgets/SettingsWindow/Tabs/Misc.qml +++ b/Widgets/SettingsWindow/Tabs/Misc.qml @@ -28,10 +28,10 @@ ColumnLayout { Text { text: "Media" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ColumnLayout { @@ -40,14 +40,14 @@ ColumnLayout { Text { text: "Visualizer Type" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Choose the style of the audio visualizer" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true diff --git a/Widgets/SettingsWindow/Tabs/Network.qml b/Widgets/SettingsWindow/Tabs/Network.qml index 5bc582c..09ec6ce 100644 --- a/Widgets/SettingsWindow/Tabs/Network.qml +++ b/Widgets/SettingsWindow/Tabs/Network.qml @@ -30,10 +30,10 @@ ColumnLayout { Text { text: "Wi-Fi" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ToggleOption { @@ -58,10 +58,10 @@ ColumnLayout { Text { text: "Bluetooth" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ToggleOption { diff --git a/Widgets/SettingsWindow/Tabs/ScreenRecorder.qml b/Widgets/SettingsWindow/Tabs/ScreenRecorder.qml index ccb9250..166bf06 100644 --- a/Widgets/SettingsWindow/Tabs/ScreenRecorder.qml +++ b/Widgets/SettingsWindow/Tabs/ScreenRecorder.qml @@ -34,7 +34,7 @@ ColumnLayout { ColumnLayout { // Text { // text: "Screen Recording" - // font.pixelSize: 18 * Theme.uiScale + // font.pixelSize: 18 * Theme.scale(Screen) // font.bold: true // color: Theme.textPrimary // Layout.bottomMargin: 8 @@ -49,14 +49,14 @@ ColumnLayout { Text { text: "Output Directory" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Directory where screen recordings will be saved" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -78,7 +78,7 @@ ColumnLayout { anchors.topMargin: 6 anchors.bottomMargin: 6 text: Settings.settings.videoPath !== undefined ? Settings.settings.videoPath : "" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -108,14 +108,14 @@ ColumnLayout { Text { text: "Frame Rate" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Target frame rate for screen recordings (default: 60)" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -144,7 +144,7 @@ ColumnLayout { contentItem: TextInput { text: frameRateSpinBox.textFromValue(frameRateSpinBox.value, frameRateSpinBox.locale) - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary selectionColor: Theme.accentPrimary selectedTextColor: Theme.onAccent @@ -183,7 +183,7 @@ ColumnLayout { Text { text: "add" font.family: "Material Symbols Outlined" - font.pixelSize: 20 * Theme.uiScale + font.pixelSize: 20 * Theme.scale(Screen) color: Theme.textPrimary anchors.centerIn: parent } @@ -200,7 +200,7 @@ ColumnLayout { Text { text: "remove" font.family: "Material Symbols Outlined" - font.pixelSize: 20 * Theme.uiScale + font.pixelSize: 20 * Theme.scale(Screen) color: Theme.textPrimary anchors.centerIn: parent } @@ -218,14 +218,14 @@ ColumnLayout { Text { text: "Audio Source" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Audio source to capture during recording" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -265,7 +265,7 @@ ColumnLayout { return audioSourceComboBox.currentText; } } - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -276,7 +276,7 @@ ColumnLayout { y: audioSourceComboBox.topPadding + (audioSourceComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.textPrimary } @@ -323,7 +323,7 @@ ColumnLayout { return modelData; } } - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -346,14 +346,14 @@ ColumnLayout { Text { text: "Video Quality" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Higher quality results in larger file sizes" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -395,7 +395,7 @@ ColumnLayout { return qualityComboBox.currentText; } } - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -406,7 +406,7 @@ ColumnLayout { y: qualityComboBox.topPadding + (qualityComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.textPrimary } @@ -455,7 +455,7 @@ ColumnLayout { return modelData; } } - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -478,14 +478,14 @@ ColumnLayout { Text { text: "Video Codec" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Different codecs offer different compression and compatibility" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -514,7 +514,7 @@ ColumnLayout { leftPadding: 12 rightPadding: codecComboBox.indicator.width + codecComboBox.spacing text: codecComboBox.currentText.toUpperCase() - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -525,7 +525,7 @@ ColumnLayout { y: codecComboBox.topPadding + (codecComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.textPrimary } @@ -561,7 +561,7 @@ ColumnLayout { contentItem: Text { text: modelData.toUpperCase() - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -584,14 +584,14 @@ ColumnLayout { Text { text: "Audio Codec" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Opus is recommended for best performance and smallest audio size" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -620,7 +620,7 @@ ColumnLayout { leftPadding: 12 rightPadding: audioCodecComboBox.indicator.width + audioCodecComboBox.spacing text: audioCodecComboBox.currentText.toUpperCase() - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -631,7 +631,7 @@ ColumnLayout { y: audioCodecComboBox.topPadding + (audioCodecComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.textPrimary } @@ -667,7 +667,7 @@ ColumnLayout { contentItem: Text { text: modelData.toUpperCase() - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -691,14 +691,14 @@ ColumnLayout { Text { text: "Color Range" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Limited is recommended for better compatibility" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.bottomMargin: 4 } @@ -727,7 +727,7 @@ ColumnLayout { leftPadding: 12 rightPadding: colorRangeComboBox.indicator.width + colorRangeComboBox.spacing text: colorRangeComboBox.currentText.charAt(0).toUpperCase() + colorRangeComboBox.currentText.slice(1) - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -738,7 +738,7 @@ ColumnLayout { y: colorRangeComboBox.topPadding + (colorRangeComboBox.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.textPrimary } @@ -774,7 +774,7 @@ ColumnLayout { contentItem: Text { text: modelData.charAt(0).toUpperCase() + modelData.slice(1) - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight diff --git a/Widgets/SettingsWindow/Tabs/TimeWeather.qml b/Widgets/SettingsWindow/Tabs/TimeWeather.qml index 3ae3205..e228df6 100644 --- a/Widgets/SettingsWindow/Tabs/TimeWeather.qml +++ b/Widgets/SettingsWindow/Tabs/TimeWeather.qml @@ -29,10 +29,10 @@ ColumnLayout { Text { text: "Time" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ToggleOption { @@ -64,27 +64,27 @@ ColumnLayout { Text { text: "Weather" - font.pixelSize: 18 * Theme.uiScale + font.pixelSize: 18 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary - Layout.bottomMargin: 16 * Theme.uiScale + Layout.bottomMargin: 16 * Theme.scale(Screen) } ColumnLayout { spacing: 8 Layout.fillWidth: true - Layout.bottomMargin: 8 * Theme.uiScale + Layout.bottomMargin: 8 * Theme.scale(Screen) Text { text: "City" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Your city name for weather information" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary Layout.fillWidth: true } @@ -106,7 +106,7 @@ ColumnLayout { anchors.topMargin: 6 anchors.bottomMargin: 6 text: Settings.settings.weatherCity - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: TextInput.AlignVCenter clip: true @@ -147,14 +147,14 @@ ColumnLayout { Text { text: "Temperature Unit" - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: "Choose between Celsius and Fahrenheit" - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary wrapMode: Text.WordWrap Layout.fillWidth: true diff --git a/Widgets/SidePanel/Button.qml b/Widgets/SidePanel/Button.qml index 94c9e65..d4b2b51 100644 --- a/Widgets/SidePanel/Button.qml +++ b/Widgets/SidePanel/Button.qml @@ -44,7 +44,7 @@ Item { id: iconText text: "dashboard" font.family: isActive ? "Material Symbols Rounded" : "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: sidebarPopup.visible ? Theme.accentPrimary : Theme.textPrimary anchors.centerIn: parent z: 1 diff --git a/Widgets/SidePanel/Music.qml b/Widgets/SidePanel/Music.qml index c51a546..95ec4e4 100644 --- a/Widgets/SidePanel/Music.qml +++ b/Widgets/SidePanel/Music.qml @@ -8,15 +8,15 @@ import qs.Services Rectangle { id: musicCard - width: 360 * Theme.uiScale - height: 250 * Theme.uiScale + width: 360 * Theme.scale(Screen) + height: 250 * Theme.scale(Screen) color: "transparent" Rectangle { id: card anchors.fill: parent color: Theme.surface - radius: 18 * Theme.uiScale + radius: 18 * Theme.scale(Screen) // Show fallback UI if no player is available Item { @@ -26,12 +26,12 @@ Rectangle { ColumnLayout { anchors.centerIn: parent - spacing: 16 * Theme.uiScale + spacing: 16 * Theme.scale(Screen) Text { text: "music_note" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeHeader * Theme.uiScale + font.pixelSize: Theme.fontSizeHeader * Theme.scale(Screen) color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) Layout.alignment: Qt.AlignHCenter } @@ -40,7 +40,7 @@ Rectangle { text: MusicManager.hasPlayer ? "No controllable player selected" : "No music player detected" color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.6) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) Layout.alignment: Qt.AlignHCenter } } @@ -49,45 +49,45 @@ Rectangle { // Main player UI ColumnLayout { anchors.fill: parent - anchors.margins: 18 * Theme.uiScale - spacing: 12 * Theme.uiScale + anchors.margins: 18 * Theme.scale(Screen) + spacing: 12 * Theme.scale(Screen) visible: !!MusicManager.currentPlayer // Player selector ComboBox { id: playerSelector Layout.fillWidth: true - Layout.preferredHeight: 40 * Theme.uiScale + Layout.preferredHeight: 40 * Theme.scale(Screen) visible: MusicManager.getAvailablePlayers().length > 1 model: MusicManager.getAvailablePlayers() textRole: "identity" currentIndex: MusicManager.selectedPlayerIndex background: Rectangle { - implicitWidth: 120 * Theme.uiScale - implicitHeight: 40 * Theme.uiScale + implicitWidth: 120 * Theme.scale(Screen) + implicitHeight: 40 * Theme.scale(Screen) color: Theme.surfaceVariant border.color: playerSelector.activeFocus ? Theme.accentPrimary : Theme.outline - border.width: 1 * Theme.uiScale - radius: 16 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) } contentItem: Text { - leftPadding: 12 * Theme.uiScale + leftPadding: 12 * Theme.scale(Screen) rightPadding: playerSelector.indicator.width + playerSelector.spacing text: playerSelector.displayText - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight } indicator: Text { - x: playerSelector.width - width - 12 * Theme.uiScale + x: playerSelector.width - width - 12 * Theme.scale(Screen) y: playerSelector.topPadding + (playerSelector.availableHeight - height) / 2 text: "arrow_drop_down" font.family: "Material Symbols Outlined" - font.pixelSize: 24 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) color: Theme.textPrimary } @@ -95,7 +95,7 @@ Rectangle { y: playerSelector.height width: playerSelector.width implicitHeight: contentItem.implicitHeight - padding: 1 * Theme.uiScale + padding: 1 * Theme.scale(Screen) contentItem: ListView { clip: true @@ -109,8 +109,8 @@ Rectangle { background: Rectangle { color: Theme.surfaceVariant border.color: Theme.outline - border.width: 1 * Theme.uiScale - radius: 16 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) } } @@ -118,7 +118,7 @@ Rectangle { width: playerSelector.width contentItem: Text { text: modelData.identity - font.pixelSize: 13 * Theme.uiScale + font.pixelSize: 13 * Theme.scale(Screen) color: Theme.textPrimary verticalAlignment: Text.AlignVCenter elide: Text.ElideRight @@ -138,14 +138,14 @@ Rectangle { // Album art with spectrum visualizer RowLayout { - spacing: 12 * Theme.uiScale + spacing: 12 * Theme.scale(Screen) Layout.fillWidth: true // Album art container with circular spectrum overlay Item { id: albumArtContainer - width: 96 * Theme.uiScale - height: 96 * Theme.uiScale // enough for spectrum and art (will adjust if needed) + width: 96 * Theme.scale(Screen) + height: 96 * Theme.scale(Screen) // enough for spectrum and art (will adjust if needed) Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter // Circular spectrum visualizer around album art @@ -153,36 +153,36 @@ Rectangle { id: spectrum values: MusicManager.cavaValues anchors.centerIn: parent - innerRadius: 30 * Theme.uiScale // Position just outside 60x60 album art - outerRadius: 48 * Theme.uiScale // Extend bars outward from album art + innerRadius: 30 * Theme.scale(Screen) // Position just outside 60x60 album art + outerRadius: 48 * Theme.scale(Screen) // Extend bars outward from album art fillColor: Theme.accentPrimary strokeColor: Theme.accentPrimary - strokeWidth: 0 * Theme.uiScale + strokeWidth: 0 * Theme.scale(Screen) z: 0 } // Album art image Rectangle { id: albumArtwork - width: 60 * Theme.uiScale - height: 60 * Theme.uiScale + width: 60 * Theme.scale(Screen) + height: 60 * Theme.scale(Screen) anchors.centerIn: parent - radius: 30 * Theme.uiScale // circle + radius: 30 * Theme.scale(Screen) // circle color: Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) Image { id: albumArt anchors.fill: parent - anchors.margins: 2 * Theme.uiScale + anchors.margins: 2 * Theme.scale(Screen) fillMode: Image.PreserveAspectCrop smooth: true mipmap: true cache: false asynchronous: true - sourceSize.width: 60 * Theme.uiScale - sourceSize.height: 60 * Theme.uiScale + sourceSize.width: 60 * Theme.scale(Screen) + sourceSize.height: 60 * Theme.scale(Screen) source: MusicManager.trackArtUrl visible: source.toString() !== "" @@ -213,7 +213,7 @@ Rectangle { anchors.centerIn: parent text: "album" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody * Theme.uiScale + font.pixelSize: Theme.fontSizeBody * Theme.scale(Screen) color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.4) visible: !albumArt.visible } @@ -223,13 +223,13 @@ Rectangle { // Track metadata ColumnLayout { Layout.fillWidth: true - spacing: 4 * Theme.uiScale + spacing: 4 * Theme.scale(Screen) Text { text: MusicManager.trackTitle color: Theme.textPrimary font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeSmall * Theme.uiScale + font.pixelSize: Theme.fontSizeSmall * Theme.scale(Screen) font.bold: true elide: Text.ElideRight wrapMode: Text.Wrap @@ -241,7 +241,7 @@ Rectangle { text: MusicManager.trackArtist color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.8) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption * Theme.uiScale + font.pixelSize: Theme.fontSizeCaption * Theme.scale(Screen) elide: Text.ElideRight Layout.fillWidth: true } @@ -250,7 +250,7 @@ Rectangle { text: MusicManager.trackAlbum color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.6) font.family: Theme.fontFamily - font.pixelSize: Theme.fontSizeCaption * Theme.uiScale + font.pixelSize: Theme.fontSizeCaption * Theme.scale(Screen) elide: Text.ElideRight Layout.fillWidth: true } @@ -261,8 +261,8 @@ Rectangle { Rectangle { id: progressBarBackground width: parent.width - height: 6 * Theme.uiScale - radius: 3 * Theme.uiScale + height: 6 * Theme.scale(Screen) + radius: 3 * Theme.scale(Screen) color: Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.15) Layout.fillWidth: true @@ -290,12 +290,12 @@ Rectangle { // Interactive progress handle Rectangle { id: progressHandle - width: 12 * Theme.uiScale - height: 12 * Theme.uiScale - radius: 6 * Theme.uiScale + width: 12 * Theme.scale(Screen) + height: 12 * Theme.scale(Screen) + radius: 6 * Theme.scale(Screen) color: Theme.accentPrimary border.color: Qt.lighter(Theme.accentPrimary, 1.3) - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) x: Math.max(0, Math.min(parent.width - width, progressFill.width - width / 2)) anchors.verticalCenter: parent.verticalCenter @@ -334,18 +334,18 @@ Rectangle { // Media controls RowLayout { - spacing: 4 * Theme.uiScale + spacing: 4 * Theme.scale(Screen) Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter // Previous button Rectangle { - width: 28 * Theme.uiScale - height: 28 * Theme.uiScale - radius: 14 * Theme.uiScale + width: 28 * Theme.scale(Screen) + height: 28 * Theme.scale(Screen) + radius: 14 * Theme.scale(Screen) color: previousButton.containsMouse ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.2) : Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) MouseArea { id: previousButton @@ -360,19 +360,19 @@ Rectangle { anchors.centerIn: parent text: "skip_previous" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeCaption * Theme.uiScale + font.pixelSize: Theme.fontSizeCaption * Theme.scale(Screen) color: previousButton.enabled ? Theme.accentPrimary : Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) } } // Play/Pause button Rectangle { - width: 36 * Theme.uiScale - height: 36 * Theme.uiScale - radius: 18 * Theme.uiScale + width: 36 * Theme.scale(Screen) + height: 36 * Theme.scale(Screen) + radius: 18 * Theme.scale(Screen) color: playButton.containsMouse ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.2) : Qt.darker(Theme.surface, 1.1) border.color: Theme.accentPrimary - border.width: 2 * Theme.uiScale + border.width: 2 * Theme.scale(Screen) MouseArea { id: playButton @@ -387,19 +387,19 @@ Rectangle { anchors.centerIn: parent text: MusicManager.isPlaying ? "pause" : "play_arrow" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeBody * Theme.uiScale + font.pixelSize: Theme.fontSizeBody * Theme.scale(Screen) color: playButton.enabled ? Theme.accentPrimary : Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) } } // Next button Rectangle { - width: 28 * Theme.uiScale - height: 28 * Theme.uiScale - radius: 14 * Theme.uiScale + width: 28 * Theme.scale(Screen) + height: 28 * Theme.scale(Screen) + radius: 14 * Theme.scale(Screen) color: nextButton.containsMouse ? Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.2) : Qt.darker(Theme.surface, 1.1) border.color: Qt.rgba(Theme.accentPrimary.r, Theme.accentPrimary.g, Theme.accentPrimary.b, 0.3) - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) MouseArea { id: nextButton @@ -414,7 +414,7 @@ Rectangle { anchors.centerIn: parent text: "skip_next" font.family: "Material Symbols Outlined" - font.pixelSize: Theme.fontSizeCaption * Theme.uiScale + font.pixelSize: Theme.fontSizeCaption * Theme.scale(Screen) color: nextButton.enabled ? Theme.accentPrimary : Qt.rgba(Theme.textPrimary.r, Theme.textPrimary.g, Theme.textPrimary.b, 0.3) } } diff --git a/Widgets/SidePanel/PanelPopup.qml b/Widgets/SidePanel/PanelPopup.qml index 1a69956..a3dbf86 100644 --- a/Widgets/SidePanel/PanelPopup.qml +++ b/Widgets/SidePanel/PanelPopup.qml @@ -45,8 +45,8 @@ PanelWithOverlay { property real slideOffset: width property bool isAnimating: false - property int leftPadding: 20 * Theme.uiScale - property int bottomPadding: 20 * Theme.uiScale + property int leftPadding: 20 * Theme.scale(Screen) + property int bottomPadding: 20 * Theme.scale(Screen) // Recording properties property bool isRecording: false @@ -126,8 +126,8 @@ PanelWithOverlay { isRecording = false; } - implicitWidth: 500 * Theme.uiScale - implicitHeight: 700 * Theme.uiScale + implicitWidth: 500 * Theme.scale(Screen) + implicitHeight: 700 * Theme.scale(Screen) visible: parent.visible color: "transparent" anchors.top: parent.top @@ -176,7 +176,7 @@ PanelWithOverlay { x: sidebarPopupRect.leftPadding + sidebarPopupRect.slideOffset y: 0 color: Theme.backgroundPrimary - bottomLeftRadius: 20 * Theme.uiScale + bottomLeftRadius: 20 * Theme.scale(Screen) z: 0 Behavior on x { @@ -211,8 +211,8 @@ PanelWithOverlay { ColumnLayout { anchors.fill: parent - anchors.margins: 20 * Theme.uiScale - spacing: 4 * Theme.uiScale + anchors.margins: 20 * Theme.scale(Screen) + spacing: 4 * Theme.scale(Screen) PowerMenu { id: systemWidget @@ -230,7 +230,7 @@ PanelWithOverlay { // Music and System Monitor row RowLayout { - spacing: 12 * Theme.uiScale + spacing: 12 * Theme.scale(Screen) Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter @@ -248,37 +248,37 @@ PanelWithOverlay { // Power profile, Record and Wallpaper row RowLayout { Layout.alignment: Qt.AlignVCenter - spacing: 10 * Theme.uiScale - Layout.preferredHeight: 80 * Theme.uiScale + spacing: 10 * Theme.scale(Screen) + Layout.preferredHeight: 80 * Theme.scale(Screen) z: 3 PowerProfile { Layout.alignment: Qt.AlignVCenter - Layout.preferredHeight: 80 * Theme.uiScale + Layout.preferredHeight: 80 * Theme.scale(Screen) } // Record and Wallpaper card Rectangle { - Layout.preferredHeight: 80 * Theme.uiScale - Layout.preferredWidth: 140 * Theme.uiScale + Layout.preferredHeight: 80 * Theme.scale(Screen) + Layout.preferredWidth: 140 * Theme.scale(Screen) Layout.fillWidth: false color: Theme.surface - radius: 18 * Theme.uiScale + radius: 18 * Theme.scale(Screen) Row { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - spacing: 20 * Theme.uiScale + spacing: 20 * Theme.scale(Screen) // Record button Rectangle { id: recordButton - width: 36 * Theme.uiScale - height: 36 * Theme.uiScale - radius: 18 * Theme.uiScale + width: 36 * Theme.scale(Screen) + height: 36 * Theme.scale(Screen) + radius: 18 * Theme.scale(Screen) border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) color: sidebarPopupRect.isRecording ? Theme.accentPrimary : (recordButtonArea.containsMouse ? Theme.accentPrimary : "transparent") @@ -286,7 +286,7 @@ PanelWithOverlay { anchors.centerIn: parent text: "photo_camera" font.family: "Material Symbols Outlined" - font.pixelSize: 22 * Theme.uiScale + font.pixelSize: 22 * Theme.scale(Screen) color: sidebarPopupRect.isRecording || recordButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter @@ -321,18 +321,18 @@ PanelWithOverlay { Rectangle { id: wallpaperButton - width: 36 * Theme.uiScale - height: 36 * Theme.uiScale - radius: 18 * Theme.uiScale + width: 36 * Theme.scale(Screen) + height: 36 * Theme.scale(Screen) + radius: 18 * Theme.scale(Screen) border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent" Text { anchors.centerIn: parent text: "image" font.family: "Material Symbols Outlined" - font.pixelSize: 22 * Theme.uiScale + font.pixelSize: 22 * Theme.scale(Screen) color: wallpaperButtonArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter diff --git a/Widgets/SidePanel/PowerMenu.qml b/Widgets/SidePanel/PowerMenu.qml index f1c3564..ec60ed4 100644 --- a/Widgets/SidePanel/PowerMenu.qml +++ b/Widgets/SidePanel/PowerMenu.qml @@ -44,8 +44,8 @@ Rectangle { uptimeProcess.running = true; } - width: 440 * Theme.uiScale - height: 80 * Theme.uiScale + width: 440 * Theme.scale(Screen) + height: 80 * Theme.scale(Screen) color: "transparent" anchors.horizontalCenterOffset: -2 onPanelVisibleChanged: { @@ -62,29 +62,29 @@ Rectangle { anchors.fill: parent color: Theme.surface - radius: 18 * Theme.uiScale + radius: 18 * Theme.scale(Screen) ColumnLayout { anchors.fill: parent - anchors.margins: 18 * Theme.uiScale - spacing: 12 * Theme.uiScale + anchors.margins: 18 * Theme.scale(Screen) + spacing: 12 * Theme.scale(Screen) RowLayout { Layout.fillWidth: true - spacing: 12 * Theme.uiScale + spacing: 12 * Theme.scale(Screen) Rectangle { - width: 48 * Theme.uiScale - height: 48 * Theme.uiScale - radius: 24 * Theme.uiScale + width: 48 * Theme.scale(Screen) + height: 48 * Theme.scale(Screen) + radius: 24 * Theme.scale(Screen) color: Theme.accentPrimary Rectangle { anchors.fill: parent color: "transparent" - radius: 24 * Theme.uiScale + radius: 24 * Theme.scale(Screen) border.color: Theme.accentPrimary - border.width: 2 * Theme.uiScale + border.width: 2 * Theme.scale(Screen) z: 2 } @@ -94,13 +94,13 @@ Rectangle { } ColumnLayout { - spacing: 4 * Theme.uiScale + spacing: 4 * Theme.scale(Screen) Layout.fillWidth: true Text { text: Quickshell.env("USER") font.family: Theme.fontFamily - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } @@ -108,7 +108,7 @@ Rectangle { Text { text: "System Uptime: " + uptimeText font.family: Theme.fontFamily - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary } @@ -121,19 +121,19 @@ Rectangle { Rectangle { id: settingsButton - width: 32 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale + width: 32 * Theme.scale(Screen) + height: 32 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) color: settingsButtonArea.containsMouse || settingsButtonArea.pressed ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) Text { anchors.centerIn: parent anchors.horizontalCenterOffset: -1 text: "settings" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: settingsButtonArea.containsMouse || settingsButtonArea.pressed ? Theme.backgroundPrimary : Theme.accentPrimary } @@ -162,18 +162,18 @@ Rectangle { Rectangle { id: systemButton - width: 32 * Theme.uiScale - height: 32 * Theme.uiScale - radius: 16 * Theme.uiScale + width: 32 * Theme.scale(Screen) + height: 32 * Theme.scale(Screen) + radius: 16 * Theme.scale(Screen) color: systemButtonArea.containsMouse || systemButtonArea.pressed ? Theme.accentPrimary : "transparent" border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) Text { anchors.centerIn: parent text: "power_settings_new" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: systemButtonArea.containsMouse || systemButtonArea.pressed ? Theme.backgroundPrimary : Theme.accentPrimary } @@ -211,18 +211,18 @@ Rectangle { anchors.right: systemButton.right Rectangle { - width: 160 * Theme.uiScale - height: 220 * Theme.uiScale + width: 160 * Theme.scale(Screen) + height: 220 * Theme.scale(Screen) color: Theme.surface - radius: 8 * Theme.uiScale + radius: 8 * Theme.scale(Screen) border.color: Theme.outline - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) visible: true z: 9999 anchors.top: parent.top anchors.right: parent.right - anchors.rightMargin: 32 * Theme.uiScale - anchors.topMargin: systemButton.y + systemButton.height + 48 * Theme.uiScale + anchors.rightMargin: 32 * Theme.scale(Screen) + anchors.topMargin: systemButton.y + systemButton.height + 48 * Theme.scale(Screen) // Prevent closing when clicking in the panel bg MouseArea { @@ -231,31 +231,31 @@ Rectangle { ColumnLayout { anchors.fill: parent - anchors.margins: 8 * Theme.uiScale - spacing: 4 * Theme.uiScale + anchors.margins: 8 * Theme.scale(Screen) + spacing: 4 * Theme.scale(Screen) Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 * Theme.uiScale - radius: 6 * Theme.uiScale + Layout.preferredHeight: 36 * Theme.scale(Screen) + radius: 6 * Theme.scale(Screen) color: lockButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 * Theme.uiScale - spacing: 8 * Theme.uiScale + anchors.margins: 12 * Theme.scale(Screen) + spacing: 8 * Theme.scale(Screen) Text { text: "lock_outline" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: lockButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Lock Screen" font.family: Theme.fontFamily - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: lockButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -278,25 +278,25 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 * Theme.uiScale - radius: 6 * Theme.uiScale + Layout.preferredHeight: 36 * Theme.scale(Screen) + radius: 6 * Theme.scale(Screen) color: suspendButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 * Theme.uiScale - spacing: 8 * Theme.uiScale + anchors.margins: 12 * Theme.scale(Screen) + spacing: 8 * Theme.scale(Screen) Text { text: "bedtime" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: suspendButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Suspend" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: suspendButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -319,26 +319,26 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 * Theme.uiScale - radius: 6 * Theme.uiScale + Layout.preferredHeight: 36 * Theme.scale(Screen) + radius: 6 * Theme.scale(Screen) color: rebootButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 * Theme.uiScale - spacing: 8 * Theme.uiScale + anchors.margins: 12 * Theme.scale(Screen) + spacing: 8 * Theme.scale(Screen) Text { text: "refresh" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: rebootButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Reboot" font.family: Theme.fontFamily - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: rebootButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -361,25 +361,25 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 * Theme.uiScale - radius: 6 * Theme.uiScale + Layout.preferredHeight: 36 * Theme.scale(Screen) + radius: 6 * Theme.scale(Screen) color: logoutButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 * Theme.uiScale - spacing: 8 * Theme.uiScale + anchors.margins: 12 * Theme.scale(Screen) + spacing: 8 * Theme.scale(Screen) Text { text: "exit_to_app" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: logoutButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Logout" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: logoutButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } @@ -402,25 +402,25 @@ Rectangle { Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 36 * Theme.uiScale - radius: 6 * Theme.uiScale + Layout.preferredHeight: 36 * Theme.scale(Screen) + radius: 6 * Theme.scale(Screen) color: shutdownButtonArea.containsMouse ? Theme.accentPrimary : "transparent" RowLayout { anchors.fill: parent - anchors.margins: 12 * Theme.uiScale - spacing: 8 * Theme.uiScale + anchors.margins: 12 * Theme.scale(Screen) + spacing: 8 * Theme.scale(Screen) Text { text: "power_settings_new" font.family: "Material Symbols Outlined" - font.pixelSize: 16 * Theme.uiScale + font.pixelSize: 16 * Theme.scale(Screen) color: shutdownButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary } Text { text: "Shutdown" - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) color: shutdownButtonArea.containsMouse ? Theme.onAccent : Theme.textPrimary Layout.fillWidth: true } diff --git a/Widgets/SidePanel/PowerProfile.qml b/Widgets/SidePanel/PowerProfile.qml index 775046e..409ebac 100644 --- a/Widgets/SidePanel/PowerProfile.qml +++ b/Widgets/SidePanel/PowerProfile.qml @@ -7,22 +7,22 @@ import qs.Components Rectangle { id: card - width: 200 * Theme.uiScale - height: 70 * Theme.uiScale + width: 200 * Theme.scale(Screen) + height: 70 * Theme.scale(Screen) color: Theme.surface - radius: 18 * Theme.uiScale + radius: 18 * Theme.scale(Screen) Row { anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - spacing: 20 * Theme.uiScale + spacing: 20 * Theme.scale(Screen) Rectangle { - width: 36 * Theme.uiScale; height: 36 * Theme.uiScale - radius: 18 * Theme.uiScale + width: 36 * Theme.scale(Screen); height: 36 * Theme.scale(Screen) + radius: 18 * Theme.scale(Screen) border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Performance) ? Theme.accentPrimary : (perfMouseArea.containsMouse ? Theme.accentPrimary : "transparent") @@ -33,7 +33,7 @@ Rectangle { anchors.centerIn: parent text: "speed" font.family: "Material Symbols Outlined" - font.pixelSize: 22 * Theme.uiScale + font.pixelSize: 22 * Theme.scale(Screen) color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Performance) || perfMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary @@ -65,10 +65,10 @@ Rectangle { Rectangle { - width: 36 * Theme.uiScale; height: 36 * Theme.uiScale - radius: 18 * Theme.uiScale + width: 36 * Theme.scale(Screen); height: 36 * Theme.scale(Screen) + radius: 18 * Theme.scale(Screen) border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Balanced) ? Theme.accentPrimary : (balMouseArea.containsMouse ? Theme.accentPrimary : "transparent") @@ -79,7 +79,7 @@ Rectangle { anchors.centerIn: parent text: "balance" font.family: "Material Symbols Outlined" - font.pixelSize: 22 * Theme.uiScale + font.pixelSize: 22 * Theme.scale(Screen) color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.Balanced) || balMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary @@ -111,10 +111,10 @@ Rectangle { Rectangle { - width: 36 * Theme.uiScale; height: 36 * Theme.uiScale - radius: 18 * Theme.uiScale + width: 36 * Theme.scale(Screen); height: 36 * Theme.scale(Screen) + radius: 18 * Theme.scale(Screen) border.color: Theme.accentPrimary - border.width: 1 * Theme.uiScale + border.width: 1 * Theme.scale(Screen) color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.PowerSaver) ? Theme.accentPrimary : (saveMouseArea.containsMouse ? Theme.accentPrimary : "transparent") @@ -125,7 +125,7 @@ Rectangle { anchors.centerIn: parent text: "eco" font.family: "Material Symbols Outlined" - font.pixelSize: 22 * Theme.uiScale + font.pixelSize: 22 * Theme.scale(Screen) color: (typeof PowerProfiles !== 'undefined' && PowerProfiles.profile === PowerProfile.PowerSaver) || saveMouseArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary diff --git a/Widgets/SidePanel/SettingsIcon.qml b/Widgets/SidePanel/SettingsIcon.qml index dc12ac2..bdae248 100644 --- a/Widgets/SidePanel/SettingsIcon.qml +++ b/Widgets/SidePanel/SettingsIcon.qml @@ -10,8 +10,8 @@ import qs.Components PanelWindow { id: settingsModal - implicitWidth: 480 * Theme.uiScale - implicitHeight: 780 * Theme.uiScale + implicitWidth: 480 * Theme.scale(Screen) + implicitHeight: 780 * Theme.scale(Screen) visible: false color: "transparent" anchors.top: true diff --git a/Widgets/SidePanel/SettingsModal.qml b/Widgets/SidePanel/SettingsModal.qml index 792f76e..5a47ecf 100644 --- a/Widgets/SidePanel/SettingsModal.qml +++ b/Widgets/SidePanel/SettingsModal.qml @@ -10,8 +10,8 @@ import qs.Components PanelWindow { id: settingsModal - implicitWidth: 480 * Theme.uiScale - implicitHeight: 780 * Theme.uiScale + implicitWidth: 480 * Theme.scale(Screen) + implicitHeight: 780 * Theme.scale(Screen) visible: false color: "transparent" anchors.top: true diff --git a/Widgets/SidePanel/SystemMonitor.qml b/Widgets/SidePanel/SystemMonitor.qml index 59fc877..dd0464a 100644 --- a/Widgets/SidePanel/SystemMonitor.qml +++ b/Widgets/SidePanel/SystemMonitor.qml @@ -8,8 +8,8 @@ import qs.Settings Rectangle { id: systemMonitor - width: 70 * Theme.uiScale - height: 250 * Theme.uiScale + width: 70 * Theme.scale(Screen) + height: 250 * Theme.scale(Screen) color: "transparent" // Track visibility state for panel integration @@ -19,26 +19,26 @@ Rectangle { id: card anchors.fill: parent color: Theme.surface - radius: 18 * Theme.uiScale + radius: 18 * Theme.scale(Screen) ColumnLayout { anchors.fill: parent - anchors.margins: 8 * Theme.uiScale - spacing: 12 * Theme.uiScale + anchors.margins: 8 * Theme.scale(Screen) + spacing: 12 * Theme.scale(Screen) Layout.alignment: Qt.AlignVCenter // CPU usage indicator with circular progress bar Item { - width: 50 * Theme.uiScale; height: 50 * Theme.uiScale + width: 50 * Theme.scale(Screen); height: 50 * Theme.scale(Screen) CircularProgressBar { id: cpuBar progress: Sysinfo.cpuUsage / 100 - size: 50 * Theme.uiScale - strokeWidth: 4 * Theme.uiScale + size: 50 * Theme.scale(Screen) + strokeWidth: 4 * Theme.scale(Screen) hasNotch: true notchIcon: "speed" - notchIconSize: 14 * Theme.uiScale + notchIconSize: 14 * Theme.scale(Screen) Layout.alignment: Qt.AlignHCenter } MouseArea { @@ -60,16 +60,16 @@ Rectangle { // CPU temperature indicator with circular progress bar Item { - width: 50 * Theme.uiScale; height: 50 * Theme.uiScale + width: 50 * Theme.scale(Screen); height: 50 * Theme.scale(Screen) CircularProgressBar { id: tempBar progress: Sysinfo.cpuTemp / 100 - size: 50 * Theme.uiScale - strokeWidth: 4 * Theme.uiScale + size: 50 * Theme.scale(Screen) + strokeWidth: 4 * Theme.scale(Screen) hasNotch: true units: "°C" notchIcon: "thermometer" - notchIconSize: 14 * Theme.uiScale + notchIconSize: 14 * Theme.scale(Screen) Layout.alignment: Qt.AlignHCenter } MouseArea { @@ -91,15 +91,15 @@ Rectangle { // Memory usage indicator with circular progress bar Item { - width: 50 * Theme.uiScale; height: 50 * Theme.uiScale + width: 50 * Theme.scale(Screen); height: 50 * Theme.scale(Screen) CircularProgressBar { id: memBar progress: Sysinfo.memoryUsagePer / 100 - size: 50 * Theme.uiScale - strokeWidth: 4 * Theme.uiScale + size: 50 * Theme.scale(Screen) + strokeWidth: 4 * Theme.scale(Screen) hasNotch: true notchIcon: "memory" - notchIconSize: 14 * Theme.uiScale + notchIconSize: 14 * Theme.scale(Screen) Layout.alignment: Qt.AlignHCenter } MouseArea { @@ -121,15 +121,15 @@ Rectangle { // Disk usage indicator with circular progress bar Item { - width: 50 * Theme.uiScale; height: 50 * Theme.uiScale + width: 50 * Theme.scale(Screen); height: 50 * Theme.scale(Screen) CircularProgressBar { id: diskBar progress: Sysinfo.diskUsage / 100 - size: 50 * Theme.uiScale - strokeWidth: 4 * Theme.uiScale + size: 50 * Theme.scale(Screen) + strokeWidth: 4 * Theme.scale(Screen) hasNotch: true notchIcon: "storage" - notchIconSize: 14 * Theme.uiScale + notchIconSize: 14 * Theme.scale(Screen) Layout.alignment: Qt.AlignHCenter } MouseArea { diff --git a/Widgets/SidePanel/Weather.qml b/Widgets/SidePanel/Weather.qml index 1c77192..3927e93 100644 --- a/Widgets/SidePanel/Weather.qml +++ b/Widgets/SidePanel/Weather.qml @@ -6,8 +6,8 @@ import "../../Helpers/Weather.js" as WeatherHelper Rectangle { id: weatherRoot - width: 440 * Theme.uiScale - height: 180 * Theme.uiScale + width: 440 * Theme.scale(Screen) + height: 180 * Theme.scale(Screen) color: "transparent" anchors.horizontalCenterOffset: -2 @@ -83,29 +83,29 @@ Rectangle { id: card anchors.fill: parent color: Theme.surface - radius: 18 * Theme.uiScale + radius: 18 * Theme.scale(Screen) ColumnLayout { anchors.fill: parent - anchors.margins: 18 * Theme.uiScale - spacing: 12 * Theme.uiScale + anchors.margins: 18 * Theme.scale(Screen) + spacing: 12 * Theme.scale(Screen) RowLayout { - spacing: 12 * Theme.uiScale + spacing: 12 * Theme.scale(Screen) Layout.fillWidth: true RowLayout { - spacing: 12 * Theme.uiScale - Layout.preferredWidth: 140 * Theme.uiScale + spacing: 12 * Theme.scale(Screen) + Layout.preferredWidth: 140 * Theme.scale(Screen) Text { id: weatherIcon text: isLoading ? "sync" : (weatherData && weatherData.current_weather ? materialSymbolForCode(weatherData.current_weather.weathercode) : "cloud") font.family: "Material Symbols Outlined" - font.pixelSize: 28 * Theme.uiScale + font.pixelSize: 28 * Theme.scale(Screen) verticalAlignment: Text.AlignVCenter color: isLoading ? Theme.accentPrimary : Theme.accentPrimary Layout.alignment: Qt.AlignVCenter @@ -121,28 +121,28 @@ Rectangle { } ColumnLayout { - spacing: 2 * Theme.uiScale + spacing: 2 * Theme.scale(Screen) RowLayout { - spacing: 4 * Theme.uiScale + spacing: 4 * Theme.scale(Screen) Text { text: city font.family: Theme.fontFamily - font.pixelSize: 14 * Theme.uiScale + font.pixelSize: 14 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } Text { text: weatherData && weatherData.timezone_abbreviation ? `(${weatherData.timezone_abbreviation})` : "" font.family: Theme.fontFamily - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) color: Theme.textSecondary - leftPadding: 2 * Theme.uiScale + leftPadding: 2 * Theme.scale(Screen) } } Text { 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 * Theme.uiScale + font.pixelSize: 24 * Theme.scale(Screen) font.bold: true color: Theme.textPrimary } @@ -157,16 +157,16 @@ Rectangle { Rectangle { width: parent.width - height: 1 * Theme.uiScale + height: 1 * Theme.scale(Screen) color: Qt.rgba(Theme.textSecondary.g, Theme.textSecondary.g, Theme.textSecondary.b, 0.12) Layout.fillWidth: true - Layout.topMargin: 2 * Theme.uiScale - Layout.bottomMargin: 2 * Theme.uiScale + Layout.topMargin: 2 * Theme.scale(Screen) + Layout.bottomMargin: 2 * Theme.scale(Screen) } RowLayout { - spacing: 12 * Theme.uiScale + spacing: 12 * Theme.scale(Screen) Layout.fillWidth: true Layout.alignment: Qt.AlignHCenter visible: weatherData && weatherData.daily && weatherData.daily.time @@ -174,13 +174,13 @@ Rectangle { Repeater { model: weatherData && weatherData.daily && weatherData.daily.time ? 5 : 0 delegate: ColumnLayout { - spacing: 2 * Theme.uiScale + spacing: 2 * Theme.scale(Screen) Layout.alignment: Qt.AlignHCenter Text { text: Qt.formatDateTime(new Date(weatherData.daily.time[index]), "ddd") font.family: Theme.fontFamily - font.pixelSize: 12 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textSecondary horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter @@ -189,7 +189,7 @@ Rectangle { text: materialSymbolForCode(weatherData.daily.weathercode[index]) font.family: "Material Symbols Outlined" - font.pixelSize: 22 * Theme.uiScale + font.pixelSize: 22 * Theme.scale(Screen) color: Theme.accentPrimary horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter @@ -198,7 +198,7 @@ Rectangle { 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 * Theme.uiScale + font.pixelSize: 12 * Theme.scale(Screen) color: Theme.textPrimary horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter @@ -213,7 +213,7 @@ Rectangle { color: Theme.error visible: errorString !== "" font.family: Theme.fontFamily - font.pixelSize: 10 * Theme.uiScale + font.pixelSize: 10 * Theme.scale(Screen) horizontalAlignment: Text.AlignHCenter Layout.alignment: Qt.AlignHCenter } From 4f4c22baf9706c7e84f16d98a27b971261b751f8 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 14:26:43 -0400 Subject: [PATCH 40/43] Corners: removed all but the screen corners - renamed Corners to Corner as it create only a single corner --- Bar/Bar.qml | 8 ++-- Bar/Modules/ActiveWindow.qml | 25 ------------ Bar/Modules/Applauncher.qml | 28 ------------- Components/{Corners.qml => Corner.qml} | 0 Widgets/LockScreen/LockScreen.qml | 29 ------------- Widgets/SettingsWindow/Tabs/General.qml | 2 +- Widgets/SidePanel/PanelPopup.qml | 54 +------------------------ 7 files changed, 6 insertions(+), 140 deletions(-) rename Components/{Corners.qml => Corner.qml} (100%) diff --git a/Bar/Bar.qml b/Bar/Bar.qml index 48c18cb..653f6e9 100644 --- a/Bar/Bar.qml +++ b/Bar/Bar.qml @@ -182,7 +182,7 @@ Scope { aboveWindows: false implicitHeight: 24 - Corners { + Corner { id: topLeftCorner position: "bottomleft" @@ -209,7 +209,7 @@ Scope { aboveWindows: false implicitHeight: 24 - Corners { + Corner { id: topRightCorner position: "bottomright" @@ -235,7 +235,7 @@ Scope { aboveWindows: false implicitHeight: 24 - Corners { + Corner { id: bottomLeftCorner position: "topleft" @@ -261,7 +261,7 @@ Scope { aboveWindows: false implicitHeight: 24 - Corners { + Corner{ id: bottomRightCorner position: "topright" diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index fca39d5..d13bca7 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -140,31 +140,6 @@ PanelWindow { maximumLineCount: 1 } - Loader { - active: Settings.settings.showCorners - anchors.top: parent.top - sourceComponent: Item { - Corners { - id: activeCornerRight - position: "bottomleft" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: parent.top - offsetX: activeWindowTitleContainer.width - 34 - offsetY: -1 - } - - Corners { - id: activeCornerLeft - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: parent.top - offsetX: 34 - offsetY: -1 - } - } - } } } diff --git a/Bar/Modules/Applauncher.qml b/Bar/Modules/Applauncher.qml index 174364b..8e58320 100644 --- a/Bar/Modules/Applauncher.qml +++ b/Bar/Modules/Applauncher.qml @@ -830,34 +830,6 @@ PanelWithOverlay { } } } - - Loader { - active: Settings.settings.showCorners - anchors.top: parent.top - anchors.left: parent.left - anchors.right: parent.right - sourceComponent: Item { - Corners { - id: launcherCornerRight - position: "bottomleft" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: parent.top - offsetX: 427 - offsetY: 0 - } - - Corners { - id: launcherCornerLeft - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: parent.top - offsetX: -427 - offsetY: 0 - } - } - } } } } \ No newline at end of file diff --git a/Components/Corners.qml b/Components/Corner.qml similarity index 100% rename from Components/Corners.qml rename to Components/Corner.qml diff --git a/Widgets/LockScreen/LockScreen.qml b/Widgets/LockScreen/LockScreen.qml index b6ab0a7..adf524c 100644 --- a/Widgets/LockScreen/LockScreen.qml +++ b/Widgets/LockScreen/LockScreen.qml @@ -294,35 +294,6 @@ WlSessionLock { } } - Loader { - active: Settings.settings.showCorners - anchors.fill: parent - - sourceComponent: Item { - Corners { - id: topRightCorner - position: "bottomleft" - size: 1.3 - fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" - offsetX: screen.width / 2 + 53 - offsetY: 0 - anchors.top: parent.top - z: 50 - } - - Corners { - id: topLeftCorner - position: "bottomright" - size: 1.3 - fillColor: (Theme.backgroundPrimary !== undefined && Theme.backgroundPrimary !== null) ? Theme.backgroundPrimary : "#222" - offsetX: -Screen.width / 2 - 53 - offsetY: 0 - anchors.top: parent.top - z: 51 - } - } - } - Rectangle { width: infoColumn.width + 32 * Theme.scale(Screen) height: infoColumn.height + 8 * Theme.scale(Screen) diff --git a/Widgets/SettingsWindow/Tabs/General.qml b/Widgets/SettingsWindow/Tabs/General.qml index 5cbc059..5600558 100644 --- a/Widgets/SettingsWindow/Tabs/General.qml +++ b/Widgets/SettingsWindow/Tabs/General.qml @@ -134,7 +134,7 @@ ColumnLayout { ToggleOption { label: "Show Corners" - description: "Display rounded corners" + description: "Display rounded corners on the edge of the screen" value: Settings.settings.showCorners onToggled: function() { Settings.settings.showCorners = !Settings.settings.showCorners; diff --git a/Widgets/SidePanel/PanelPopup.qml b/Widgets/SidePanel/PanelPopup.qml index a3dbf86..af2cc3a 100644 --- a/Widgets/SidePanel/PanelPopup.qml +++ b/Widgets/SidePanel/PanelPopup.qml @@ -382,59 +382,7 @@ PanelWithOverlay { } - Loader { - active: Settings.settings.showCorners - anchors.fill: parent - - sourceComponent: Item { - Corners { - id: sidebarCornerLeft - - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.top: parent.top - offsetX: -464 + sidebarPopupRect.slideOffset - offsetY: 0 - - Behavior on offsetX { - enabled: !sidebarPopupRect.isAnimating - - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic - } - - } - - } - - Corners { - id: sidebarCornerBottom - - position: "bottomright" - size: 1.1 - fillColor: Theme.backgroundPrimary - anchors.bottom: sidebarPopupRect.bottom - offsetX: 33 + sidebarPopupRect.slideOffset - offsetY: 46 - - Behavior on offsetX { - enabled: !sidebarPopupRect.isAnimating - - NumberAnimation { - duration: 300 - easing.type: Easing.OutCubic - } - - } - - } - - } - - } - + } } From a465872e0d0f095658e3a046e3da3f7061b220af Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 14:41:14 -0400 Subject: [PATCH 41/43] ActiveWindow: added more breathing room and a little oultine --- Bar/Modules/ActiveWindow.qml | 80 +++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index d13bca7..fa7b210 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -101,46 +101,62 @@ PanelWindow { Rectangle { id: activeWindowTitleContainer - color: Theme.backgroundPrimary - bottomLeftRadius: Math.max(0, width / 2) - bottomRightRadius: Math.max(0, width / 2) + color: "transparent" - width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + (Settings.settings.showActiveWindowIcon ? 28 : 22)) + + width: Math.min(barBackground.width - 200, activeWindowTitle.implicitWidth + (Settings.settings.showActiveWindowIcon ? 28 : 22)) + 16 height: activeWindowTitle.implicitHeight + 12 anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter - IconImage { - id: icon - width: 12 - height: 12 - anchors.left: parent.left - anchors.leftMargin: 6 - anchors.verticalCenter: parent.verticalCenter - source: ToplevelManager?.activeToplevel ? getIcon() : "" - visible: Settings.settings.showActiveWindowIcon - anchors.verticalCenterOffset: -3 + Rectangle { + id: innerRect + bottomLeftRadius: Math.max(0, width / 2) + bottomRightRadius: Math.max(0, width / 2) + color: Theme.backgroundSecondary + anchors { + fill: parent + leftMargin: 0 + rightMargin: 0 + topMargin: -1 + bottomMargin: 0 + } + + + border.color: Theme.outline || "#444"; + border.width: 1; + + IconImage { + id: icon + width: 12 + height: 12 + anchors.left: parent.left + anchors.leftMargin: 14 + anchors.verticalCenter: parent.verticalCenter + source: ToplevelManager?.activeToplevel ? getIcon() : "" + visible: Settings.settings.showActiveWindowIcon + anchors.verticalCenterOffset: -3 + + } + + Text { + id: activeWindowTitle + text: ToplevelManager?.activeToplevel?.title && ToplevelManager?.activeToplevel?.title.length > 60 ? ToplevelManager?.activeToplevel?.title.substring(0, 60) + "..." : ToplevelManager?.activeToplevel?.title || "" + font.pixelSize: 12 * Theme.scale(Screen) + color: Theme.textSecondary + anchors.left: icon.right + anchors.leftMargin: Settings.settings.showActiveWindowIcon ? 4 : 6 + anchors.right: parent.right + anchors.rightMargin: 14 + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: -3 + horizontalAlignment: Settings.settings.showActiveWindowIcon ? Text.AlignRight : Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + maximumLineCount: 1 + } } - - Text { - id: activeWindowTitle - text: ToplevelManager?.activeToplevel?.title && ToplevelManager?.activeToplevel?.title.length > 60 ? ToplevelManager?.activeToplevel?.title.substring(0, 60) + "..." : ToplevelManager?.activeToplevel?.title || "" - font.pixelSize: 12 * Theme.scale(Screen) - color: Theme.textSecondary - anchors.left: icon.right - anchors.leftMargin: Settings.settings.showActiveWindowIcon ? 4 : 6 - anchors.right: parent.right - anchors.rightMargin: 6 - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -3 - horizontalAlignment: Settings.settings.showActiveWindowIcon ? Text.AlignRight : Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - maximumLineCount: 1 - } - } - } } From 7844cc297582b7c657c6e07bfea33a87a43f7a14 Mon Sep 17 00:00:00 2001 From: quadbyte Date: Thu, 7 Aug 2025 14:58:36 -0400 Subject: [PATCH 42/43] Active window tweaks --- Bar/Modules/ActiveWindow.qml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Bar/Modules/ActiveWindow.qml b/Bar/Modules/ActiveWindow.qml index fa7b210..67c2f8a 100644 --- a/Bar/Modules/ActiveWindow.qml +++ b/Bar/Modules/ActiveWindow.qml @@ -7,6 +7,10 @@ import qs.Settings PanelWindow { id: activeWindowPanel + + // Lower case "screen" from modelData + property int barHeight: 36 * Theme.scale(screen) + screen: (typeof modelData !== 'undefined' ? modelData : null) WlrLayershell.exclusionMode: ExclusionMode.Ignore anchors.top: true @@ -17,7 +21,6 @@ PanelWindow { visible: Settings.settings.showActiveWindow && !activeWindowWrapper.finallyHidden implicitHeight: activeWindowTitleContainer.height implicitWidth: 0 - property int barHeight: 36 * Theme.scale(Screen) color: "transparent" function getIcon() { @@ -44,7 +47,7 @@ PanelWindow { Timer { id: visibilityTimer - interval: 1200 + interval: 1500 running: false onTriggered: { activeWindowWrapper.shouldShow = false; @@ -111,11 +114,11 @@ PanelWindow { anchors.horizontalCenter: parent.horizontalCenter Rectangle { - id: innerRect + id: innerRect bottomLeftRadius: Math.max(0, width / 2) bottomRightRadius: Math.max(0, width / 2) - color: Theme.backgroundSecondary + color: Theme.backgroundPrimary anchors { fill: parent leftMargin: 0 From 8a6b842ed03310080f8853bd36d9c5f8cf16ffa4 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Thu, 7 Aug 2025 21:21:03 +0200 Subject: [PATCH 43/43] Add close logic for WallpaperSelector --- Widgets/SettingsWindow/SettingsWindow.qml | 9 +++++++- .../Tabs/Components/WallpaperSelector.qml | 21 +++++++++++++++++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/Widgets/SettingsWindow/SettingsWindow.qml b/Widgets/SettingsWindow/SettingsWindow.qml index 593c386..d57e5e2 100644 --- a/Widgets/SettingsWindow/SettingsWindow.qml +++ b/Widgets/SettingsWindow/SettingsWindow.qml @@ -294,7 +294,14 @@ PanelWithOverlay { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onClicked: panelMain.dismiss() + onClicked: { + // If wallpaper selector is open, close it instead of the settings window + if (wallpaperSelector.visible) { + wallpaperSelector.hide(); + } else { + panelMain.dismiss(); + } + } } } diff --git a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml index ab28d4e..5d2722d 100644 --- a/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml +++ b/Widgets/SettingsWindow/Tabs/Components/WallpaperSelector.qml @@ -8,23 +8,38 @@ import qs.Settings Rectangle { id: wallpaperOverlay + focus: true // Function to show the overlay and load wallpapers function show() { // Ensure wallpapers are loaded WallpaperManager.loadWallpapers(); wallpaperOverlay.visible = true; + wallpaperOverlay.forceActiveFocus(); + } + + // Function to hide the overlay + function hide() { + wallpaperOverlay.visible = false; } color: Theme.backgroundPrimary visible: false z: 1000 + // Handle escape key to close + Keys.onPressed: function(event) { + if (event.key === Qt.Key_Escape) { + wallpaperOverlay.hide(); + event.accepted = true; + } + } + // Click outside to close MouseArea { anchors.fill: parent onClicked: { - wallpaperOverlay.visible = false; + wallpaperOverlay.hide(); } } @@ -41,6 +56,8 @@ Rectangle { anchors.fill: parent spacing: 0 + + // Wallpaper Grid Item { Layout.fillWidth: true @@ -133,7 +150,7 @@ Rectangle { cursorShape: Qt.PointingHandCursor onClicked: { WallpaperManager.changeWallpaper(modelData); - wallpaperOverlay.visible = false; + wallpaperOverlay.hide(); } }