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 {
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"
screen: modelData
WlrLayershell.layer: WlrLayer.Background

View file

@ -9,9 +9,9 @@ Variants {
delegate: PanelWindow {
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"
screen: modelData
WlrLayershell.layer: WlrLayer.Background

View file

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

View file

@ -1,75 +1,105 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Services
import qs.Widgets
Item {
// Optional scaling prop to match other tabs
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 {
id: root
ColumnLayout {
anchors.fill: parent
spacing: Style.marginMedium * scaling
spacing: 0
NText {
text: "Elements"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
ScrollView {
id: scrollView
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
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
}
}
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
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
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
}
}
}
}

View file

@ -1,113 +1,143 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Services
import qs.Widgets
Item {
id: generalPage
ColumnLayout {
id: root
// Public API
// 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
spacing: 0
Layout.fillWidth: true
Layout.fillHeight: true
ScrollView {
id: scrollView
ColumnLayout {
anchors.fill: parent
anchors.margins: 0
spacing: Style.marginMedium * scaling
Layout.fillWidth: true
Layout.fillHeight: true
padding: 16
rightPadding: 12
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
// Profile section
NText {
text: "Profile"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
ColumnLayout {
width: scrollView.availableWidth
spacing: 0
RowLayout {
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 {
Item {
Layout.fillWidth: true
spacing: 2 * scaling
Layout.preferredHeight: 0
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "Profile Image"
color: Colors.textPrimary
text: "General Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
NText {
text: "Your profile picture displayed in various places throughout the shell"
color: Colors.textSecondary
font.pointSize: Style.fontSizeSmall * scaling
}
NTextInput {
text: Settings.data.general.avatarImage
placeholderText: "/home/user/.face"
// Profile section
ColumnLayout {
spacing: 8
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.general.avatarImage = text
Layout.topMargin: 8
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.Controls
import QtQuick.Layouts
import qs.Services
import qs.Widgets
Item {
property real scaling: 1
readonly property string tabIcon: "more_horiz"
readonly property string tabLabel: "Misc"
readonly property int tabIndex: 7
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
id: root
ColumnLayout {
anchors.fill: parent
spacing: Style.marginMedium * scaling
spacing: 0
NText {
text: "Media"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
ScrollView {
id: scrollView
NText {
text: "Visualizer Type"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Choose the style of the audio visualizer"
color: Colors.textSecondary
}
Layout.fillWidth: true
Layout.fillHeight: true
padding: 16
rightPadding: 12
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
NComboBox {
id: visualizerTypeComboBox
optionsKeys: ["radial", "fire", "diamond"]
optionsLabels: ["Radial", "Fire", "Diamond"]
currentKey: Settings.data.audioVisualizer.type
onSelected: function (key) {
Settings.data.audioVisualizer.type = key
ColumnLayout {
width: scrollView.availableWidth
spacing: 0
Item {
Layout.fillWidth: true
Layout.preferredHeight: 0
}
}
Item {
Layout.fillHeight: true
ColumnLayout {
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.Controls
import QtQuick.Layouts
import Quickshell
import Quickshell.Bluetooth
import qs.Services
import qs.Widgets
Item {
property real scaling: 1
readonly property string tabIcon: "wifi"
readonly property string tabLabel: "Network"
readonly property int tabIndex: 4
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
id: root
ColumnLayout {
anchors.fill: parent
spacing: Style.marginMedium * scaling
spacing: 0
NText {
text: "WiFi"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
ScrollView {
id: scrollView
NToggle {
label: "Enable WiFi"
description: "Turn WiFi radio on or off"
value: Settings.data.network.wifiEnabled
onToggled: function (newValue) {
Settings.data.network.wifiEnabled = newValue
Quickshell.execDetached(["nmcli", "radio", "wifi", newValue ? "on" : "off"])
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
}
}
NDivider {
Layout.fillWidth: true
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "Bluetooth"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
NText {
text: "Network Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
NToggle {
label: "Enable Bluetooth"
description: "Turn Bluetooth radio on or off"
value: Settings.data.network.bluetoothEnabled
onToggled: function (newValue) {
Settings.data.network.bluetoothEnabled = newValue
if (Bluetooth.defaultAdapter) {
Bluetooth.defaultAdapter.enabled = newValue
if (Bluetooth.defaultAdapter.enabled)
Bluetooth.defaultAdapter.discovering = true
// Network interfaces section
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
NText {
text: "Network Interfaces"
font.pointSize: 13
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.Controls
import QtQuick.Layouts
import qs.Services
import qs.Widgets
Item {
property real scaling: 1
readonly property string tabIcon: "videocam"
readonly property string tabLabel: "Screen Recorder"
readonly property int tabIndex: 3
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
id: root
ColumnLayout {
anchors.fill: parent
spacing: Style.marginMedium * scaling
spacing: 0
NText {
text: "Screen Recording"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
ScrollView {
id: scrollView
// Output Directory
NText {
text: "Output Directory"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Directory where screen recordings will be saved"
color: Colors.textSecondary
}
NTextInput {
text: Settings.data.screenRecorder.directory
Layout.fillWidth: true
onEditingFinished: function () {
Settings.data.screenRecorder.directory = text
}
}
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
// 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 {
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
NText {
text: "Audio Source"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
NText {
text: "Audio source to capture during recording"
color: Colors.textSecondary
}
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
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: "Screen Recording"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
// Output Directory
ColumnLayout {
spacing: 8
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.Controls
import QtQuick.Layouts
import qs.Services
import qs.Widgets
Item {
property real scaling: 1
readonly property string tabIcon: "schedule"
readonly property string tabLabel: "Time & Weather"
readonly property int tabIndex: 2
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
id: root
ColumnLayout {
anchors.fill: parent
spacing: Style.marginMedium * scaling
spacing: 0
NText {
text: "Time"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
ScrollView {
id: scrollView
NToggle {
label: "Use 12 Hour Clock"
description: "Display time in 12-hour format (e.g., 2:30 PM) instead of 24-hour format"
value: Settings.data.location.use12HourClock
onToggled: function (newValue) {
Settings.data.location.use12HourClock = newValue
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
}
}
NToggle {
label: "US Style Date"
description: "Display dates in MM/DD/YYYY format instead of DD/MM/YYYY"
value: Settings.data.location.reverseDayMonth
onToggled: function (newValue) {
Settings.data.location.reverseDayMonth = newValue
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
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.Controls
import QtQuick.Layouts
import qs.Services
import qs.Widgets
Item {
property real scaling: 1
readonly property string tabIcon: "image"
readonly property string tabLabel: "Wallpaper"
readonly property int tabIndex: 6
Layout.fillWidth: true
Layout.fillHeight: true
ColumnLayout {
id: root
ColumnLayout {
anchors.fill: parent
spacing: Style.marginMedium * scaling
spacing: 0
NText {
text: "Wallpaper Settings"
font.weight: Style.fontWeightBold
color: Colors.accentSecondary
}
ScrollView {
id: scrollView
// Folder
NText {
text: "Wallpaper Folder"
Layout.fillWidth: true
Layout.fillHeight: true
padding: 16
rightPadding: 12
clip: true
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
ScrollBar.vertical.policy: ScrollBar.AsNeeded
font.weight: Style.fontWeightBold
}
NText {
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
}
}
ColumnLayout {
width: scrollView.availableWidth
spacing: 0
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 {
Layout.fillWidth: true
}
}
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
}
Layout.preferredHeight: 0
}
NText {
text: "Transition Type"
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 {
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: Settings.data.wallpaper.swww.transitionFps + " FPS"
text: "Wallpaper Settings"
font.pointSize: 18
font.weight: Style.fontWeightBold
color: Colors.textPrimary
Layout.bottomMargin: 8
}
Item {
// Wallpaper Settings Category
ColumnLayout {
spacing: 8
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 {
text: "Transition Duration"
color: Colors.textPrimary
font.weight: Style.fontWeightBold
}
RowLayout {
NDivider {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
NText {
text: Settings.data.wallpaper.swww.transitionDuration.toFixed(2) + " s"
text: "Automation"
font.pointSize: 18
font.weight: Style.fontWeightBold
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 {
Layout.fillHeight: true
// Random Wallpaper
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 () {
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) {
if (error.toString().includes("No such file") || error === 2)
// File doesn't exist, create it with default values
@ -113,7 +120,7 @@ Singleton {
wallpaper: JsonObject {
property string directory: "/usr/share/wallpapers"
property string current: defaultWallpaper
property string current: ""
property bool isRandom: false
property int randomInterval: 300
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 {
Component.onCompleted: {
loadWallpapers()
setCurrentWallpaper(currentWallpaper, true)
toggleRandomWallpaper()
// Only set initial wallpaper if it's not empty
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 bool scanning: false
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() {
scanning = true
@ -29,10 +34,12 @@ Singleton {
}
function changeWallpaper(path) {
setCurrentWallpaper(path)
console.log("Wallpapers: changeWallpaper called with:", path)
setCurrentWallpaper(path, false)
}
function setCurrentWallpaper(path, isInitial) {
console.log("Wallpapers: Setting wallpaper to:", path, "isInitial:", isInitial)
currentWallpaper = path
if (!isInitial) {
Settings.data.wallpaper.current = path
@ -43,7 +50,11 @@ Singleton {
} else {
transitionType = Settings.data.wallpaper.swww.transitionType
}
console.log("SWWW: Changing wallpaper with transition type:", transitionType)
changeWallpaperProcess.running = true
} else {
// Fallback: update the settings directly for non-SWWW mode
console.log("Non-SWWW mode: Setting wallpaper directly")
}
if (randomWallpaperTimer.running) {
@ -59,7 +70,7 @@ Singleton {
if (!randomPath) {
return
}
setCurrentWallpaper(randomPath)
setCurrentWallpaper(randomPath, false)
}
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 {
id: randomWallpaperTimer
interval: Settings.data.wallpaper.randomInterval * 1000
@ -120,6 +138,18 @@ Singleton {
), "--transition-type", transitionType, "--transition-duration", Settings.data.wallpaper.swww.transitionDuration.toString(
), currentWallpaper]
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 {
@ -128,4 +158,23 @@ Singleton {
workingDirectory: Quickshell.shellDir
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.")
}
}
}
}