From 61e852ed517b37708f18e0e73a5a2f748b71c376 Mon Sep 17 00:00:00 2001 From: Ly-sec Date: Tue, 5 Aug 2025 22:17:47 +0200 Subject: [PATCH] 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 {