SettingsTabs: use NHeader, move display settings around

This commit is contained in:
Ly-sec 2025-09-14 15:50:23 +02:00
parent 0d611fc891
commit 7594651e05
19 changed files with 483 additions and 480 deletions

View file

@ -106,6 +106,7 @@ Singleton {
"settings-wallpaper-selector": "library-photo",
"settings-screen-recorder": "video",
"settings-hooks": "link",
"settings-notification": "notification",
"settings-about": "info-square-rounded",
"bluetooth": "bluetooth",
"bt-device-generic": "bluetooth",

View file

@ -399,6 +399,10 @@ Singleton {
property list<string> monitors: []
// Last time the user opened the notification history (ms since epoch)
property real lastSeenTs: 0
// Duration settings for different urgency levels (in seconds)
property int lowUrgencyDuration: 3
property int normalUrgencyDuration: 8
property int criticalUrgencyDuration: 15
}
// audio

View file

@ -29,11 +29,11 @@ NPanel {
Dock,
Hooks,
Launcher,
Brightness,
ColorScheme,
Display,
General,
Network,
Notification,
ScreenRecorder,
Weather,
Wallpaper,
@ -72,10 +72,6 @@ NPanel {
id: audioTab
Tabs.AudioTab {}
}
Component {
id: brightnessTab
Tabs.BrightnessTab {}
}
Component {
id: displayTab
Tabs.DisplayTab {}
@ -116,6 +112,10 @@ NPanel {
id: dockTab
Tabs.DockTab {}
}
Component {
id: notificationTab
Tabs.NotificationTab {}
}
// Order *DOES* matter
function updateTabsModel() {
@ -149,16 +149,16 @@ NPanel {
"label": "Display",
"icon": "settings-display",
"source": displayTab
}, {
"id": SettingsPanel.Tab.Notification,
"label": "Notification",
"icon": "settings-notification",
"source": notificationTab
}, {
"id": SettingsPanel.Tab.Network,
"label": "Network",
"icon": "settings-network",
"source": networkTab
}, {
"id": SettingsPanel.Tab.Brightness,
"label": "Brightness",
"icon": "settings-brightness",
"source": brightnessTab
}, {
"id": SettingsPanel.Tab.Weather,
"label": "Weather",

View file

@ -9,6 +9,11 @@ import qs.Services
ColumnLayout {
id: root
NHeader {
title: "Audio Settings"
description: "Configure system audio, devices, and media player preferences."
}
property real localVolume: AudioService.volume
Connections {
@ -158,12 +163,8 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginS * scaling
NText {
text: "Audio Devices"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "Audio Devices"
}
// -------------------------------
@ -234,12 +235,8 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginL * scaling
NText {
text: "Media Player"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "Media Player"
}
// Preferred player
@ -360,12 +357,8 @@ ColumnLayout {
spacing: Style.marginS * scaling
Layout.fillWidth: true
NText {
text: "Audio Visualizer"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "Audio Visualizer"
}
// AudioService Visualizer section

View file

@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services
import qs.Widgets
@ -9,6 +10,24 @@ import qs.Modules.SettingsPanel.Bar
ColumnLayout {
id: root
NHeader {
title: "Bar Settings"
description: "Configure bar appearance, positioning, and monitor settings."
}
// Helper functions to update arrays immutably
function addMonitor(list, name) {
const arr = (list || []).slice()
if (!arr.includes(name))
arr.push(name)
return arr
}
function removeMonitor(list, name) {
return (list || []).filter(function (n) {
return n !== name
})
}
// Handler for drag start - disables panel background clicks
function handleDragStart() {
var panel = PanelService.getPanel("settingsPanel")
@ -56,23 +75,19 @@ ColumnLayout {
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NText {
text: "Background Opacity"
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
}
NText {
text: "Adjust the background opacity of the bar."
font.pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
NHeader {
title: "Background Opacity"
description: "Adjust the background opacity of the bar."
}
RowLayout {
@ -189,25 +204,49 @@ ColumnLayout {
Layout.bottomMargin: Style.marginXL * scaling
}
// Monitor Configuration
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NHeader {
title: "Monitor Configuration"
description: "Choose which monitors should display the bar."
}
Repeater {
model: Quickshell.screens || []
delegate: NCheckbox {
Layout.fillWidth: true
label: `${modelData.name || "Unknown"}${modelData.model ? `: ${modelData.model}` : ""}`
description: `${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})`
checked: (Settings.data.bar.monitors || []).indexOf(modelData.name) !== -1
onToggled: checked => {
if (checked) {
Settings.data.bar.monitors = addMonitor(Settings.data.bar.monitors, modelData.name)
} else {
Settings.data.bar.monitors = removeMonitor(Settings.data.bar.monitors, modelData.name)
}
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
// Widgets Management Section
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NText {
text: "Widgets Positioning"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
}
NText {
text: "Drag and drop widgets to reorder them within each section, or use the add/remove buttons to manage widgets."
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
NHeader {
title: "Widgets Positioning"
description: "Drag and drop widgets to reorder them within each section, or use the add/remove buttons to manage widgets."
bottomMargin: 0
}
// Bar Sections

View file

@ -1,340 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Io
import qs.Commons
import qs.Services
import qs.Widgets
ColumnLayout {
id: root
// Time dropdown options (00:00 .. 23:30)
ListModel {
id: timeOptions
}
Component.onCompleted: {
for (var h = 0; h < 24; h++) {
for (var m = 0; m < 60; m += 30) {
var hh = ("0" + h).slice(-2)
var mm = ("0" + m).slice(-2)
var key = hh + ":" + mm
timeOptions.append({
"key": key,
"name": key
})
}
}
}
// Check for wlsunset availability when enabling Night Light
Process {
id: wlsunsetCheck
command: ["which", "wlsunset"]
running: false
onExited: function (exitCode) {
if (exitCode === 0) {
Settings.data.nightLight.enabled = true
NightLightService.apply()
ToastService.showNotice("Night Light", "Enabled")
} else {
Settings.data.nightLight.enabled = false
ToastService.showWarning("Night Light", "wlsunset not installed")
}
}
stdout: StdioCollector {}
stderr: StdioCollector {}
}
spacing: Style.marginL * scaling
// Brightness Step Section
ColumnLayout {
spacing: Style.marginS * scaling
Layout.fillWidth: true
NSpinBox {
Layout.fillWidth: true
label: "Brightness Step Size"
description: "Adjust the step size for brightness changes (scroll wheel, keyboard shortcuts)."
minimum: 1
maximum: 50
value: Settings.data.brightness.brightnessStep
stepSize: 1
suffix: "%"
onValueChanged: {
Settings.data.brightness.brightnessStep = value
}
}
}
// Monitor Overview Section
ColumnLayout {
spacing: Style.marginL * scaling
NLabel {
label: "Monitors Brightness Control"
description: "Current brightness levels for all detected monitors."
}
// Single monitor display using the same data source as the bar icon
Repeater {
model: BrightnessService.monitors
Rectangle {
Layout.fillWidth: true
radius: Style.radiusM * scaling
color: Color.mSurface
border.color: Color.mOutline
border.width: Math.max(1, Style.borderS * scaling)
implicitHeight: contentCol.implicitHeight + Style.marginXL * 2 * scaling
ColumnLayout {
id: contentCol
anchors.fill: parent
anchors.margins: Style.marginL * scaling
spacing: Style.marginM * scaling
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM * scaling
NText {
text: `${model.modelData.name} [${model.modelData.model}]`
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
}
Item {
Layout.fillWidth: true
}
NText {
text: model.method
font.pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignRight
}
}
RowLayout {
Layout.fillWidth: true
spacing: Style.marginM * scaling
NText {
text: "Brightness:"
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurface
}
NSlider {
Layout.fillWidth: true
from: 0
to: 1
value: model.brightness
stepSize: 0.05
onPressedChanged: {
if (!pressed) {
var monitor = BrightnessService.getMonitorForScreen(model.modelData)
monitor.setBrightness(value)
}
}
}
NText {
text: Math.round(model.brightness * 100) + "%"
font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.alignment: Qt.AlignRight
}
}
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
// Night Light Section
ColumnLayout {
spacing: Style.marginXS * scaling
Layout.fillWidth: true
NText {
text: "Night Light"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
}
NText {
text: "Reduce blue light emission to help you sleep better and reduce eye strain."
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
NToggle {
label: "Enable Night Light"
description: "Apply a warm color filter to reduce blue light emission."
checked: Settings.data.nightLight.enabled
onToggled: checked => {
if (checked) {
// Verify wlsunset exists before enabling
wlsunsetCheck.running = true
} else {
Settings.data.nightLight.enabled = false
Settings.data.nightLight.forced = false
NightLightService.apply()
ToastService.showNotice("Night Light", "Disabled")
}
}
}
// Temperature
ColumnLayout {
spacing: Style.marginXS * scaling
Layout.alignment: Qt.AlignVCenter
NLabel {
label: "Color temperature"
description: "Choose two temperatures in Kelvin."
}
RowLayout {
visible: Settings.data.nightLight.enabled
spacing: Style.marginM * scaling
Layout.fillWidth: false
Layout.fillHeight: true
Layout.alignment: Qt.AlignVCenter
NText {
text: "Night"
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
NTextInput {
text: Settings.data.nightLight.nightTemp
inputMethodHints: Qt.ImhDigitsOnly
Layout.alignment: Qt.AlignVCenter
onEditingFinished: {
var nightTemp = parseInt(text)
var dayTemp = parseInt(Settings.data.nightLight.dayTemp)
if (!isNaN(nightTemp) && !isNaN(dayTemp)) {
// Clamp value between [1000 .. (dayTemp-500)]
var clampedValue = Math.min(dayTemp - 500, Math.max(1000, nightTemp))
text = Settings.data.nightLight.nightTemp = clampedValue.toString()
}
}
}
Item {}
NText {
text: "Day"
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.alignment: Qt.AlignVCenter
}
NTextInput {
text: Settings.data.nightLight.dayTemp
inputMethodHints: Qt.ImhDigitsOnly
Layout.alignment: Qt.AlignVCenter
onEditingFinished: {
var dayTemp = parseInt(text)
var nightTemp = parseInt(Settings.data.nightLight.nightTemp)
if (!isNaN(nightTemp) && !isNaN(dayTemp)) {
// Clamp value between [(nightTemp+500) .. 6500]
var clampedValue = Math.max(nightTemp + 500, Math.min(6500, dayTemp))
text = Settings.data.nightLight.dayTemp = clampedValue.toString()
}
}
}
}
}
NToggle {
label: "Automatic Scheduling"
description: `Based on the sunset and sunrise time in <i>${LocationService.stableName}</i> - recommended.`
checked: Settings.data.nightLight.autoSchedule
onToggled: checked => Settings.data.nightLight.autoSchedule = checked
visible: Settings.data.nightLight.enabled
}
// Schedule settings
ColumnLayout {
spacing: Style.marginXS * scaling
visible: Settings.data.nightLight.enabled && !Settings.data.nightLight.autoSchedule && !Settings.data.nightLight.forced
RowLayout {
Layout.fillWidth: false
spacing: Style.marginM * scaling
NLabel {
label: "Manual Scheduling"
}
Item {// add a little more spacing
}
NText {
text: "Sunrise Time"
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
}
NComboBox {
model: timeOptions
currentKey: Settings.data.nightLight.manualSunrise
placeholder: "Select start time"
onSelected: key => Settings.data.nightLight.manualSunrise = key
minimumWidth: 120 * scaling
}
Item {// add a little more spacing
}
NText {
text: "Sunset Time"
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
}
NComboBox {
model: timeOptions
currentKey: Settings.data.nightLight.manualSunset
placeholder: "Select stop time"
onSelected: key => Settings.data.nightLight.manualSunset = key
minimumWidth: 120 * scaling
}
}
}
// Force activation toggle
NToggle {
label: "Force activation"
description: "Immediately apply night temperature without scheduling or fade."
checked: Settings.data.nightLight.forced
onToggled: checked => {
Settings.data.nightLight.forced = checked
if (checked && !Settings.data.nightLight.enabled) {
// Ensure enabled when forcing
wlsunsetCheck.running = true
} else {
NightLightService.apply()
}
}
visible: Settings.data.nightLight.enabled
}
}

View file

@ -10,6 +10,11 @@ ColumnLayout {
id: root
spacing: 0
NHeader {
title: "Color Schemes"
description: "Choose and customize color schemes for your interface."
}
// Cache for scheme JSON (can be flat or {dark, light})
property var schemeColorsCache: ({})
@ -151,19 +156,9 @@ ColumnLayout {
spacing: Style.marginM * scaling
Layout.fillWidth: true
NText {
text: "Predefined Color Schemes"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
}
NText {
text: "To use these color schemes, you must turn off Matugen. With Matugen enabled, colors are automatically generated from your wallpaper."
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.fillWidth: true
wrapMode: Text.WordWrap
NHeader {
title: "Predefined Color Schemes"
description: "To use these color schemes, you must turn off Matugen. With Matugen enabled, colors are automatically generated from your wallpaper."
}
// Color Schemes Grid

View file

@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import Quickshell
import qs.Commons
import qs.Services
import qs.Widgets
@ -10,6 +11,24 @@ ColumnLayout {
spacing: Style.marginL * scaling
width: root.width
NHeader {
title: "Dock Settings"
description: "Configure dock behavior, appearance, and monitor settings."
}
// Helper functions to update arrays immutably
function addMonitor(list, name) {
const arr = (list || []).slice()
if (!arr.includes(name))
arr.push(name)
return arr
}
function removeMonitor(list, name) {
return (list || []).filter(function (n) {
return n !== name
})
}
NToggle {
label: "Auto-hide"
description: "Automatically hide when not in use."
@ -24,12 +43,52 @@ ColumnLayout {
onToggled: checked => Settings.data.dock.exclusive = checked
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
// Monitor Configuration
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NLabel {
label: "Background Opacity"
NHeader {
title: "Monitor Configuration"
description: "Choose which monitors should display the dock."
}
Repeater {
model: Quickshell.screens || []
delegate: NCheckbox {
Layout.fillWidth: true
label: `${modelData.name || "Unknown"}${modelData.model ? `: ${modelData.model}` : ""}`
description: `${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})`
checked: (Settings.data.dock.monitors || []).indexOf(modelData.name) !== -1
onToggled: checked => {
if (checked) {
Settings.data.dock.monitors = addMonitor(Settings.data.dock.monitors, modelData.name)
} else {
Settings.data.dock.monitors = removeMonitor(Settings.data.dock.monitors, modelData.name)
}
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NHeader {
title: "Background Opacity"
description: "Adjust the background opacity."
}
@ -53,12 +112,18 @@ ColumnLayout {
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NLabel {
label: "Dock Floating Distance"
NHeader {
title: "Dock Floating Distance"
description: "Adjust the floating distance from the screen edge."
}

View file

@ -9,6 +9,11 @@ import qs.Widgets
ColumnLayout {
id: root
NHeader {
title: "Profile"
description: "Configure your user profile and avatar settings."
}
// Profile section
RowLayout {
Layout.fillWidth: true
@ -48,12 +53,8 @@ ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "User Interface"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "User Interface"
}
NToggle {
@ -133,12 +134,8 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Screen Corners"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "Screen Corners"
}
NToggle {
@ -187,12 +184,8 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Fonts"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "Fonts"
}
// Font configuration section

View file

@ -10,6 +10,11 @@ ColumnLayout {
spacing: Style.marginL * scaling
width: root.width
NHeader {
title: "System Hooks"
description: "Configure commands to be executed when system events occur."
}
// Enable/Disable Toggle
NToggle {
label: "Enable Hooks"

View file

@ -8,6 +8,11 @@ import qs.Widgets
ColumnLayout {
id: root
NHeader {
title: "Launcher Settings"
description: "Configure the application launcher panel behavior and appearance."
}
ColumnLayout {
spacing: Style.marginL * scaling

View file

@ -11,6 +11,11 @@ ColumnLayout {
id: root
spacing: Style.marginL * scaling
NHeader {
title: "Network Settings"
description: "Configure Wi-Fi and Bluetooth connectivity options."
}
NToggle {
label: "Enable Wi-Fi"
description: "Enable Wi-Fi connectivity."

View file

@ -0,0 +1,182 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import qs.Commons
import qs.Services
import qs.Widgets
ColumnLayout {
id: root
// Helper functions to update arrays immutably
function addMonitor(list, name) {
const arr = (list || []).slice()
if (!arr.includes(name))
arr.push(name)
return arr
}
function removeMonitor(list, name) {
return (list || []).filter(function (n) {
return n !== name
})
}
// General Notification Settings
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NHeader {
title: "General Settings"
}
NToggle {
label: "Do Not Disturb"
description: "Disable all notification popups when enabled."
checked: Settings.data.notifications.doNotDisturb
onToggled: checked => Settings.data.notifications.doNotDisturb = checked
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
// Monitor Configuration
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NHeader {
title: "Monitor Configuration"
description: "Choose which monitors should display notifications."
}
Repeater {
model: Quickshell.screens || []
delegate: NCheckbox {
Layout.fillWidth: true
label: `${modelData.name || "Unknown"}${modelData.model ? `: ${modelData.model}` : ""}`
description: `${modelData.width}x${modelData.height} at (${modelData.x}, ${modelData.y})`
checked: (Settings.data.notifications.monitors || []).indexOf(modelData.name) !== -1
onToggled: checked => {
if (checked) {
Settings.data.notifications.monitors = addMonitor(Settings.data.notifications.monitors, modelData.name)
} else {
Settings.data.notifications.monitors = removeMonitor(Settings.data.notifications.monitors, modelData.name)
}
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
// Notification Duration Settings
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NHeader {
title: "Notification Duration"
description: "Configure how long notifications stay visible based on their urgency level."
}
// Low Urgency Duration
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NLabel {
label: "Low Urgency Duration"
description: "How long low priority notifications stay visible."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.lowUrgencyDuration
onMoved: Settings.data.notifications.lowUrgencyDuration = value
cutoutColor: Color.mSurface
}
NText {
text: Settings.data.notifications.lowUrgencyDuration + "s"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
}
}
// Normal Urgency Duration
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NLabel {
label: "Normal Urgency Duration"
description: "How long normal priority notifications stay visible."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.normalUrgencyDuration
onMoved: Settings.data.notifications.normalUrgencyDuration = value
cutoutColor: Color.mSurface
}
NText {
text: Settings.data.notifications.normalUrgencyDuration + "s"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
}
}
// Critical Urgency Duration
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NLabel {
label: "Critical Urgency Duration"
description: "How long critical priority notifications stay visible."
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.criticalUrgencyDuration
onMoved: Settings.data.notifications.criticalUrgencyDuration = value
cutoutColor: Color.mSurface
}
NText {
text: Settings.data.notifications.criticalUrgencyDuration + "s"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
}
}
}
}

View file

@ -10,6 +10,11 @@ ColumnLayout {
spacing: Style.marginL * scaling
NHeader {
title: "Screen Recorder"
description: "Configure screen recording settings and output options."
}
// Output Directory
ColumnLayout {
spacing: Style.marginS * scaling
@ -53,12 +58,8 @@ ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Video Settings"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "Video Settings"
}
// Source
@ -203,12 +204,8 @@ ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Audio Settings"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
NHeader {
title: "Audio Settings"
}
// Audio Source

View file

@ -12,6 +12,11 @@ ColumnLayout {
spacing: Style.marginL * scaling
NHeader {
title: "Wallpaper Selector"
description: "Browse and select wallpapers from your configured directory."
}
property list<string> wallpapersList: []
property string currentWallpaper: ""
@ -42,11 +47,8 @@ ColumnLayout {
}
// Current wallpaper display
NText {
text: "Current Wallpaper"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
NHeader {
title: "Current Wallpaper"
}
Rectangle {
@ -80,18 +82,9 @@ ColumnLayout {
Layout.fillWidth: true
// Wallpaper grid
NText {
text: "Wallpaper Selector"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
}
NText {
text: "Click on a wallpaper to set it as your current wallpaper."
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
NHeader {
title: "Wallpaper Selector"
description: "Click on a wallpaper to set it as your current wallpaper."
}
}

View file

@ -10,6 +10,11 @@ import qs.Widgets
ColumnLayout {
id: root
NHeader {
title: "Wallpaper Management"
description: "Configure wallpaper settings, automation, and appearance options."
}
NToggle {
label: "Enable Wallpaper Management"
description: "Manage wallpapers with Noctalia. (Uncheck if you prefer using another application)."
@ -89,11 +94,8 @@ ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Look & Feel"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
NHeader {
title: "Look & Feel"
}
// Fill Mode
@ -189,11 +191,8 @@ ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Automation"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
NHeader {
title: "Automation"
}
// Random Wallpaper

View file

@ -8,6 +8,11 @@ import qs.Widgets
ColumnLayout {
id: root
NHeader {
title: "Weather & Location"
description: "Configure weather display and location settings."
}
// Location section
RowLayout {
Layout.fillWidth: true
@ -57,11 +62,8 @@ ColumnLayout {
spacing: Style.marginM * scaling
Layout.fillWidth: true
NText {
text: "Weather"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
NHeader {
title: "Weather"
}
NToggle {

View file

@ -87,9 +87,26 @@ Singleton {
// Maximum visible notifications
property int maxVisible: 5
// Function to get duration based on urgency
function getDurationForUrgency(urgency) {
switch (urgency) {
case 0:
// Low urgency
return (Settings.data.notifications.lowUrgencyDuration || 3) * 1000
case 1:
// Normal urgency
return (Settings.data.notifications.normalUrgencyDuration || 8) * 1000
case 2:
// Critical urgency
return (Settings.data.notifications.criticalUrgencyDuration || 15) * 1000
default:
return (Settings.data.notifications.normalUrgencyDuration || 8) * 1000
}
}
// Auto-hide timer
property Timer hideTimer: Timer {
interval: 8000 // 8 seconds - longer display time
interval: 1000 // Check every second
repeat: true
running: notificationModel.count > 0
@ -98,11 +115,26 @@ Singleton {
return
}
// Remove the oldest notification (last in the list)
let oldestNotification = notificationModel.get(notificationModel.count - 1).rawNotification
if (oldestNotification) {
// Trigger animation signal instead of direct dismiss
animateAndRemove(oldestNotification, notificationModel.count - 1)
// Check each notification for expiration
for (var i = notificationModel.count - 1; i >= 0; i--) {
let notificationData = notificationModel.get(i)
if (notificationData && notificationData.rawNotification) {
let notification = notificationData.rawNotification
let urgency = notificationData.urgency
let timestamp = notificationData.timestamp
// Calculate if this notification should be removed
let duration = getDurationForUrgency(urgency)
let now = new Date()
let elapsed = now.getTime() - timestamp.getTime()
if (elapsed >= duration) {
// Trigger animation signal instead of direct dismiss
animateAndRemove(notification, i)
break
// Only remove one notification per check to avoid conflicts
}
}
}
}
}

33
Widgets/NHeader.qml Normal file
View file

@ -0,0 +1,33 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
ColumnLayout {
id: root
property string title: ""
property string description: ""
property real bottomMargin: Style.marginL * scaling
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NText {
text: root.title
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
visible: root.title !== ""
}
NText {
text: root.description
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
Layout.bottomMargin: root.bottomMargin
visible: root.description !== ""
}
}