Settings: better horizontal dividers

This commit is contained in:
quadbyte 2025-08-06 20:03:41 -04:00
parent 7c2d2b4d66
commit 63eeb40c64
6 changed files with 980 additions and 856 deletions

View file

@ -1,156 +1,163 @@
import Quickshell
import Quickshell.Wayland
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Wayland
import qs.Components
import qs.Settings
import qs.Widgets.SettingsWindow.Tabs
import qs.Widgets.SettingsWindow.Tabs.Components
import qs.Components
PanelWithOverlay {
id: panelMain
property int activeTabIndex: 0
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
property int activeTabIndex: 0
// Function to show wallpaper selector
function showWallpaperSelector() {
if (wallpaperSelector) {
if (wallpaperSelector)
wallpaperSelector.show();
}
}
// Function to show settings window
function showSettings() {
show();
}
// Handle activeTabIndex changes
onActiveTabIndexChanged: {
if (activeTabIndex >= 0 && activeTabIndex <= 8) {
loadComponentForTab(activeTabIndex);
}
}
// Function to load component for a specific tab
function loadComponentForTab(tabIndex) {
const componentMap = {
0: generalSettings,
1: barSettings,
2: timeWeatherSettings,
3: recordingSettings,
4: networkSettings,
5: displaySettings,
6: wallpaperSettings,
7: miscSettings,
8: aboutSettings
"0": generalSettings,
"1": barSettings,
"2": timeWeatherSettings,
"3": recordingSettings,
"4": networkSettings,
"5": displaySettings,
"6": wallpaperSettings,
"7": miscSettings,
"8": aboutSettings
};
const tabNames = [
"General",
"Bar",
"Time & Weather",
"Screen Recorder",
"Network",
"Display",
"Wallpaper",
"Misc",
"About"
];
const tabNames = ["General", "Bar", "Time & Weather", "Screen Recorder", "Network", "Display", "Wallpaper", "Misc", "About"];
if (componentMap[tabIndex]) {
settingsLoader.sourceComponent = componentMap[tabIndex];
if (tabName) {
if (tabName)
tabName.text = tabNames[tabIndex];
}
}
}
WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand
// Handle activeTabIndex changes
onActiveTabIndexChanged: {
if (activeTabIndex >= 0 && activeTabIndex <= 8)
loadComponentForTab(activeTabIndex);
}
// Add safety checks for component loading
Component.onCompleted: {
// Ensure we start with a valid tab
if (activeTabIndex < 0 || activeTabIndex > 8) {
if (activeTabIndex < 0 || activeTabIndex > 8)
activeTabIndex = 0;
}
}
}
// Cleanup when window is hidden
onVisibleChanged: {
if (!visible) {
// Reset to default tab when hiding to prevent state issues
activeTabIndex = 0;
if (tabName) {
if (tabName)
tabName.text = "General";
}
}
}
Component {
id: generalSettings
General {}
General {
}
}
Component {
id: barSettings
Bar {}
Bar {
}
}
Component {
id: timeWeatherSettings
TimeWeather {}
TimeWeather {
}
}
Component {
id: recordingSettings
Recording {}
Recording {
}
}
Component {
id: networkSettings
Network {}
Network {
}
}
Component {
id: miscSettings
Misc {}
Misc {
}
}
Component {
id: aboutSettings
About {}
About {
}
}
Component {
id: displaySettings
Display {}
Display {
}
}
Component {
id: wallpaperSettings
Wallpaper {}
Wallpaper {
}
}
Rectangle {
id: settingsWindowRect
implicitWidth: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 2 : 600
implicitHeight: Quickshell.screens.length > 0 ? Quickshell.screens[0].height / 2 : 400
visible: parent.visible
color: "transparent"
// Center the settings window on screen
anchors.centerIn: parent
Rectangle {
id: background
color: Theme.backgroundPrimary
anchors.fill: parent
radius: 20
@ -167,11 +174,16 @@ PanelWithOverlay {
shadowVerticalOffset: 2
shadowBlur: 12
}
}
Rectangle {
id: settings
color: Theme.backgroundPrimary
topRightRadius: 20
bottomRightRadius: 20
anchors {
left: tabs.right
top: parent.top
@ -179,151 +191,159 @@ PanelWithOverlay {
right: parent.right
margins: 12
}
topRightRadius: 20
bottomRightRadius: 20
Rectangle {
id: headerArea
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 16
}
height: 48
color: "transparent"
Rectangle {
id: headerArea
RowLayout {
anchors.fill: parent
spacing: 12
height: 48
color: "transparent"
Text {
id: tabName
text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" :
activeTabIndex === 1 ? "Bar" :
activeTabIndex === 2 ? "Time & Weather" :
activeTabIndex === 3 ? "Screen Recorder" :
activeTabIndex === 4 ? "Network" :
activeTabIndex === 5 ? "Display" :
activeTabIndex === 6 ? "Wallpaper" :
activeTabIndex === 7 ? "Misc" :
activeTabIndex === 8 ? "About" : "General")
font.pixelSize: 18
font.bold: true
color: Theme.textPrimary
Layout.fillWidth: true
anchors {
top: parent.top
left: parent.left
right: parent.right
margins: 16
}
// Wallpaper Selection Button (only visible on Wallpaper tab)
Rectangle {
width: 160
height: 32
radius: 16
color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent"
border.color: Theme.accentPrimary
border.width: 1
visible: activeTabIndex === 6 // Wallpaper tab index
Row {
anchors.centerIn: parent
spacing: 6
Text {
text: "image"
font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
font.pixelSize: 16
color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Select Wallpaper"
font.pixelSize: 13
font.bold: true
color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: wallpaperButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
// Show the wallpaper selector
wallpaperSelector.show();
}
}
}
Rectangle {
width: 32
height: 32
radius: 16
color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent"
border.color: Theme.accentPrimary
border.width: 1
RowLayout {
anchors.fill: parent
spacing: 12
Text {
anchors.centerIn: parent
text: "close"
font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
id: tabName
text: wallpaperSelector.visible ? "Select Wallpaper" : (activeTabIndex === 0 ? "General" : activeTabIndex === 1 ? "Bar" : activeTabIndex === 2 ? "Time & Weather" : activeTabIndex === 3 ? "Screen Recorder" : activeTabIndex === 4 ? "Network" : activeTabIndex === 5 ? "Display" : activeTabIndex === 6 ? "Wallpaper" : activeTabIndex === 7 ? "Misc" : activeTabIndex === 8 ? "About" : "General")
font.pixelSize: 18
color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
font.bold: true
color: Theme.textPrimary
Layout.fillWidth: true
}
MouseArea {
id: closeButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: panelMain.dismiss()
// Wallpaper Selection Button (only visible on Wallpaper tab)
Rectangle {
width: 160
height: 32
radius: 16
color: wallpaperButtonArea.containsMouse ? Theme.accentPrimary : "transparent"
border.color: Theme.accentPrimary
border.width: 1
visible: activeTabIndex === 6 // Wallpaper tab index
Row {
anchors.centerIn: parent
spacing: 6
Text {
text: "image"
font.family: wallpaperButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
font.pixelSize: 16
color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
anchors.verticalCenter: parent.verticalCenter
}
Text {
text: "Select Wallpaper"
font.pixelSize: 13
font.bold: true
color: wallpaperButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
anchors.verticalCenter: parent.verticalCenter
}
}
MouseArea {
id: wallpaperButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
// Show the wallpaper selector
wallpaperSelector.show();
}
}
}
Rectangle {
width: 32
height: 32
radius: 16
color: closeButtonArea.containsMouse ? Theme.accentPrimary : "transparent"
border.color: Theme.accentPrimary
border.width: 1
Text {
anchors.centerIn: parent
text: "close"
font.family: closeButtonArea.containsMouse ? "Material Symbols Rounded" : "Material Symbols Outlined"
font.pixelSize: 18
color: closeButtonArea.containsMouse ? Theme.onAccent : Theme.accentPrimary
}
MouseArea {
id: closeButtonArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: panelMain.dismiss()
}
}
}
}
Rectangle {
height: 1
color: Theme.outline
opacity: 0.3
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
margins: 16
}
}
Item {
id: settingsContainer
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
margins: 16
topMargin: 32
}
// Simplified single loader approach
Loader {
id: settingsLoader
anchors.fill: parent
sourceComponent: generalSettings
}
// Wallpaper Selector Component
WallpaperSelector {
id: wallpaperSelector
anchors.fill: parent
}
}
}
Rectangle {
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
margins: 16
}
height: 1
color: Theme.outline
opacity: 0.3
}
Item {
id: settingsContainer
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
bottom: parent.bottom
margins: 16
topMargin: 32
}
// Simplified single loader approach
Loader {
id: settingsLoader
anchors.fill: parent
sourceComponent: generalSettings
}
// Wallpaper Selector Component
WallpaperSelector {
id: wallpaperSelector
anchors.fill: parent
}
}
}
Rectangle {
id: tabs
color: Theme.surface
width: Quickshell.screens.length > 0 ? Quickshell.screens[0].width / 9 : 100
height: settingsWindowRect.height
@ -332,101 +352,135 @@ PanelWithOverlay {
border.color: Theme.outline
border.width: 1
Column {
width: parent.width
spacing: 0
topPadding: 8
bottomPadding: 8
Column {
width: parent.width
spacing: 0
topPadding: 8
bottomPadding: 8
Repeater {
id: repeater
model: [
{ icon: "tune", text: "General" },
{ icon: "space_dashboard", text: "Bar" },
{ icon: "schedule", text: "Time & Weather" },
{ icon: "photo_camera", text: "Screen Recorder" },
{ icon: "wifi", text: "Network" },
{ icon: "monitor", text: "Display" },
{ icon: "wallpaper", text: "Wallpaper" },
{ icon: "settings_suggest", text: "Misc" },
{ icon: "info", text: "About" }
]
Repeater {
id: repeater
delegate: Rectangle {
width: tabs.width
height: 48
color: "transparent"
model: [{
"icon": "tune",
"text": "General"
}, {
"icon": "space_dashboard",
"text": "Bar"
}, {
"icon": "schedule",
"text": "Time & Weather"
}, {
"icon": "photo_camera",
"text": "Screen Recorder"
}, {
"icon": "wifi",
"text": "Network"
}, {
"icon": "monitor",
"text": "Display"
}, {
"icon": "wallpaper",
"text": "Wallpaper"
}, {
"icon": "settings_suggest",
"text": "Misc"
}, {
"icon": "info",
"text": "About"
}]
RowLayout {
anchors.fill: parent
spacing: 8
delegate: Rectangle {
width: tabs.width
height: 48
color: "transparent"
RowLayout {
anchors.fill: parent
spacing: 8
Rectangle {
id: activeIndicator
Layout.leftMargin: 8
Layout.preferredWidth: 3
Layout.preferredHeight: 24
Layout.alignment: Qt.AlignVCenter
radius: 2
color: Theme.accentPrimary
opacity: index === activeTabIndex ? 1 : 0
Behavior on opacity {
NumberAnimation {
duration: 200
}
}
}
Label {
id: icon
text: modelData.icon
font.family: "Material Symbols Outlined"
font.pixelSize: 24
color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary
opacity: index === activeTabIndex ? 1 : 0.8
Layout.leftMargin: 20
Layout.preferredWidth: 24
Layout.preferredHeight: 24
Layout.alignment: Qt.AlignVCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
Label {
id: label
text: modelData.text
font.pixelSize: 16
color: index === activeTabIndex ? Theme.accentPrimary : (tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary)
font.weight: index === activeTabIndex ? Font.DemiBold : (tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal)
Layout.fillWidth: true
Layout.preferredHeight: 24
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.leftMargin: 4
Layout.rightMargin: 16
verticalAlignment: Text.AlignVCenter
}
}
MouseArea {
id: tabMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
activeTabIndex = index;
loadComponentForTab(index);
}
}
Rectangle {
id: activeIndicator
Layout.leftMargin: 8
Layout.preferredWidth: 3
Layout.preferredHeight: 24
Layout.alignment: Qt.AlignVCenter
radius: 2
color: Theme.accentPrimary
opacity: index === activeTabIndex ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 200 } }
width: parent.width
height: 1
color: Theme.outline
opacity: 0.6
visible: index < (repeater.count - 1)
anchors.bottom: parent.bottom
}
Label {
id: icon
text: modelData.icon
font.family: "Material Symbols Outlined"
font.pixelSize: 24
color: index === activeTabIndex ? Theme.accentPrimary : Theme.textPrimary
opacity: index === activeTabIndex ? 1 : 0.8
Layout.leftMargin: 20
Layout.preferredWidth: 24
Layout.preferredHeight: 24
Layout.alignment: Qt.AlignVCenter
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
Label {
id: label
text: modelData.text
font.pixelSize: 16
color: index === activeTabIndex ? Theme.accentPrimary :
(tabMouseArea.containsMouse ? Theme.accentPrimary : Theme.textSecondary)
font.weight: index === activeTabIndex ? Font.DemiBold :
(tabMouseArea.containsMouse ? Font.DemiBold : Font.Normal)
Layout.fillWidth: true
Layout.preferredHeight: 24
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.leftMargin: 4
Layout.rightMargin: 16
verticalAlignment: Text.AlignVCenter
}
}
MouseArea {
id: tabMouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
activeTabIndex = index;
loadComponentForTab(index);
}
}
Rectangle {
width: parent.width
height: 1
color: Theme.outline
opacity: 0.6
visible: index < (repeater.count - 1)
anchors.bottom: parent.bottom
}
}
}
}
}
}
}
}

View file

@ -1,11 +1,11 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import QtQuick.Effects
import QtQuick.Layouts
import Quickshell
import Quickshell.Io
import qs.Settings
import qs.Components
import qs.Settings
Item {
id: root
@ -15,391 +15,430 @@ Item {
property var contributors: []
property string githubDataPath: Settings.settingsDir + "github_data.json"
function loadFromFile() {
const now = Date.now();
const data = githubData;
if (!data.timestamp || (now - data.timestamp > 3.6e+06)) {
console.log("[About] Cache expired or missing, fetching new data from GitHub...");
fetchFromGitHub();
return ;
}
console.log("[About] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)");
if (data.version)
root.latestVersion = data.version;
if (data.contributors)
root.contributors = data.contributors;
}
function fetchFromGitHub() {
versionProcess.running = true;
contributorsProcess.running = true;
}
function saveData() {
githubData.timestamp = Date.now();
Qt.callLater(() => {
githubDataFile.writeAdapter();
});
}
Process {
id: currentVersionProcess
command: ["sh", "-c", "cd " + Quickshell.shellDir + " && git describe --tags --abbrev=0 2>/dev/null || echo 'Unknown'"]
Component.onCompleted: {
running = true;
}
stdout: StdioCollector {
onStreamFinished: {
const version = text.trim()
const version = text.trim();
if (version && version !== "Unknown") {
root.currentVersion = version
root.currentVersion = version;
} else {
currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"]
currentVersionProcess.running = true
currentVersionProcess.command = ["sh", "-c", "cd " + Quickshell.shellDir + " && cat package.json 2>/dev/null | grep '\"version\"' | cut -d'\"' -f4 || echo 'Unknown'"];
currentVersionProcess.running = true;
}
}
}
Component.onCompleted: {
running = true
}
}
FileView {
id: githubDataFile
path: root.githubDataPath
blockLoading: true
printErrors: true
watchChanges: true
onFileChanged: githubDataFile.reload()
onLoaded: loadFromFile()
onLoadFailed: function(error) {
console.log("GitHub data file doesn't exist yet, creating it...");
githubData.version = "Unknown";
githubData.contributors = [];
githubData.timestamp = 0;
githubDataFile.writeAdapter();
fetchFromGitHub();
}
Component.onCompleted: {
if (path)
reload();
}
JsonAdapter {
id: githubData
property string version: "Unknown"
property var contributors: []
property double timestamp: 0
}
onFileChanged: githubDataFile.reload()
onLoaded: loadFromFile()
onLoadFailed: function(error) {
console.log("GitHub data file doesn't exist yet, creating it...")
githubData.version = "Unknown"
githubData.contributors = []
githubData.timestamp = 0
githubDataFile.writeAdapter()
fetchFromGitHub()
}
Component.onCompleted: if (path) reload()
}
function loadFromFile() {
const now = Date.now()
const data = githubData
if (!data.timestamp || (now - data.timestamp > 3600000)) {
console.log("[About] Cache expired or missing, fetching new data from GitHub...")
fetchFromGitHub()
return
}
console.log("[About] Loading cached GitHub data (age: " + Math.round((now - data.timestamp) / 60000) + " minutes)")
if (data.version) {
root.latestVersion = data.version
}
if (data.contributors) {
root.contributors = data.contributors
}
}
Process {
id: versionProcess
command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/releases/latest"]
stdout: StdioCollector {
onStreamFinished: {
try {
const data = JSON.parse(text)
const data = JSON.parse(text);
if (data.tag_name) {
const version = data.tag_name
githubData.version = version
root.latestVersion = version
console.log("[About] Latest version fetched from GitHub:", version)
const version = data.tag_name;
githubData.version = version;
root.latestVersion = version;
console.log("[About] Latest version fetched from GitHub:", version);
} else {
console.log("No tag_name in GitHub response")
console.log("No tag_name in GitHub response");
}
saveData()
saveData();
} catch (e) {
console.error("Failed to parse version:", e)
console.error("Failed to parse version:", e);
}
}
}
}
Process {
id: contributorsProcess
command: ["curl", "-s", "https://api.github.com/repos/Ly-sec/Noctalia/contributors?per_page=100"]
stdout: StdioCollector {
onStreamFinished: {
try {
const data = JSON.parse(text)
githubData.contributors = data || []
root.contributors = githubData.contributors
console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors")
saveData()
const data = JSON.parse(text);
githubData.contributors = data || [];
root.contributors = githubData.contributors;
console.log("[About] Contributors data fetched from GitHub:", githubData.contributors.length, "contributors");
saveData();
} catch (e) {
console.error("Failed to parse contributors:", e)
root.contributors = []
console.error("Failed to parse contributors:", e);
root.contributors = [];
}
}
}
}
function fetchFromGitHub() {
versionProcess.running = true
contributorsProcess.running = true
}
function saveData() {
githubData.timestamp = Date.now()
Qt.callLater(() => {
githubDataFile.writeAdapter()
})
}
Item {
anchors.fill: parent
ColumnLayout {
id: mainLayout
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
spacing: 8
Item {
Layout.fillWidth: true
Layout.preferredHeight: 32
}
Text {
text: "Noctalia"
font.pixelSize: 24
font.bold: true
color: Theme.textPrimary
Layout.alignment: Qt.AlignCenter
}
GridLayout {
Layout.alignment: Qt.AlignCenter
columns: 2
rowSpacing: 4
columnSpacing: 8
Text {
text: "Latest Version:"
font.pixelSize: 16
color: Theme.textSecondary
Layout.alignment: Qt.AlignRight
}
Text {
text: root.latestVersion
font.pixelSize: 16
color: Theme.textPrimary
font.bold: true
}
Text {
text: "Installed Version:"
font.pixelSize: 16
color: Theme.textSecondary
Layout.alignment: Qt.AlignRight
}
Text {
text: root.currentVersion
font.pixelSize: 16
color: Theme.textPrimary
font.bold: true
}
}
Rectangle {
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 8
Layout.preferredWidth: updateText.implicitWidth + 46
Layout.preferredHeight: 32
radius: 20
color: updateArea.containsMouse ? Theme.accentPrimary : "transparent"
border.color: Theme.accentPrimary
border.width: 1
visible: {
if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown") {
return false
}
const latest = root.latestVersion.replace("v", "").split(".")
const current = root.currentVersion.replace("v", "").split(".")
for (let i = 0; i < Math.max(latest.length, current.length); i++) {
const l = parseInt(latest[i] || "0")
const c = parseInt(current[i] || "0")
if (l > c) return true
if (l < c) return false
}
return false
}
RowLayout {
anchors.centerIn: parent
spacing: 8
Text {
text: "system_update"
font.family: "Material Symbols Outlined"
font.pixelSize: 18
color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
}
Text {
id: updateText
text: "Download latest release"
font.pixelSize: 14
color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
}
}
MouseArea {
id: updateArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"])
}
}
}
Text {
text: "Description something something <.< I hate writing text..."
font.pixelSize: 14
color: Theme.textSecondary
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 24
}
ColumnLayout {
Layout.fillWidth: true
Layout.topMargin: 32
Layout.leftMargin: 32
Layout.rightMargin: 32
spacing: 16
RowLayout {
Layout.alignment: Qt.AlignCenter
spacing: 8
Text {
text: "Contributors"
font.pixelSize: 18
font.bold: true
color: Theme.textPrimary
}
Text {
text: "(" + root.contributors.length + ")"
font.pixelSize: 14
color: Theme.textSecondary
}
}
ScrollView {
Item {
Layout.fillWidth: true
Layout.preferredHeight: 300
clip: true
Layout.preferredHeight: 16
}
Text {
text: "Noctalia"
font.pixelSize: 24
font.bold: true
color: Theme.textPrimary
Layout.alignment: Qt.AlignCenter
}
GridLayout {
Layout.alignment: Qt.AlignCenter
columns: 2
rowSpacing: 4
columnSpacing: 8
Text {
text: "Latest Version:"
font.pixelSize: 16
color: Theme.textSecondary
Layout.alignment: Qt.AlignRight
}
Text {
text: root.latestVersion
font.pixelSize: 16
color: Theme.textPrimary
font.bold: true
}
Text {
text: "Installed Version:"
font.pixelSize: 16
color: Theme.textSecondary
Layout.alignment: Qt.AlignRight
}
Text {
text: root.currentVersion
font.pixelSize: 16
color: Theme.textPrimary
font.bold: true
}
}
Rectangle {
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 8
Layout.preferredWidth: updateText.implicitWidth + 46
Layout.preferredHeight: 32
radius: 20
color: updateArea.containsMouse ? Theme.accentPrimary : "transparent"
border.color: Theme.accentPrimary
border.width: 1
visible: {
if (root.currentVersion === "Unknown" || root.latestVersion === "Unknown")
return false;
const latest = root.latestVersion.replace("v", "").split(".");
const current = root.currentVersion.replace("v", "").split(".");
for (let i = 0; i < Math.max(latest.length, current.length); i++) {
const l = parseInt(latest[i] || "0");
const c = parseInt(current[i] || "0");
if (l > c)
return true;
if (l < c)
return false;
}
return false;
}
RowLayout {
anchors.centerIn: parent
spacing: 8
Text {
text: "system_update"
font.family: "Material Symbols Outlined"
font.pixelSize: 18
color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
}
Text {
id: updateText
text: "Download latest release"
font.pixelSize: 14
color: updateArea.containsMouse ? Theme.backgroundPrimary : Theme.accentPrimary
}
}
MouseArea {
id: updateArea
Item {
anchors.fill: parent
GridView {
id: contributorsGrid
anchors.centerIn: parent
width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200)
height: parent.height
cellWidth: 200
cellHeight: 110
model: root.contributors
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
Quickshell.execDetached(["xdg-open", "https://github.com/Ly-sec/Noctalia/releases/latest"]);
}
}
delegate: Rectangle {
width: contributorsGrid.cellWidth - 4
height: contributorsGrid.cellHeight - 10
radius: 20
color: contributorArea.containsMouse ? Theme.highlight : "transparent"
}
RowLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 12
Text {
text: "Description something something <.< I hate writing text..."
font.pixelSize: 14
color: Theme.textSecondary
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 24
}
Item {
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
height: 1
color: Theme.outline
opacity: 0.3
}
Image {
id: avatarImage
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: 32
Layout.rightMargin: 32
spacing: 16
RowLayout {
Layout.alignment: Qt.AlignCenter
spacing: 8
Text {
text: "Contributors"
font.pixelSize: 18
font.bold: true
color: Theme.textPrimary
}
Text {
text: "(" + root.contributors.length + ")"
font.pixelSize: 14
color: Theme.textSecondary
}
}
ScrollView {
Layout.fillWidth: true
Layout.preferredHeight: 300
clip: true
Item {
anchors.fill: parent
GridView {
id: contributorsGrid
anchors.centerIn: parent
width: Math.min(parent.width, Math.ceil(root.contributors.length / 3) * 200)
height: parent.height
cellWidth: 200
cellHeight: 100
model: root.contributors
delegate: Rectangle {
width: contributorsGrid.cellWidth - 4
height: contributorsGrid.cellHeight - 10
radius: 20
color: contributorArea.containsMouse ? Theme.highlight : "transparent"
RowLayout {
anchors.fill: parent
source: modelData.avatar_url || ""
sourceSize: Qt.size(80, 80)
visible: false
mipmap: true
smooth: true
asynchronous: true
fillMode: Image.PreserveAspectCrop
cache: true
anchors.margins: 8
spacing: 12
Item {
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 40
Layout.preferredHeight: 40
Image {
id: avatarImage
anchors.fill: parent
source: modelData.avatar_url || ""
sourceSize: Qt.size(80, 80)
visible: false
mipmap: true
smooth: true
asynchronous: true
fillMode: Image.PreserveAspectCrop
cache: true
}
MultiEffect {
anchors.fill: parent
source: avatarImage
maskEnabled: true
maskSource: mask
}
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
radius: avatarImage.width / 2
}
}
Text {
anchors.centerIn: parent
text: "person"
font.family: "Material Symbols Outlined"
font.pixelSize: 24
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
visible: !avatarImage.source || avatarImage.status !== Image.Ready
}
}
ColumnLayout {
spacing: 4
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
Text {
text: modelData.login || "Unknown"
font.pixelSize: 13
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: (modelData.contributions || 0) + " commits"
font.pixelSize: 11
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary
}
}
}
MultiEffect {
anchors.fill: parent
source: avatarImage
maskEnabled: true
maskSource: mask
}
MouseArea {
id: contributorArea
Item {
id: mask
anchors.fill: parent
layer.enabled: true
visible: false
Rectangle {
anchors.fill: parent
radius: avatarImage.width / 2
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (modelData.html_url)
Quickshell.execDetached(["xdg-open", modelData.html_url]);
}
}
Text {
anchors.centerIn: parent
text: "person"
font.family: "Material Symbols Outlined"
font.pixelSize: 24
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
visible: !avatarImage.source || avatarImage.status !== Image.Ready
}
}
ColumnLayout {
spacing: 4
Layout.alignment: Qt.AlignVCenter
Layout.fillWidth: true
Text {
text: modelData.login || "Unknown"
font.pixelSize: 13
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textPrimary
elide: Text.ElideRight
Layout.fillWidth: true
}
Text {
text: (modelData.contributions || 0) + " commits"
font.pixelSize: 11
color: contributorArea.containsMouse ? Theme.backgroundPrimary : Theme.textSecondary
}
}
}
MouseArea {
id: contributorArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (modelData.html_url) {
Quickshell.execDetached(["xdg-open", modelData.html_url])
}
}
}
}
}
}
}
}
}
}
}
}

View file

@ -1,11 +1,12 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Settings
import qs.Components
import qs.Settings
ColumnLayout {
id: root
spacing: 0
anchors.fill: parent
anchors.margins: 0
@ -15,7 +16,6 @@ ColumnLayout {
Layout.preferredHeight: 0
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
@ -28,7 +28,6 @@ ColumnLayout {
Layout.bottomMargin: 8
}
ColumnLayout {
spacing: 2
Layout.fillWidth: true
@ -67,7 +66,9 @@ ColumnLayout {
z: 2
}
Avatar {}
Avatar {
}
}
Rectangle {
@ -80,6 +81,7 @@ ColumnLayout {
TextInput {
id: profileImageInput
anchors.fill: parent
anchors.leftMargin: 12
anchors.rightMargin: 12
@ -96,25 +98,27 @@ ColumnLayout {
onTextChanged: {
Settings.settings.profileImage = text;
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.IBeamCursor
onClicked: profileImageInput.forceActiveFocus()
}
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1
color: Theme.outline
opacity: 0.3
@ -124,7 +128,6 @@ ColumnLayout {
spacing: 4
Layout.fillWidth: true
Text {
text: "User Interface"
font.pixelSize: 18
@ -133,7 +136,6 @@ ColumnLayout {
Layout.bottomMargin: 8
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
@ -160,10 +162,12 @@ ColumnLayout {
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: cornersSwitch
width: 52
height: 32
radius: 16
@ -173,6 +177,7 @@ ColumnLayout {
Rectangle {
id: cornersThumb
width: 28
height: 28
radius: 14
@ -187,7 +192,9 @@ ColumnLayout {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
@ -197,10 +204,12 @@ ColumnLayout {
Settings.settings.showCorners = !Settings.settings.showCorners;
}
}
}
}
}
}
}
}
ColumnLayout {
spacing: 8
@ -229,10 +238,12 @@ ColumnLayout {
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: dockSwitch
width: 52
height: 32
radius: 16
@ -242,6 +253,7 @@ ColumnLayout {
Rectangle {
id: dockThumb
width: 28
height: 28
radius: 14
@ -256,7 +268,9 @@ ColumnLayout {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
@ -266,10 +280,12 @@ ColumnLayout {
Settings.settings.showDock = !Settings.settings.showDock;
}
}
}
}
}
}
}
}
ColumnLayout {
spacing: 8
@ -298,10 +314,12 @@ ColumnLayout {
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: dimSwitch
width: 52
height: 32
radius: 16
@ -311,6 +329,7 @@ ColumnLayout {
Rectangle {
id: dimThumb
width: 28
height: 28
radius: 14
@ -325,7 +344,9 @@ ColumnLayout {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
@ -335,13 +356,18 @@ ColumnLayout {
Settings.settings.dimPanels = !Settings.settings.dimPanels;
}
}
}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}

View file

@ -97,19 +97,14 @@ ColumnLayout {
}
}
Rectangle {
Layout.topMargin: 26
Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1
color: Theme.outline
opacity: 0.3
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
height: 1
color: Theme.outline
opacity: 0.3
}
ColumnLayout {
spacing: 16

View file

@ -1,12 +1,13 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Settings
import qs.Components
import qs.Settings
import qs.Widgets.SettingsWindow.Tabs.Components
ColumnLayout {
id: root
spacing: 0
anchors.fill: parent
anchors.margins: 0
@ -16,7 +17,6 @@ ColumnLayout {
Layout.preferredHeight: 0
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
@ -29,7 +29,6 @@ ColumnLayout {
Layout.bottomMargin: 8
}
ColumnLayout {
spacing: 8
Layout.fillWidth: true
@ -57,10 +56,12 @@ ColumnLayout {
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: use12HourClockSwitch
width: 52
height: 32
radius: 16
@ -70,6 +71,7 @@ ColumnLayout {
Rectangle {
id: use12HourClockThumb
width: 28
height: 28
radius: 14
@ -84,7 +86,9 @@ ColumnLayout {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
@ -94,10 +98,12 @@ ColumnLayout {
Settings.settings.use12HourClock = !Settings.settings.use12HourClock;
}
}
}
}
}
}
}
}
ColumnLayout {
spacing: 8
@ -126,10 +132,12 @@ ColumnLayout {
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: reverseDayMonthSwitch
width: 52
height: 32
radius: 16
@ -139,6 +147,7 @@ ColumnLayout {
Rectangle {
id: reverseDayMonthThumb
width: 28
height: 28
radius: 14
@ -153,7 +162,9 @@ ColumnLayout {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
@ -163,25 +174,24 @@ ColumnLayout {
Settings.settings.reverseDayMonth = !Settings.settings.reverseDayMonth;
}
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1
color: Theme.outline
opacity: 0.3
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
@ -194,7 +204,6 @@ ColumnLayout {
Layout.bottomMargin: 8
}
ColumnLayout {
spacing: 8
Layout.fillWidth: true
@ -223,6 +232,7 @@ ColumnLayout {
TextInput {
id: cityInput
anchors.fill: parent
anchors.leftMargin: 12
anchors.rightMargin: 12
@ -237,7 +247,6 @@ ColumnLayout {
selectByMouse: true
activeFocusOnTab: true
inputMethodHints: Qt.ImhNone
onTextChanged: {
Settings.settings.weatherCity = text;
}
@ -249,10 +258,12 @@ ColumnLayout {
cityInput.forceActiveFocus();
}
}
}
}
}
}
}
}
ColumnLayout {
spacing: 8
@ -281,15 +292,21 @@ ColumnLayout {
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
UnitSelector {
}
UnitSelector {}
}
}
}
Item {
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}

View file

@ -122,264 +122,101 @@ ColumnLayout {
}
}
}
Rectangle {
Layout.topMargin: 26
Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1
color: Theme.outline
opacity: 0.3
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
height: 1
color: Theme.outline
opacity: 0.3
}
ColumnLayout {
spacing: 4
Layout.fillWidth: true
Text {
text: "Automation"
font.pixelSize: 18
font.bold: true
color: Theme.textPrimary
Layout.bottomMargin: 8
}
// Random Wallpaper
ColumnLayout {
spacing: 4
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
Text {
text: "Automation"
font.pixelSize: 18
font.bold: true
color: Theme.textPrimary
Layout.bottomMargin: 8
}
// Random Wallpaper
ColumnLayout {
RowLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
RowLayout {
spacing: 8
Layout.fillWidth: true
ColumnLayout {
spacing: 4
Layout.fillWidth: true
Text {
text: "Random Wallpaper"
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
Text {
text: "Automatically select random wallpapers from the folder"
font.pixelSize: 12
color: Theme.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: randomWallpaperSwitch
width: 52
height: 32
radius: 16
color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant
border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline
border.width: 2
Rectangle {
id: randomWallpaperThumb
width: 28
height: 28
radius: 14
color: Theme.surface
border.color: Theme.outline
border.width: 1
y: 2
x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2
Behavior on x {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper;
}
}
}
}
}
// Use Wallpaper Theme
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
RowLayout {
spacing: 8
Layout.fillWidth: true
ColumnLayout {
spacing: 4
Layout.fillWidth: true
Text {
text: "Use Wallpaper Theme"
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
Text {
text: "Automatically adjust theme colors based on wallpaper"
font.pixelSize: 12
color: Theme.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: wallpaperThemeSwitch
width: 52
height: 32
radius: 16
color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant
border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline
border.width: 2
Rectangle {
id: wallpaperThemeThumb
width: 28
height: 28
radius: 14
color: Theme.surface
border.color: Theme.outline
border.width: 1
y: 2
x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2
Behavior on x {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme;
}
}
}
}
}
// Wallpaper Interval
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
Text {
text: "Wallpaper Interval"
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
Text {
text: "How often to change wallpapers automatically (in seconds)"
font.pixelSize: 12
color: Theme.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
ColumnLayout {
spacing: 4
Layout.fillWidth: true
Text {
text: Settings.settings.wallpaperInterval + " seconds"
text: "Random Wallpaper"
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
Item {
Text {
text: "Automatically select random wallpapers from the folder"
font.pixelSize: 12
color: Theme.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Slider {
id: intervalSlider
Rectangle {
id: randomWallpaperSwitch
Layout.fillWidth: true
from: 10
to: 900
stepSize: 10
value: Settings.settings.wallpaperInterval
snapMode: Slider.SnapAlways
onMoved: {
Settings.settings.wallpaperInterval = Math.round(value);
}
width: 52
height: 32
radius: 16
color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.surfaceVariant
border.color: Settings.settings.randomWallpaper ? Theme.accentPrimary : Theme.outline
border.width: 2
background: Rectangle {
x: intervalSlider.leftPadding
y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: intervalSlider.availableWidth
height: implicitHeight
radius: 2
color: Theme.surfaceVariant
Rectangle {
id: randomWallpaperThumb
width: 28
height: 28
radius: 14
color: Theme.surface
border.color: Theme.outline
border.width: 1
y: 2
x: Settings.settings.randomWallpaper ? randomWallpaperSwitch.width - width - 2 : 2
Behavior on x {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
Rectangle {
width: intervalSlider.visualPosition * parent.width
height: parent.height
color: Theme.accentPrimary
radius: 2
}
}
handle: Rectangle {
x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width)
y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2
implicitWidth: 20
implicitHeight: 20
radius: 10
color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface
border.color: Theme.accentPrimary
border.width: 2
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
Settings.settings.randomWallpaper = !Settings.settings.randomWallpaper;
}
}
}
@ -388,16 +225,172 @@ ColumnLayout {
}
// Use Wallpaper Theme
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
RowLayout {
spacing: 8
Layout.fillWidth: true
ColumnLayout {
spacing: 4
Layout.fillWidth: true
Text {
text: "Use Wallpaper Theme"
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
Text {
text: "Automatically adjust theme colors based on wallpaper"
font.pixelSize: 12
color: Theme.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
Rectangle {
id: wallpaperThemeSwitch
width: 52
height: 32
radius: 16
color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.surfaceVariant
border.color: Settings.settings.useWallpaperTheme ? Theme.accentPrimary : Theme.outline
border.width: 2
Rectangle {
id: wallpaperThemeThumb
width: 28
height: 28
radius: 14
color: Theme.surface
border.color: Theme.outline
border.width: 1
y: 2
x: Settings.settings.useWallpaperTheme ? wallpaperThemeSwitch.width - width - 2 : 2
Behavior on x {
NumberAnimation {
duration: 200
easing.type: Easing.OutCubic
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
Settings.settings.useWallpaperTheme = !Settings.settings.useWallpaperTheme;
}
}
}
}
}
// Wallpaper Interval
ColumnLayout {
spacing: 8
Layout.fillWidth: true
Layout.topMargin: 8
Text {
text: "Wallpaper Interval"
font.pixelSize: 13
font.bold: true
color: Theme.textPrimary
}
Text {
text: "How often to change wallpapers automatically (in seconds)"
font.pixelSize: 12
color: Theme.textSecondary
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Text {
text: Settings.settings.wallpaperInterval + " seconds"
font.pixelSize: 13
color: Theme.textPrimary
}
Item {
Layout.fillWidth: true
}
}
Slider {
id: intervalSlider
Layout.fillWidth: true
from: 10
to: 900
stepSize: 10
value: Settings.settings.wallpaperInterval
snapMode: Slider.SnapAlways
onMoved: {
Settings.settings.wallpaperInterval = Math.round(value);
}
background: Rectangle {
x: intervalSlider.leftPadding
y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2
implicitWidth: 200
implicitHeight: 4
width: intervalSlider.availableWidth
height: implicitHeight
radius: 2
color: Theme.surfaceVariant
Rectangle {
width: intervalSlider.visualPosition * parent.width
height: parent.height
color: Theme.accentPrimary
radius: 2
}
}
handle: Rectangle {
x: intervalSlider.leftPadding + intervalSlider.visualPosition * (intervalSlider.availableWidth - width)
y: intervalSlider.topPadding + intervalSlider.availableHeight / 2 - height / 2
implicitWidth: 20
implicitHeight: 20
radius: 10
color: intervalSlider.pressed ? Theme.surfaceVariant : Theme.surface
border.color: Theme.accentPrimary
border.width: 2
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.topMargin: 26
Layout.bottomMargin: 18
anchors {
top: headerArea.bottom
left: parent.left
right: parent.right
}
height: 1
color: Theme.outline
opacity: 0.3