Add WallpaperSelector, add some scrolling in SettingsWindow

This commit is contained in:
Ly-sec 2025-08-12 17:26:23 +02:00
parent eff47f504f
commit 8155ef20eb
14 changed files with 1451 additions and 674 deletions

View file

@ -8,9 +8,18 @@ Variants {
delegate: PanelWindow { delegate: PanelWindow {
required property ShellScreen modelData required property ShellScreen modelData
property string wallpaperSource: Settings.data.wallpaper.current property string wallpaperSource: Wallpapers.currentWallpaper !== "" && !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : ""
visible: wallpaperSource !== "" visible: wallpaperSource !== "" && !Settings.data.wallpaper.swww.enabled
// Force update when SWWW setting changes
onVisibleChanged: {
if (visible) {
console.log("Background: Showing wallpaper:", wallpaperSource)
} else {
console.log("Background: Hiding wallpaper (SWWW enabled)")
}
}
color: "transparent" color: "transparent"
screen: modelData screen: modelData
WlrLayershell.layer: WlrLayer.Background WlrLayershell.layer: WlrLayer.Background

View file

@ -9,9 +9,9 @@ Variants {
delegate: PanelWindow { delegate: PanelWindow {
required property ShellScreen modelData required property ShellScreen modelData
property string wallpaperSource: Qt.resolvedUrl("../../Assets/Tests/wallpaper.png") property string wallpaperSource: Wallpapers.currentWallpaper !== "" && !Settings.data.wallpaper.swww.enabled ? Wallpapers.currentWallpaper : ""
visible: wallpaperSource !== "" visible: wallpaperSource !== "" && !Settings.data.wallpaper.swww.enabled
color: "transparent" color: "transparent"
screen: modelData screen: modelData
WlrLayershell.layer: WlrLayer.Background WlrLayershell.layer: WlrLayer.Background

View file

@ -46,7 +46,13 @@ NLoader {
"label": "Wallpaper", "label": "Wallpaper",
"icon": "image", "icon": "image",
"source": "Tabs/Wallpaper.qml" "source": "Tabs/Wallpaper.qml"
}, { },
{
"label": "Wallpaper Selector",
"icon": "wallpaper_slideshow",
"source": "Tabs/WallpaperSelector.qml"
},
{
"label": "Misc", "label": "Misc",
"icon": "more_horiz", "icon": "more_horiz",
"source": "Tabs/Misc.qml" "source": "Tabs/Misc.qml"
@ -207,6 +213,7 @@ NLoader {
Tabs.Network {} Tabs.Network {}
Tabs.Display {} Tabs.Display {}
Tabs.Wallpaper {} Tabs.Wallpaper {}
Tabs.WallpaperSelector {}
Tabs.Misc {} Tabs.Misc {}
Tabs.About {} Tabs.About {}
} }

View file

@ -1,75 +1,105 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { ColumnLayout {
// Optional scaling prop to match other tabs id: root
property real scaling: 1
// Tab metadata
readonly property string tabIcon: "web_asset"
readonly property string tabLabel: "Bar"
readonly property int tabIndex: 1
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout { spacing: 0
anchors.fill: parent
spacing: Style.marginMedium * scaling
NText { ScrollView {
text: "Elements" id: scrollView
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NToggle { Layout.fillWidth: true
label: "Show Active Window" Layout.fillHeight: true
description: "Display the title of the currently focused window below the bar" padding: 16
value: Settings.data.bar.showActiveWindow rightPadding: 12
onToggled: function (newValue) { clip: true
Settings.data.bar.showActiveWindow = newValue ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ColumnLayout {
width: scrollView.availableWidth
spacing: 0
Item {
Layout.fillWidth: true
Layout.preferredHeight: 0
} }
}
NToggle { ColumnLayout {
label: "Show Active Window Icon" spacing: 4
description: "Display the icon of the currently focused window" Layout.fillWidth: true
value: Settings.data.bar.showActiveWindowIcon
onToggled: function (newValue) { NText {
Settings.data.bar.showActiveWindowIcon = newValue text: "Bar Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
// Elements section
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Elements"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NToggle {
label: "Show Active Window"
description: "Display the title of the currently focused window below the bar"
value: Settings.data.bar.showActiveWindow
onToggled: function (newValue) {
Settings.data.bar.showActiveWindow = newValue
}
}
NToggle {
label: "Show Active Window Icon"
description: "Display the icon of the currently focused window"
value: Settings.data.bar.showActiveWindowIcon
onToggled: function (newValue) {
Settings.data.bar.showActiveWindowIcon = newValue
}
}
NToggle {
label: "Show System Info"
description: "Display system information (CPU, RAM, Temperature)"
value: Settings.data.bar.showSystemInfo
onToggled: function (newValue) {
Settings.data.bar.showSystemInfo = newValue
}
}
NToggle {
label: "Show Taskbar"
description: "Display a taskbar showing currently open windows"
value: Settings.data.bar.showTaskbar
onToggled: function (newValue) {
Settings.data.bar.showTaskbar = newValue
}
}
NToggle {
label: "Show Media"
description: "Display media controls and information"
value: Settings.data.bar.showMedia
onToggled: function (newValue) {
Settings.data.bar.showMedia = newValue
}
}
}
} }
} }
NToggle {
label: "Show System Info"
description: "Display system information (CPU, RAM, Temperature)"
value: Settings.data.bar.showSystemInfo
onToggled: function (newValue) {
Settings.data.bar.showSystemInfo = newValue
}
}
NToggle {
label: "Show Taskbar"
description: "Display a taskbar showing currently open windows"
value: Settings.data.bar.showTaskbar
onToggled: function (newValue) {
Settings.data.bar.showTaskbar = newValue
}
}
NToggle {
label: "Show Media"
description: "Display media controls and information"
value: Settings.data.bar.showMedia
onToggled: function (newValue) {
Settings.data.bar.showMedia = newValue
}
}
Item {
Layout.fillHeight: true
}
} }
} }

View file

@ -116,4 +116,4 @@ Item {
Layout.fillHeight: true Layout.fillHeight: true
} }
} }
} }

View file

@ -1,113 +1,143 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { ColumnLayout {
id: generalPage id: root
// Public API spacing: 0
// Scaling factor provided by the parent settings window
property real scaling: 1
// Tab metadata
readonly property string tabIcon: "tune"
readonly property string tabLabel: "General"
readonly property int tabIndex: 0
Layout.fillWidth: true ScrollView {
Layout.fillHeight: true id: scrollView
ColumnLayout { Layout.fillWidth: true
anchors.fill: parent Layout.fillHeight: true
anchors.margins: 0 padding: 16
spacing: Style.marginMedium * scaling rightPadding: 12
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
// Profile section ColumnLayout {
NText { width: scrollView.availableWidth
text: "Profile" spacing: 0
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
RowLayout { Item {
Layout.fillWidth: true
spacing: Style.marginMedium * scaling
// Avatar preview
NImageRounded {
width: 64 * scaling
height: 64 * scaling
imagePath: Settings.data.general.avatarImage
fallbackIcon: "person"
borderColor: Colors.accentPrimary
borderWidth: Math.max(1, Style.borderMedium * scaling)
}
ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: 2 * scaling Layout.preferredHeight: 0
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText { NText {
text: "Profile Image" text: "General Settings"
color: Colors.textPrimary font.pointSize: 18
font.weight: Style.fontWeightBold font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
} }
NText {
text: "Your profile picture displayed in various places throughout the shell" // Profile section
color: Colors.textSecondary ColumnLayout {
font.pointSize: Style.fontSizeSmall * scaling spacing: 8
}
NTextInput {
text: Settings.data.general.avatarImage
placeholderText: "/home/user/.face"
Layout.fillWidth: true Layout.fillWidth: true
onEditingFinished: function () { Layout.topMargin: 8
Settings.data.general.avatarImage = text
NText {
text: "Profile"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
RowLayout {
Layout.fillWidth: true
spacing: 16
// Avatar preview
NImageRounded {
width: 64
height: 64
imagePath: Settings.data.general.avatarImage
fallbackIcon: "person"
borderColor: Colors.accentPrimary
borderWidth: Math.max(1, Style.borderMedium)
}
ColumnLayout {
Layout.fillWidth: true
spacing: 4
NText {
text: "Profile Image"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Your profile picture displayed in various places throughout the shell"
color: Colors.textSecondary
font.pointSize: 12
}
NTextInput {
text: Settings.data.general.avatarImage
placeholderText: "/home/user/.face"
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.general.avatarImage = text
}
}
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "User Interface"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
NToggle {
label: "Show Corners"
description: "Display rounded corners on the edge of the screen"
value: Settings.data.general.showScreenCorners
onToggled: function (v) {
Settings.data.general.showScreenCorners = v
}
}
NToggle {
label: "Show Dock"
description: "Display a dock at the bottom of the screen for quick access to applications"
value: Settings.data.general.showDock
onToggled: function (v) {
Settings.data.general.showDock = v
}
}
NToggle {
label: "Dim Desktop"
description: "Dim the desktop when panels or menus are open"
value: Settings.data.general.dimDesktop
onToggled: function (v) {
Settings.data.general.dimDesktop = v
} }
} }
} }
} }
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginSmall * scaling
Layout.bottomMargin: Style.marginSmall * scaling
}
// UI section
NText {
text: "User Interface"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NToggle {
label: "Show Corners"
description: "Display rounded corners on the edge of the screen"
value: Settings.data.general.showScreenCorners
onToggled: function (v) {
Settings.data.general.showScreenCorners = v
}
}
NToggle {
label: "Show Dock"
description: "Display a dock at the bottom of the screen for quick access to applications"
value: Settings.data.general.showDock
onToggled: function (v) {
Settings.data.general.showDock = v
}
}
NToggle {
label: "Dim Desktop"
description: "Dim the desktop when panels or menus are open"
value: Settings.data.general.dimDesktop
onToggled: function (v) {
Settings.data.general.dimDesktop = v
}
}
Item {
Layout.fillHeight: true
}
} }
} }

View file

@ -1,48 +1,69 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { ColumnLayout {
property real scaling: 1 id: root
readonly property string tabIcon: "more_horiz"
readonly property string tabLabel: "Misc"
readonly property int tabIndex: 7
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout { spacing: 0
anchors.fill: parent
spacing: Style.marginMedium * scaling
NText { ScrollView {
text: "Media" id: scrollView
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NText { Layout.fillWidth: true
text: "Visualizer Type" Layout.fillHeight: true
color: Colors.textPrimary padding: 16
font.weight: Style.fontWeightBold rightPadding: 12
} clip: true
NText { ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
text: "Choose the style of the audio visualizer" ScrollBar.vertical.policy: ScrollBar.AsNeeded
color: Colors.textSecondary
}
NComboBox { ColumnLayout {
id: visualizerTypeComboBox width: scrollView.availableWidth
optionsKeys: ["radial", "fire", "diamond"] spacing: 0
optionsLabels: ["Radial", "Fire", "Diamond"]
currentKey: Settings.data.audioVisualizer.type Item {
onSelected: function (key) { Layout.fillWidth: true
Settings.data.audioVisualizer.type = key Layout.preferredHeight: 0
} }
}
Item { ColumnLayout {
Layout.fillHeight: true spacing: 4
Layout.fillWidth: true
NText {
text: "Miscellaneous Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
// Audio Visualizer section
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Audio Visualizer"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NComboBox {
optionsKeys: ["radial", "bars", "wave"]
optionsLabels: ["Radial", "Bars", "Wave"]
currentKey: Settings.data.audioVisualizer.type
onSelected: function (key) {
Settings.data.audioVisualizer.type = key
}
}
}
}
} }
} }
} }

View file

@ -1,64 +1,80 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Quickshell import Quickshell
import Quickshell.Bluetooth import Quickshell.Bluetooth
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { ColumnLayout {
property real scaling: 1 id: root
readonly property string tabIcon: "wifi"
readonly property string tabLabel: "Network"
readonly property int tabIndex: 4
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout { spacing: 0
anchors.fill: parent
spacing: Style.marginMedium * scaling
NText { ScrollView {
text: "WiFi" id: scrollView
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NToggle { Layout.fillWidth: true
label: "Enable WiFi" Layout.fillHeight: true
description: "Turn WiFi radio on or off" padding: 16
value: Settings.data.network.wifiEnabled rightPadding: 12
onToggled: function (newValue) { clip: true
Settings.data.network.wifiEnabled = newValue ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
Quickshell.execDetached(["nmcli", "radio", "wifi", newValue ? "on" : "off"]) ScrollBar.vertical.policy: ScrollBar.AsNeeded
ColumnLayout {
width: scrollView.availableWidth
spacing: 0
Item {
Layout.fillWidth: true
Layout.preferredHeight: 0
} }
}
NDivider { ColumnLayout {
Layout.fillWidth: true spacing: 4
} Layout.fillWidth: true
NText { NText {
text: "Bluetooth" text: "Network Settings"
font.weight: Style.fontWeightBold font.pointSize: 18
color: Colors.accentSecondary font.weight: Style.fontWeightBold
} color: Colors.textPrimary
Layout.bottomMargin: 8
}
NToggle { // Network interfaces section
label: "Enable Bluetooth" ColumnLayout {
description: "Turn Bluetooth radio on or off" spacing: 8
value: Settings.data.network.bluetoothEnabled Layout.fillWidth: true
onToggled: function (newValue) { Layout.topMargin: 8
Settings.data.network.bluetoothEnabled = newValue
if (Bluetooth.defaultAdapter) { NText {
Bluetooth.defaultAdapter.enabled = newValue text: "Network Interfaces"
if (Bluetooth.defaultAdapter.enabled) font.pointSize: 13
Bluetooth.defaultAdapter.discovering = true font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NToggle {
label: "WiFi Enabled"
description: "Enable WiFi connectivity"
value: Settings.data.network.wifiEnabled
onToggled: function (newValue) {
Settings.data.network.wifiEnabled = newValue
}
}
NToggle {
label: "Bluetooth Enabled"
description: "Enable Bluetooth connectivity"
value: Settings.data.network.bluetoothEnabled
onToggled: function (newValue) {
Settings.data.network.bluetoothEnabled = newValue
}
}
} }
} }
} }
Item {
Layout.fillHeight: true
}
} }
} }

View file

@ -1,180 +1,327 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { ColumnLayout {
property real scaling: 1 id: root
readonly property string tabIcon: "videocam"
readonly property string tabLabel: "Screen Recorder"
readonly property int tabIndex: 3
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout { spacing: 0
anchors.fill: parent
spacing: Style.marginMedium * scaling
NText { ScrollView {
text: "Screen Recording" id: scrollView
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
// Output Directory Layout.fillWidth: true
NText { Layout.fillHeight: true
text: "Output Directory" padding: 16
color: Colors.textPrimary rightPadding: 12
font.weight: Style.fontWeightBold clip: true
} ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
NText { ScrollBar.vertical.policy: ScrollBar.AsNeeded
text: "Directory where screen recordings will be saved"
color: Colors.textSecondary ColumnLayout {
} width: scrollView.availableWidth
NTextInput { spacing: 0
text: Settings.data.screenRecorder.directory
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.screenRecorder.directory = text
}
}
// Frame Rate
NText {
text: "Frame Rate"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Target frame rate for screen recordings (default: 60)"
color: Colors.textSecondary
}
RowLayout {
Layout.fillWidth: true
NText {
text: Settings.data.screenRecorder.frameRate + " FPS"
color: Colors.textPrimary
}
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 0
} }
}
NSlider {
Layout.fillWidth: true
from: 24
to: 144
stepSize: 1
value: Settings.data.screenRecorder.frameRate
onPressedChanged: Settings.data.screenRecorder.frameRate = Math.round(value)
cutoutColor: Colors.surface
}
// Audio Source ColumnLayout {
NText { spacing: 4
text: "Audio Source" Layout.fillWidth: true
color: Colors.textPrimary
font.weight: Style.fontWeightBold NText {
} text: "Screen Recording"
NText { font.pointSize: 18
text: "Audio source to capture during recording" font.weight: Style.fontWeightBold
color: Colors.textSecondary color: Colors.textPrimary
} Layout.bottomMargin: 8
NComboBox { }
optionsKeys: ["default_output", "default_input", "both"]
optionsLabels: ["System Audio", "Microphone", "System Audio + Microphone"] // Output Directory
currentKey: Settings.data.screenRecorder.audioSource ColumnLayout {
onSelected: function (key) { spacing: 8
Settings.data.screenRecorder.audioSource = key Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Output Directory"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Directory where screen recordings will be saved"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NTextInput {
text: Settings.data.screenRecorder.directory
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.screenRecorder.directory = text
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
// Video Settings
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "Video Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
// Frame Rate
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Frame Rate"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Target frame rate for screen recordings (default: 60)"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
NText {
text: Settings.data.screenRecorder.frameRate + " FPS"
font.pointSize: 13
color: Colors.textPrimary
}
Item {
Layout.fillWidth: true
}
}
NSlider {
Layout.fillWidth: true
from: 24
to: 144
stepSize: 1
value: Settings.data.screenRecorder.frameRate
onMoved: Settings.data.screenRecorder.frameRate = Math.round(value)
cutoutColor: Colors.backgroundPrimary
}
}
// Video Quality
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Video Quality"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Higher quality results in larger file sizes"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NComboBox {
optionsKeys: ["medium", "high", "very_high", "ultra"]
optionsLabels: ["Medium", "High", "Very High", "Ultra"]
currentKey: Settings.data.screenRecorder.quality
onSelected: function (key) {
Settings.data.screenRecorder.quality = key
}
}
}
// Video Codec
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Video Codec"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Different codecs offer different compression and compatibility"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NComboBox {
optionsKeys: ["h264", "hevc", "av1", "vp8", "vp9"]
optionsLabels: ["H264", "HEVC", "AV1", "VP8", "VP9"]
currentKey: Settings.data.screenRecorder.videoCodec
onSelected: function (key) {
Settings.data.screenRecorder.videoCodec = key
}
}
}
// Color Range
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Color Range"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Limited is recommended for better compatibility"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NComboBox {
optionsKeys: ["limited", "full"]
optionsLabels: ["Limited", "Full"]
currentKey: Settings.data.screenRecorder.colorRange
onSelected: function (key) {
Settings.data.screenRecorder.colorRange = key
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
// Audio Settings
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "Audio Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
// Audio Source
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Audio Source"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Audio source to capture during recording"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NComboBox {
optionsKeys: ["default_output", "default_input", "both"]
optionsLabels: ["System Audio", "Microphone", "System Audio + Microphone"]
currentKey: Settings.data.screenRecorder.audioSource
onSelected: function (key) {
Settings.data.screenRecorder.audioSource = key
}
}
}
// Audio Codec
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Audio Codec"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Opus is recommended for best performance and smallest audio size"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NComboBox {
optionsKeys: ["opus", "aac"]
optionsLabels: ["OPUS", "AAC"]
currentKey: Settings.data.screenRecorder.audioCodec
onSelected: function (key) {
Settings.data.screenRecorder.audioCodec = key
}
}
}
// Show Cursor
NToggle {
label: "Show Cursor"
description: "Record mouse cursor in the video"
value: Settings.data.screenRecorder.showCursor
onToggled: function (newValue) {
Settings.data.screenRecorder.showCursor = newValue
}
}
}
} }
} }
// Video Quality
NText {
text: "Video Quality"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Higher quality results in larger file sizes"
color: Colors.textSecondary
}
NComboBox {
optionsKeys: ["medium", "high", "very_high", "ultra"]
optionsLabels: ["Medium", "High", "Very High", "Ultra"]
currentKey: Settings.data.screenRecorder.quality
onSelected: function (key) {
Settings.data.screenRecorder.quality = key
}
}
// Video Codec
NText {
text: "Video Codec"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Different codecs offer different compression and compatibility"
color: Colors.textSecondary
}
NComboBox {
optionsKeys: ["h264", "hevc", "av1", "vp8", "vp9"]
optionsLabels: ["H264", "HEVC", "AV1", "VP8", "VP9"]
currentKey: Settings.data.screenRecorder.videoCodec
onSelected: function (key) {
Settings.data.screenRecorder.videoCodec = key
}
}
// Audio Codec
NText {
text: "Audio Codec"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Opus is recommended for best performance and smallest audio size"
color: Colors.textSecondary
}
NComboBox {
optionsKeys: ["opus", "aac"]
optionsLabels: ["OPUS", "AAC"]
currentKey: Settings.data.screenRecorder.audioCodec
onSelected: function (key) {
Settings.data.screenRecorder.audioCodec = key
}
}
// Color Range
NText {
text: "Color Range"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Limited is recommended for better compatibility"
color: Colors.textSecondary
}
NComboBox {
optionsKeys: ["limited", "full"]
optionsLabels: ["Limited", "Full"]
currentKey: Settings.data.screenRecorder.colorRange
onSelected: function (key) {
Settings.data.screenRecorder.colorRange = key
}
}
NToggle {
label: "Show Cursor"
description: "Record mouse cursor in the video"
value: Settings.data.screenRecorder.showCursor
onToggled: function (newValue) {
Settings.data.screenRecorder.showCursor = newValue
}
}
Item {
Layout.fillHeight: true
}
} }
} }

View file

@ -1,94 +1,136 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { ColumnLayout {
property real scaling: 1 id: root
readonly property string tabIcon: "schedule"
readonly property string tabLabel: "Time & Weather"
readonly property int tabIndex: 2
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout { spacing: 0
anchors.fill: parent
spacing: Style.marginMedium * scaling
NText { ScrollView {
text: "Time" id: scrollView
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NToggle { Layout.fillWidth: true
label: "Use 12 Hour Clock" Layout.fillHeight: true
description: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format" padding: 16
value: Settings.data.location.use12HourClock rightPadding: 12
onToggled: function (newValue) { clip: true
Settings.data.location.use12HourClock = newValue ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
ColumnLayout {
width: scrollView.availableWidth
spacing: 0
Item {
Layout.fillWidth: true
Layout.preferredHeight: 0
} }
}
NToggle { ColumnLayout {
label: "US Style Date" spacing: 4
description: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY" Layout.fillWidth: true
value: Settings.data.location.reverseDayMonth
onToggled: function (newValue) { NText {
Settings.data.location.reverseDayMonth = newValue text: "Time & Weather Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
// Location section
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Location"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NTextInput {
text: Settings.data.location.name
placeholderText: "Enter city name"
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.location.name = text
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
// Time section
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "Time Format"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
NToggle {
label: "Use 12-Hour Clock"
description: "Display time in 12-hour format (AM/PM) instead of 24-hour"
value: Settings.data.location.use12HourClock
onToggled: function (newValue) {
Settings.data.location.use12HourClock = newValue
}
}
NToggle {
label: "Reverse Day/Month"
description: "Display date as DD/MM instead of MM/DD"
value: Settings.data.location.reverseDayMonth
onToggled: function (newValue) {
Settings.data.location.reverseDayMonth = newValue
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
// Weather section
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "Weather"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
NToggle {
label: "Use Fahrenheit"
description: "Display temperature in Fahrenheit instead of Celsius"
value: Settings.data.location.useFahrenheit
onToggled: function (newValue) {
Settings.data.location.useFahrenheit = newValue
}
}
}
} }
} }
NDivider {
Layout.fillWidth: true
}
NText {
text: "Weather"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NText {
text: "Location"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Your city name for weather information"
color: Colors.textSecondary
font.pointSize: Style.fontSizeSmall * scaling
}
NTextInput {
text: Settings.data.location.name
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.location.name = text
Location.resetWeather()
}
}
NText {
text: "Temperature Unit"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Choose between Celsius and Fahrenheit"
color: Colors.textSecondary
font.pointSize: Style.fontSizeSmall * scaling
}
NComboBox {
optionsKeys: ["c", "f"]
optionsLabels: ["Celsius", "Fahrenheit"]
currentKey: Settings.data.location.useFahrenheit ? "f" : "c"
onSelected: function (key) {
Settings.data.location.useFahrenheit = (key === "f")
}
}
Item {
Layout.fillHeight: true
}
} }
} }

View file

@ -1,215 +1,357 @@
import QtQuick import QtQuick
import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import qs.Services import qs.Services
import qs.Widgets import qs.Widgets
Item { ColumnLayout {
property real scaling: 1 id: root
readonly property string tabIcon: "image"
readonly property string tabLabel: "Wallpaper"
readonly property int tabIndex: 6
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout { spacing: 0
anchors.fill: parent
spacing: Style.marginMedium * scaling
NText { ScrollView {
text: "Wallpaper Settings" id: scrollView
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
// Folder Layout.fillWidth: true
NText { Layout.fillHeight: true
text: "Wallpaper Folder" padding: 16
rightPadding: 12
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
font.weight: Style.fontWeightBold ColumnLayout {
} width: scrollView.availableWidth
NText { spacing: 0
text: "Path to your wallpaper folder"
color: Colors.textSecondary
wrapMode: Text.WordWrap
}
NTextInput {
text: Settings.data.wallpaper.directory
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.wallpaper.directory = text
}
}
NDivider {
Layout.fillWidth: true
}
// ----------------------------
NText {
text: "Automation"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NToggle {
label: "Random Wallpaper"
description: "Automatically select random wallpapers from the folder"
value: Settings.data.wallpaper.isRandom
onToggled: function (newValue) {
Settings.data.wallpaper.isRandom = newValue
}
}
NToggle {
label: "Use Wallpaper Theme"
description: "Automatically adjust theme colors based on wallpaper"
value: Settings.data.wallpaper.generateTheme
onToggled: function (newValue) {
Settings.data.wallpaper.generateTheme = newValue
}
}
NText {
text: "Wallpaper Interval"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "How often to change wallpapers automatically (in seconds)"
color: Colors.textSecondary
}
RowLayout {
Layout.fillWidth: true
NText {
text: Settings.data.wallpaper.randomInterval + " seconds"
color: Colors.textPrimary
}
Item { Item {
Layout.fillWidth: true Layout.fillWidth: true
} Layout.preferredHeight: 0
}
NSlider {
Layout.fillWidth: true
from: 10
to: 900
stepSize: 10
value: Settings.data.wallpaper.randomInterval
onPressedChanged: Settings.data.wallpaper.randomInterval = Math.round(value)
cutoutColor: Colors.backgroundPrimary
}
NDivider {
Layout.fillWidth: true
}
NText {
text: "SWWW"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NToggle {
label: "Use SWWW"
description: "Use SWWW daemon for advanced wallpaper management"
value: Settings.data.wallpaper.swww.enabled
onToggled: function (newValue) {
Settings.data.wallpaper.swww.enabled = newValue
}
}
// SWWW settings
ColumnLayout {
spacing: Style.marginSmall * scaling
visible: Settings.data.wallpaper.swww.enabled
NText {
text: "Resize Mode"
font.weight: Style.fontWeightBold
}
NText {
text: "How SWWW should resize wallpapers to fit the screen"
color: Colors.textSecondary
wrapMode: Text.WordWrap
}
NComboBox {
optionsKeys: ["no", "crop", "fit", "stretch"]
optionsLabels: ["No", "Crop", "Fit", "Stretch"]
currentKey: Settings.data.wallpaper.swww.resizeMethod
onSelected: function (key) {
Settings.data.wallpaper.swww.resizeMethod = key
}
} }
NText { ColumnLayout {
text: "Transition Type" spacing: 4
font.weight: Style.fontWeightBold
}
NText {
text: "Animation type when switching between wallpapers"
color: Colors.textSecondary
wrapMode: Text.WordWrap
}
NComboBox {
optionsKeys: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"]
optionsLabels: ["None", "Simple", "Fade", "Left", "Right", "Top", "Bottom", "Wipe", "Wave", "Grow", "Center", "Any", "Outer", "Random"]
currentKey: Settings.data.wallpaper.swww.transitionType
onSelected: function (key) {
Settings.data.wallpaper.swww.transitionType = key
}
}
NText {
text: "Transition FPS"
font.weight: Style.fontWeightBold
}
RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
NText { NText {
text: Settings.data.wallpaper.swww.transitionFps + " FPS" text: "Wallpaper Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary color: Colors.textPrimary
Layout.bottomMargin: 8
} }
Item {
// Wallpaper Settings Category
ColumnLayout {
spacing: 8
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 8
// Wallpaper Folder
ColumnLayout {
spacing: 8
Layout.fillWidth: true
NText {
text: "Wallpaper Folder"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Path to your wallpaper folder"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NTextInput {
text: Settings.data.wallpaper.directory
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.wallpaper.directory = text
}
}
}
} }
} }
NSlider {
Layout.fillWidth: true
from: 30
to: 500
stepSize: 5
value: Settings.data.wallpaper.swww.transitionFps
onPressedChanged: Settings.data.wallpaper.swww.transitionFps = Math.round(value)
cutoutColor: Colors.backgroundPrimary
}
NText { NDivider {
text: "Transition Duration"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText { NText {
text: Settings.data.wallpaper.swww.transitionDuration.toFixed(2) + " s" text: "Automation"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary color: Colors.textPrimary
Layout.bottomMargin: 8
} }
Item {
Layout.fillWidth: true
}
}
NSlider {
Layout.fillWidth: true
from: 0.25
to: 10
stepSize: 0.05
value: Settings.data.wallpaper.swww.transitionDuration
onPressedChanged: Settings.data.wallpaper.swww.transitionDuration = value
cutoutColor: Colors.backgroundPrimary
}
}
Item { // Random Wallpaper
Layout.fillHeight: true NToggle {
label: "Random Wallpaper"
description: "Automatically select random wallpapers from the folder"
value: Settings.data.wallpaper.isRandom
onToggled: function (newValue) {
Settings.data.wallpaper.isRandom = newValue
}
}
// Use Wallpaper Theme
NToggle {
label: "Use Wallpaper Theme"
description: "Automatically adjust theme colors based on wallpaper"
value: Settings.data.wallpaper.generateTheme
onToggled: function (newValue) {
Settings.data.wallpaper.generateTheme = newValue
}
}
// Wallpaper Interval
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Wallpaper Interval"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "How often to change wallpapers automatically (in seconds)"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
NText {
text: Settings.data.wallpaper.randomInterval + " seconds"
font.pointSize: 13
color: Colors.textPrimary
}
Item {
Layout.fillWidth: true
}
}
NSlider {
Layout.fillWidth: true
from: 10
to: 900
stepSize: 10
value: Settings.data.wallpaper.randomInterval
onMoved: Settings.data.wallpaper.randomInterval = Math.round(value)
cutoutColor: Colors.backgroundPrimary
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "SWWW"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
// Use SWWW
NToggle {
label: "Use SWWW"
description: "Use SWWW daemon for advanced wallpaper management"
value: Settings.data.wallpaper.swww.enabled
onToggled: function (newValue) {
Settings.data.wallpaper.swww.enabled = newValue
}
}
// SWWW Settings (only visible when useSWWW is enabled)
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
visible: Settings.data.wallpaper.swww.enabled
// Resize Mode
ColumnLayout {
spacing: 8
Layout.fillWidth: true
NText {
text: "Resize Mode"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "How SWWW should resize wallpapers to fit the screen"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NComboBox {
optionsKeys: ["no", "crop", "fit", "stretch"]
optionsLabels: ["No", "Crop", "Fit", "Stretch"]
currentKey: Settings.data.wallpaper.swww.resizeMethod
onSelected: function (key) {
Settings.data.wallpaper.swww.resizeMethod = key
}
}
}
// Transition Type
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Transition Type"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Animation type when switching between wallpapers"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
NComboBox {
optionsKeys: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"]
optionsLabels: ["None", "Simple", "Fade", "Left", "Right", "Top", "Bottom", "Wipe", "Wave", "Grow", "Center", "Any", "Outer", "Random"]
currentKey: Settings.data.wallpaper.swww.transitionType
onSelected: function (key) {
Settings.data.wallpaper.swww.transitionType = key
}
}
}
// Transition FPS
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Transition FPS"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Frames per second for transition animations"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
NText {
text: Settings.data.wallpaper.swww.transitionFps + " FPS"
font.pointSize: 13
color: Colors.textPrimary
}
Item {
Layout.fillWidth: true
}
}
NSlider {
Layout.fillWidth: true
from: 30
to: 500
stepSize: 5
value: Settings.data.wallpaper.swww.transitionFps
onMoved: Settings.data.wallpaper.swww.transitionFps = Math.round(value)
cutoutColor: Colors.backgroundPrimary
}
}
// Transition Duration
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Transition Duration"
font.pointSize: 13
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Duration of transition animations in seconds"
font.pointSize: 12
color: Colors.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
NText {
text: Settings.data.wallpaper.swww.transitionDuration.toFixed(3) + " seconds"
font.pointSize: 13
color: Colors.textPrimary
}
Item {
Layout.fillWidth: true
}
}
NSlider {
Layout.fillWidth: true
from: 0.25
to: 10
stepSize: 0.05
value: Settings.data.wallpaper.swww.transitionDuration
onMoved: Settings.data.wallpaper.swww.transitionDuration = value
cutoutColor: Colors.backgroundPrimary
}
}
}
}
} }
} }
} }

View file

@ -0,0 +1,270 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Qt.labs.folderlistmodel
import qs.Services
import qs.Widgets
Item {
property real scaling: 1
readonly property string tabIcon: "photo_library"
readonly property string tabLabel: "Wallpaper Selector"
readonly property int tabIndex: 7
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
anchors.fill: parent
spacing: Style.marginMedium * scaling
NText {
text: "Wallpaper Selector"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NText {
text: "Select a wallpaper from your configured directory"
color: Colors.textSecondary
wrapMode: Text.WordWrap
}
// Current wallpaper display
NText {
text: "Current Wallpaper"
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 120 * scaling
radius: Style.radiusMedium * scaling
color: Colors.backgroundSecondary
border.color: Colors.outline
border.width: Math.max(1, Style.borderThin * scaling)
clip: true
Image {
id: currentWallpaperImage
anchors.fill: parent
anchors.margins: Style.marginSmall * scaling
source: Wallpapers.currentWallpaper
fillMode: Image.PreserveAspectCrop
asynchronous: true
cache: true
}
// Fallback if no image
Rectangle {
anchors.fill: parent
anchors.margins: Style.marginSmall * scaling
color: Colors.backgroundTertiary
radius: Style.radiusSmall * scaling
visible: currentWallpaperImage.status !== Image.Ready
ColumnLayout {
anchors.centerIn: parent
spacing: Style.marginSmall * scaling
NText {
text: "image"
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeLarge * scaling
color: Colors.textSecondary
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "No wallpaper selected"
color: Colors.textSecondary
Layout.alignment: Qt.AlignHCenter
}
}
}
}
NDivider {
Layout.fillWidth: true
}
// Wallpaper grid
NText {
text: "Available Wallpapers"
font.weight: Style.fontWeightBold
color: Colors.textPrimary
}
NText {
text: "Click on a wallpaper to set it as your current wallpaper"
color: Colors.textSecondary
wrapMode: Text.WordWrap
}
NText {
text: Settings.data.wallpaper.swww.enabled ?
"Wallpapers will change with " + Settings.data.wallpaper.swww.transitionType + " transition" :
"Wallpapers will change instantly"
color: Colors.textSecondary
font.pointSize: Style.fontSizeSmall * scaling
visible: Settings.data.wallpaper.swww.enabled
}
// Refresh button and status
RowLayout {
Layout.fillWidth: true
spacing: Style.marginSmall * scaling
NIconButton {
icon: "refresh"
tooltipText: "Refresh wallpaper list"
onClicked: {
Wallpapers.loadWallpapers()
}
}
NText {
text: "Refresh"
color: Colors.textSecondary
}
}
// Wallpaper grid container
Item {
Layout.fillWidth: true
Layout.fillHeight: true
FolderListModel {
id: folderModel
folder: "file://" + (Settings.data.wallpaper.directory !== undefined ? Settings.data.wallpaper.directory : "")
nameFilters: ["*.jpg", "*.jpeg", "*.png", "*.gif", "*.pnm", "*.bmp"]
showDirs: false
sortField: FolderListModel.Name
}
GridView {
id: wallpaperGridView
anchors.fill: parent
clip: true
model: folderModel
// Fixed 5 items per row - more aggressive sizing
property int itemSize: Math.floor((width - leftMargin - rightMargin - (4 * Style.marginSmall * scaling)) / 5)
cellWidth: Math.floor((width - leftMargin - rightMargin) / 5)
cellHeight: Math.floor(itemSize * 0.67) + Style.marginSmall * scaling
leftMargin: Style.marginSmall * scaling
rightMargin: Style.marginSmall * scaling
topMargin: Style.marginSmall * scaling
bottomMargin: Style.marginSmall * scaling
delegate: Rectangle {
id: wallpaperItem
property string wallpaperPath: Settings.data.wallpaper.directory + "/" + fileName
property bool isSelected: wallpaperPath === Wallpapers.currentWallpaper
width: wallpaperGridView.itemSize
height: Math.floor(wallpaperGridView.itemSize * 0.67)
radius: Style.radiusMedium * scaling
color: isSelected ? Colors.accentPrimary : Colors.backgroundSecondary
border.color: isSelected ? Colors.accentSecondary : Colors.outline
border.width: Math.max(1, Style.borderThin * scaling)
clip: true
Image {
anchors.fill: parent
anchors.margins: Style.marginTiny * scaling
source: wallpaperPath
fillMode: Image.PreserveAspectCrop
asynchronous: true
cache: true
smooth: true
}
// Selection indicator
Rectangle {
anchors.top: parent.top
anchors.right: parent.right
anchors.margins: Style.marginTiny * scaling
width: 20 * scaling
height: 20 * scaling
radius: width / 2
color: Colors.accentPrimary
border.color: Colors.onAccent
border.width: Math.max(1, Style.borderThin * scaling)
visible: isSelected
NText {
anchors.centerIn: parent
text: "check"
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeSmall * scaling
color: Colors.onAccent
}
}
// Hover effect
Rectangle {
anchors.fill: parent
color: Colors.textPrimary
opacity: mouseArea.containsMouse ? 0.1 : 0
radius: parent.radius
Behavior on opacity {
NumberAnimation { duration: 150 }
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: {
Wallpapers.changeWallpaper(wallpaperPath)
}
}
}
}
// Empty state
Rectangle {
anchors.fill: parent
color: Colors.backgroundSecondary
radius: Style.radiusMedium * scaling
border.color: Colors.outline
border.width: Math.max(1, Style.borderThin * scaling)
visible: folderModel.count === 0 && !Wallpapers.scanning
ColumnLayout {
anchors.centerIn: parent
spacing: Style.marginMedium * scaling
NText {
text: "folder_open"
font.family: "Material Symbols Outlined"
font.pointSize: Style.fontSizeLarge * scaling
color: Colors.textSecondary
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "No wallpapers found"
color: Colors.textSecondary
font.weight: Style.fontWeightBold
Layout.alignment: Qt.AlignHCenter
}
NText {
text: "Make sure your wallpaper directory is configured and contains image files"
color: Colors.textSecondary
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignHCenter
Layout.preferredWidth: 300 * scaling
}
}
}
}
}
}

View file

@ -50,7 +50,14 @@ Singleton {
Component.onCompleted: function () { Component.onCompleted: function () {
reload() reload()
} }
onLoaded: function () {} onLoaded: function () {
Qt.callLater(function () {
if (adapter.wallpaper.current !== "") {
console.log("Settings: Initializing wallpaper to:", adapter.wallpaper.current)
Wallpapers.setCurrentWallpaper(adapter.wallpaper.current, true)
}
})
}
onLoadFailed: function (error) { onLoadFailed: function (error) {
if (error.toString().includes("No such file") || error === 2) if (error.toString().includes("No such file") || error === 2)
// File doesn't exist, create it with default values // File doesn't exist, create it with default values
@ -113,7 +120,7 @@ Singleton {
wallpaper: JsonObject { wallpaper: JsonObject {
property string directory: "/usr/share/wallpapers" property string directory: "/usr/share/wallpapers"
property string current: defaultWallpaper property string current: ""
property bool isRandom: false property bool isRandom: false
property int randomInterval: 300 property int randomInterval: 300
property bool generateTheme: false property bool generateTheme: false
@ -178,4 +185,11 @@ Singleton {
} }
} }
} }
Connections {
target: adapter.wallpaper
function onIsRandomChanged() { Wallpapers.toggleRandomWallpaper() }
function onRandomIntervalChanged() { Wallpapers.restartRandomWallpaperTimer() }
function onDirectoryChanged() { Wallpapers.loadWallpapers() }
}
} }

View file

@ -11,8 +11,13 @@ Singleton {
Item { Item {
Component.onCompleted: { Component.onCompleted: {
loadWallpapers() loadWallpapers()
setCurrentWallpaper(currentWallpaper, true) // Only set initial wallpaper if it's not empty
toggleRandomWallpaper() if (currentWallpaper !== "") {
console.log("Wallpapers: Initializing with wallpaper:", currentWallpaper)
setCurrentWallpaper(currentWallpaper, true)
}
// Don't start random wallpaper during initialization
// toggleRandomWallpaper()
} }
} }
@ -20,7 +25,7 @@ Singleton {
property string currentWallpaper: Settings.data.wallpaper.current property string currentWallpaper: Settings.data.wallpaper.current
property bool scanning: false property bool scanning: false
property string transitionType: Settings.data.wallpaper.swww.transitionType property string transitionType: Settings.data.wallpaper.swww.transitionType
property var randomChoices: ["fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer"] property var randomChoices: ["simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer"]
function loadWallpapers() { function loadWallpapers() {
scanning = true scanning = true
@ -29,10 +34,12 @@ Singleton {
} }
function changeWallpaper(path) { function changeWallpaper(path) {
setCurrentWallpaper(path) console.log("Wallpapers: changeWallpaper called with:", path)
setCurrentWallpaper(path, false)
} }
function setCurrentWallpaper(path, isInitial) { function setCurrentWallpaper(path, isInitial) {
console.log("Wallpapers: Setting wallpaper to:", path, "isInitial:", isInitial)
currentWallpaper = path currentWallpaper = path
if (!isInitial) { if (!isInitial) {
Settings.data.wallpaper.current = path Settings.data.wallpaper.current = path
@ -43,7 +50,11 @@ Singleton {
} else { } else {
transitionType = Settings.data.wallpaper.swww.transitionType transitionType = Settings.data.wallpaper.swww.transitionType
} }
console.log("SWWW: Changing wallpaper with transition type:", transitionType)
changeWallpaperProcess.running = true changeWallpaperProcess.running = true
} else {
// Fallback: update the settings directly for non-SWWW mode
console.log("Non-SWWW mode: Setting wallpaper directly")
} }
if (randomWallpaperTimer.running) { if (randomWallpaperTimer.running) {
@ -59,7 +70,7 @@ Singleton {
if (!randomPath) { if (!randomPath) {
return return
} }
setCurrentWallpaper(randomPath) setCurrentWallpaper(randomPath, false)
} }
function toggleRandomWallpaper() { function toggleRandomWallpaper() {
@ -84,6 +95,13 @@ Singleton {
} }
} }
function startSWWWDaemon() {
if (Settings.data.wallpaper.swww.enabled) {
console.log("SWWW: Attempting to start swww-daemon...")
startDaemonProcess.running = true
}
}
Timer { Timer {
id: randomWallpaperTimer id: randomWallpaperTimer
interval: Settings.data.wallpaper.randomInterval * 1000 interval: Settings.data.wallpaper.randomInterval * 1000
@ -120,6 +138,18 @@ Singleton {
), "--transition-type", transitionType, "--transition-duration", Settings.data.wallpaper.swww.transitionDuration.toString( ), "--transition-type", transitionType, "--transition-duration", Settings.data.wallpaper.swww.transitionDuration.toString(
), currentWallpaper] ), currentWallpaper]
running: false running: false
onStarted: {
console.log("SWWW: Process started with command:", command.join(" "))
}
onExited: function(exitCode, exitStatus) {
console.log("SWWW: Process finished with exit code:", exitCode, "status:", exitStatus)
if (exitCode !== 0) {
console.log("SWWW: Process failed. Make sure swww-daemon is running with: swww-daemon")
console.log("SWWW: You can start it with: swww-daemon --format xrgb")
}
}
} }
Process { Process {
@ -128,4 +158,23 @@ Singleton {
workingDirectory: Quickshell.shellDir workingDirectory: Quickshell.shellDir
running: false running: false
} }
Process {
id: startDaemonProcess
command: ["swww-daemon", "--format", "xrgb"]
running: false
onStarted: {
console.log("SWWW: Daemon start process initiated")
}
onExited: function(exitCode, exitStatus) {
console.log("SWWW: Daemon start process finished with exit code:", exitCode)
if (exitCode === 0) {
console.log("SWWW: Daemon started successfully")
} else {
console.log("SWWW: Failed to start daemon. It might already be running.")
}
}
}
} }