Merge branch 'bartab-overhaul'

This commit is contained in:
Ly-sec 2025-09-08 12:21:18 +02:00
commit c02d3e3d22
59 changed files with 1651 additions and 821 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View file

@ -102,7 +102,7 @@ Singleton {
// FileView to load custom colors data from colors.json
FileView {
id: customColorsFile
path: Settings.configDir + "colors.json"
path: Settings.directoriesCreated ? (Settings.configDir + "colors.json") : ""
watchChanges: true
onFileChanged: {
Logger.log("Color", "Reloading colors from disk")
@ -112,6 +112,13 @@ Singleton {
Logger.log("Color", "Writing colors to disk")
writeAdapter()
}
// Trigger initial load when path changes from empty to actual path
onPathChanged: {
if (path === Settings.configDir + "colors.json") {
reload()
}
}
onLoadFailed: function (error) {
if (error.toString().includes("No such file") || error === 2) {
// File doesn't exist, create it with default values

View file

@ -26,11 +26,13 @@ Singleton {
property string defaultWallpapersDirectory: Quickshell.env("HOME") + "/Pictures/Wallpapers"
property string defaultVideosDirectory: Quickshell.env("HOME") + "/Videos"
property string defaultLocation: "Tokyo"
property string defaultWallpaper: Quickshell.shellDir + "/Assets/Wallpaper/noctalia.png"
// Used to access via Settings.data.xxx.yyy
readonly property alias data: adapter
property bool isLoaded: false
property bool directoriesCreated: false
// Signal emitted when settings are loaded after startupcale changes
signal settingsLoaded
@ -71,34 +73,93 @@ Singleton {
// -----------------------------------------------------
// If the settings structure has changed, ensure
// backward compatibility
// backward compatibility by upgrading the settings
function upgradeSettingsData() {
for (var i = 0; i < adapter.bar.widgets.left.length; i++) {
var obj = adapter.bar.widgets.left[i]
if (typeof obj === "string") {
adapter.bar.widgets.left[i] = {
"id": obj
const sections = ["left", "center", "right"]
// -----------------
// 1st. check our settings are not super old, when we only had the widget type as a plain string
for (var s = 0; s < sections.length; s++) {
const sectionName = sections[s]
for (var i = 0; i < adapter.bar.widgets[sectionName].length; i++) {
var widget = adapter.bar.widgets[sectionName][i]
if (typeof widget === "string") {
adapter.bar.widgets[sectionName][i] = {
"id": widget
}
}
}
}
for (var i = 0; i < adapter.bar.widgets.center.length; i++) {
var obj = adapter.bar.widgets.center[i]
if (typeof obj === "string") {
adapter.bar.widgets.center[i] = {
"id": obj
// -----------------
// 2nd. migrate global settings to user settings
for (var s = 0; s < sections.length; s++) {
const sectionName = sections[s]
for (var i = 0; i < adapter.bar.widgets[sectionName].length; i++) {
var widget = adapter.bar.widgets[sectionName][i]
// Check if widget registry supports user settings, if it does not, then there is nothing to do
const reg = BarWidgetRegistry.widgetMetadata[widget.id]
if ((reg === undefined) || (reg.allowUserSettings === undefined) || !reg.allowUserSettings) {
continue
}
}
}
for (var i = 0; i < adapter.bar.widgets.right.length; i++) {
var obj = adapter.bar.widgets.right[i]
if (typeof obj === "string") {
adapter.bar.widgets.right[i] = {
"id": obj
// Check that the widget was not previously migrated and skip if necessary
const keys = Object.keys(widget)
if (keys.length > 1) {
continue
}
migrateWidget(widget)
Logger.log("Settings", JSON.stringify(widget))
}
}
}
// -----------------------------------------------------
function migrateWidget(widget) {
Logger.log("Settings", `Migrating '${widget.id}' widget`)
switch (widget.id) {
case "ActiveWindow":
widget.showIcon = adapter.bar.showActiveWindowIcon
break
case "Battery":
widget.alwaysShowPercentage = adapter.bar.alwaysShowBatteryPercentage
break
case "Brightness":
widget.alwaysShowPercentage = BarWidgetRegistry.widgetMetadata[widget.id].alwaysShowPercentage
break
case "Clock":
widget.showDate = adapter.location.showDateWithClock
widget.use12HourClock = adapter.location.use12HourClock
widget.reverseDayMonth = adapter.location.reverseDayMonth
widget.showSeconds = BarWidgetRegistry.widgetMetadata[widget.id].showSeconds
break
case "MediaMini":
widget.showAlbumArt = adapter.audio.showMiniplayerAlbumArt
widget.showVisualizer = adapter.audio.showMiniplayerCava
widget.visualizerType = BarWidgetRegistry.widgetMetadata[widget.id].visualizerType
break
case "NotificationHistory":
widget.showUnreadBadge = BarWidgetRegistry.widgetMetadata[widget.id].showUnreadBadge
widget.hideWhenZero = BarWidgetRegistry.widgetMetadata[widget.id].hideWhenZero
break
case "SidePanelToggle":
widget.useDistroLogo = adapter.bar.useDistroLogo
break
case "SystemMonitor":
widget.showNetworkStats = adapter.bar.showNetworkStats
break
case "Volume":
widget.alwaysShowPercentage = BarWidgetRegistry.widgetMetadata[widget.id].alwaysShowPercentage
break
case "Workspace":
widget.labelMode = adapter.bar.showWorkspaceLabel
break
}
}
// -----------------------------------------------------
// Kickoff essential services
function kickOffServices() {
@ -117,14 +178,15 @@ Singleton {
}
// -----------------------------------------------------
Item {
Component.onCompleted: {
// Ensure directories exist before FileView tries to read files
Component.onCompleted: {
// ensure settings dir exists
Quickshell.execDetached(["mkdir", "-p", configDir])
Quickshell.execDetached(["mkdir", "-p", cacheDir])
Quickshell.execDetached(["mkdir", "-p", cacheDirImages])
// ensure settings dir exists
Quickshell.execDetached(["mkdir", "-p", configDir])
Quickshell.execDetached(["mkdir", "-p", cacheDir])
Quickshell.execDetached(["mkdir", "-p", cacheDirImages])
}
// Mark directories as created and trigger file loading
directoriesCreated = true
}
// Don't write settings to disk immediately
@ -138,12 +200,16 @@ Singleton {
FileView {
id: settingsFileView
path: settingsFile
path: directoriesCreated ? settingsFile : ""
watchChanges: true
onFileChanged: reload()
onAdapterUpdated: saveTimer.start()
Component.onCompleted: function () {
reload()
// Trigger initial load when path changes from empty to actual path
onPathChanged: {
if (path === settingsFile) {
reload()
}
}
onLoaded: function () {
if (!isLoaded) {
@ -174,15 +240,16 @@ Singleton {
// bar
property JsonObject bar: JsonObject {
property string position: "top" // Possible values: "top", "bottom"
property bool showActiveWindowIcon: true
property bool alwaysShowBatteryPercentage: false
property bool showNetworkStats: false
property string position: "top" // "top" or "bottom"
property real backgroundOpacity: 1.0
property bool useDistroLogo: false
property string showWorkspaceLabel: "none"
property list<string> monitors: []
property bool showActiveWindowIcon: true // TODO: delete
property bool alwaysShowBatteryPercentage: false // TODO: delete
property bool showNetworkStats: false // TODO: delete
property bool useDistroLogo: false // TODO: delete
property string showWorkspaceLabel: "none" // TODO: delete
// Widget configuration for modular bar system
property JsonObject widgets
widgets: JsonObject {
@ -236,9 +303,10 @@ Singleton {
property JsonObject location: JsonObject {
property string name: defaultLocation
property bool useFahrenheit: false
property bool reverseDayMonth: false
property bool use12HourClock: false
property bool showDateWithClock: false
property bool reverseDayMonth: false // TODO: delete
property bool use12HourClock: false // TODO: delete
property bool showDateWithClock: false // TODO: delete
}
// screen recorder
@ -267,6 +335,7 @@ Singleton {
property int transitionDuration: 1500 // 1500 ms
property string transitionType: "random"
property real transitionEdgeSmoothness: 0.05
property string defaultWallpaper: root.defaultWallpaper
property list<var> monitors: []
}
@ -299,25 +368,27 @@ Singleton {
property JsonObject notifications: JsonObject {
property bool doNotDisturb: false
property list<string> monitors: []
// Last time the user opened the notification history (ms since epoch)
property real lastSeenTs: 0
}
// audio
property JsonObject audio: JsonObject {
property bool showMiniplayerAlbumArt: false
property bool showMiniplayerCava: false
property string visualizerType: "linear"
property int volumeStep: 5
property int cavaFrameRate: 60
// MPRIS controls
property string visualizerType: "linear"
property list<string> mprisBlacklist: []
property string preferredPlayer: ""
property bool showMiniplayerAlbumArt: false // TODO: delete
property bool showMiniplayerCava: false // TODO: delete
}
// ui
property JsonObject ui: JsonObject {
property string fontDefault: "Roboto" // Default font for all text
property string fontFixed: "DejaVu Sans Mono" // Fixed width font for terminal
property string fontBillboard: "Inter" // Large bold font for clocks and prominent displays
property string fontDefault: "Roboto"
property string fontFixed: "DejaVu Sans Mono"
property string fontBillboard: "Inter"
property list<var> monitorsScaling: []
property bool idleInhibitorEnabled: false
}

View file

@ -9,52 +9,38 @@ Singleton {
id: root
property var date: new Date()
property string time: {
let timeFormat = Settings.data.location.use12HourClock ? "h:mm AP" : "HH:mm"
let timeString = Qt.formatDateTime(date, timeFormat)
if (Settings.data.location.showDateWithClock) {
let dayName = date.toLocaleDateString(Qt.locale(), "ddd")
dayName = dayName.charAt(0).toUpperCase() + dayName.slice(1)
let day = date.getDate()
let month = date.toLocaleDateString(Qt.locale(), "MMM")
return timeString + " - " + (Settings.data.location.reverseDayMonth ? `${dayName}, ${month} ${day}` : `${dayName}, ${day} ${month}`)
}
return timeString
// Returns a Unix Timestamp (in seconds)
readonly property int timestamp: {
return Math.floor(date / 1000)
}
readonly property string dateString: {
function formatDate(reverseDayMonth = true) {
let now = date
let dayName = now.toLocaleDateString(Qt.locale(), "ddd")
dayName = dayName.charAt(0).toUpperCase() + dayName.slice(1)
let day = now.getDate()
let suffix
if (day > 3 && day < 21)
suffix = 'th'
suffix = 'th'
else
switch (day % 10) {
switch (day % 10) {
case 1:
suffix = "st"
break
suffix = "st"
break
case 2:
suffix = "nd"
break
suffix = "nd"
break
case 3:
suffix = "rd"
break
suffix = "rd"
break
default:
suffix = "th"
}
suffix = "th"
}
let month = now.toLocaleDateString(Qt.locale(), "MMMM")
let year = now.toLocaleDateString(Qt.locale(), "yyyy")
return `${dayName}, `
+ (Settings.data.location.reverseDayMonth ? `${month} ${day}${suffix} ${year}` : `${day}${suffix} ${month} ${year}`)
}
// Returns a Unix Timestamp (in seconds)
readonly property int timestamp: {
return Math.floor(date / 1000)
return `${dayName}, ` + (reverseDayMonth ? `${month} ${day}${suffix} ${year}` : `${day}${suffix} ${month} ${year}`)
}

View file

@ -3,6 +3,8 @@ import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Services
import qs.Modules.SettingsPanel
import qs.Widgets
Variants {
id: backgroundVariants
@ -20,6 +22,8 @@ Variants {
// Internal state management
property string transitionType: "fade"
property real transitionProgress: 0
// Scaling support for widgets that rely on it
property real scaling: ScalingService.getScreenScale(screen)
readonly property real edgeSmoothness: Settings.data.wallpaper.transitionEdgeSmoothness
readonly property var allTransitions: WallpaperService.allTransitions
@ -87,6 +91,15 @@ Variants {
left: true
}
Connections {
target: ScalingService
function onScaleChanged(screenName, scale) {
if ((screen !== null) && (screenName === screen.name)) {
scaling = scale
}
}
}
Timer {
id: debounceTimer
interval: 333

View file

@ -76,6 +76,7 @@ Variants {
widgetProps: {
"screen": root.modelData || null,
"scaling": ScalingService.getScreenScale(screen),
"widgetId": modelData.id,
"barSection": parent.objectName,
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.left.length
@ -103,6 +104,7 @@ Variants {
widgetProps: {
"screen": root.modelData || null,
"scaling": ScalingService.getScreenScale(screen),
"widgetId": modelData.id,
"barSection": parent.objectName,
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.center.length
@ -131,6 +133,7 @@ Variants {
widgetProps: {
"screen": root.modelData || null,
"scaling": ScalingService.getScreenScale(screen),
"widgetId": modelData.id,
"barSection": parent.objectName,
"sectionWidgetIndex": index,
"sectionWidgetsCount": Settings.data.bar.widgets.right.length

View file

@ -12,6 +12,27 @@ RowLayout {
id: root
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool showIcon: (widgetSettings.showIcon !== undefined) ? widgetSettings.showIcon : widgetMetadata.showIcon
readonly property real minWidth: 160
readonly property real maxWidth: 400
Layout.alignment: Qt.AlignVCenter
@ -74,7 +95,7 @@ RowLayout {
Layout.preferredWidth: Style.fontSizeL * scaling * 1.2
Layout.preferredHeight: Style.fontSizeL * scaling * 1.2
Layout.alignment: Qt.AlignVCenter
visible: getTitle() !== "" && Settings.data.bar.showActiveWindowIcon
visible: getTitle() !== "" && showIcon
IconImage {
id: windowIcon

View file

@ -11,11 +11,42 @@ Item {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: 0
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
// Track if we've already notified to avoid spam
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
// Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property bool alwaysShowPercentage: widgetSettings.alwaysShowPercentage
!== undefined ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
readonly property real warningThreshold: widgetSettings.warningThreshold
!== undefined ? widgetSettings.warningThreshold : widgetMetadata.warningThreshold
// Test mode
readonly property bool testMode: false
readonly property int testPercent: 50
readonly property bool testCharging: true
// Main properties
readonly property var battery: UPower.displayDevice
readonly property bool isReady: testMode ? true : (battery && battery.ready && battery.isLaptopBattery
&& battery.isPresent)
readonly property real percent: testMode ? testPercent : (isReady ? (battery.percentage * 100) : 0)
readonly property bool charging: testMode ? testCharging : (isReady ? battery.state === UPowerDeviceState.Charging : false)
property bool hasNotifiedLowBattery: false
implicitWidth: pill.width
@ -23,15 +54,14 @@ Item {
// Helper to evaluate and possibly notify
function maybeNotify(percent, charging) {
const p = Math.round(percent)
// Only notify exactly at 15%, not at 0% or any other percentage
if (!charging && p === 15 && !root.hasNotifiedLowBattery) {
// Only notify once we are a below threshold
if (!charging && !root.hasNotifiedLowBattery && percent <= warningThreshold) {
root.hasNotifiedLowBattery = true
// Maybe go with toast ?
Quickshell.execDetached(
["notify-send", "-u", "critical", "-i", "battery-caution", "Low Battery", `Battery is at ${p}%. Please connect charger.`])
root.hasNotifiedLowBattery = true
}
// Reset when charging starts or when battery recovers above 20%
if (charging || p > 20) {
} else if (root.hasNotifiedLowBattery && (charging || percent > warningThreshold + 5)) {
// Reset when charging starts or when battery recovers 5% above threshold
root.hasNotifiedLowBattery = false
}
}
@ -40,19 +70,10 @@ Item {
Connections {
target: UPower.displayDevice
function onPercentageChanged() {
let battery = UPower.displayDevice
let isReady = battery && battery.ready && battery.isLaptopBattery && battery.isPresent
let percent = isReady ? (battery.percentage * 100) : 0
let charging = isReady ? battery.state === UPowerDeviceState.Charging : false
root.maybeNotify(percent, charging)
}
function onStateChanged() {
let battery = UPower.displayDevice
let isReady = battery && battery.ready && battery.isLaptopBattery && battery.isPresent
let charging = isReady ? battery.state === UPowerDeviceState.Charging : false
// Reset notification flag when charging starts
if (charging) {
root.hasNotifiedLowBattery = false
@ -63,15 +84,6 @@ Item {
NPill {
id: pill
// Test mode
property bool testMode: false
property int testPercent: 50
property bool testCharging: true
property var battery: UPower.displayDevice
property bool isReady: testMode ? true : (battery && battery.ready && battery.isLaptopBattery && battery.isPresent)
property real percent: testMode ? testPercent : (isReady ? (battery.percentage * 100) : 0)
property bool charging: testMode ? testCharging : (isReady ? battery.state === UPowerDeviceState.Charging : false)
rightOpen: BarWidgetRegistry.getNPillDirection(root)
icon: testMode ? BatteryService.getIcon(testPercent, testCharging, true) : BatteryService.getIcon(percent,
charging, isReady)
@ -81,7 +93,7 @@ Item {
iconCircleColor: Color.mPrimary
collapsedIconColor: Color.mOnSurface
autoHide: false
forceOpen: isReady && (testMode || battery.isLaptopBattery) && Settings.data.bar.alwaysShowBatteryPercentage
forceOpen: isReady && (testMode || battery.isLaptopBattery) && alwaysShowPercentage
disableOpen: (!isReady || (!testMode && !battery.isLaptopBattery))
tooltipText: {
let lines = []

View file

@ -10,10 +10,28 @@ Item {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: 0
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool userAlwaysShowPercentage: (widgetSettings.alwaysShowPercentage
!== undefined) ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
// Used to avoid opening the pill on Quickshell startup
property bool firstBrightnessReceived: false
@ -69,6 +87,7 @@ Item {
var monitor = getMonitor()
return monitor ? (Math.round(monitor.brightness * 100) + "%") : ""
}
forceOpen: userAlwaysShowPercentage
tooltipText: {
var monitor = getMonitor()
if (!monitor)

View file

@ -10,24 +10,70 @@ Rectangle {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
// Resolve settings: try user settings or defaults from BarWidgetRegistry
readonly property bool showDate: widgetSettings.showDate !== undefined ? widgetSettings.showDate : widgetMetadata.showDate
readonly property bool use12h: widgetSettings.use12HourClock !== undefined ? widgetSettings.use12HourClock : widgetMetadata.use12HourClock
readonly property bool showSeconds: widgetSettings.showSeconds !== undefined ? widgetSettings.showSeconds : widgetMetadata.showSeconds
readonly property bool reverseDayMonth: widgetSettings.reverseDayMonth
!== undefined ? widgetSettings.reverseDayMonth : widgetMetadata.reverseDayMonth
implicitWidth: clock.width + Style.marginM * 2 * scaling
implicitHeight: Math.round(Style.capsuleHeight * scaling)
radius: Math.round(Style.radiusM * scaling)
color: Color.mSurfaceVariant
// Clock Icon with attached calendar
NClock {
NText {
id: clock
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
text: {
const now = Time.date
const timeFormat = use12h ? (showSeconds ? "h:mm:ss AP" : "h:mm AP") : (showSeconds ? "HH:mm:ss" : "HH:mm")
const timeString = Qt.formatDateTime(now, timeFormat)
NTooltip {
id: tooltip
text: `${Time.dateString}.`
target: clock
positionAbove: Settings.data.bar.position === "bottom"
if (showDate) {
let dayName = now.toLocaleDateString(Qt.locale(), "ddd")
dayName = dayName.charAt(0).toUpperCase() + dayName.slice(1)
let day = now.getDate()
let month = now.toLocaleDateString(Qt.locale(), "MMM")
return timeString + " - " + (reverseDayMonth ? `${dayName}, ${month} ${day}` : `${dayName}, ${day} ${month}`)
}
return timeString
}
anchors.centerIn: parent
font.pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightBold
}
NTooltip {
id: tooltip
text: `${Time.formatDate(reverseDayMonth)}.`
target: clock
positionAbove: Settings.data.bar.position === "bottom"
}
MouseArea {
id: clockMouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: {
if (!PanelService.getPanel("calendarPanel")?.active) {
tooltip.show()

View file

@ -13,11 +13,13 @@ NIconButton {
property var screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
// Get user settings from Settings data
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
@ -30,30 +32,27 @@ NIconButton {
}
// Use settings or defaults from BarWidgetRegistry
readonly property string userIcon: widgetSettings.icon || BarWidgetRegistry.widgetMetadata["CustomButton"].icon
readonly property string userLeftClickExec: widgetSettings.leftClickExec
|| BarWidgetRegistry.widgetMetadata["CustomButton"].leftClickExec
readonly property string userRightClickExec: widgetSettings.rightClickExec
|| BarWidgetRegistry.widgetMetadata["CustomButton"].rightClickExec
readonly property string userMiddleClickExec: widgetSettings.middleClickExec
|| BarWidgetRegistry.widgetMetadata["CustomButton"].middleClickExec
readonly property bool hasExec: (userLeftClickExec || userRightClickExec || userMiddleClickExec)
readonly property string customIcon: widgetSettings.icon || widgetMetadata.icon
readonly property string leftClickExec: widgetSettings.leftClickExec || widgetMetadata.leftClickExec
readonly property string rightClickExec: widgetSettings.rightClickExec || widgetMetadata.rightClickExec
readonly property string middleClickExec: widgetSettings.middleClickExec || widgetMetadata.middleClickExec
readonly property bool hasExec: (leftClickExec || rightClickExec || middleClickExec)
sizeRatio: 0.8
icon: userIcon
icon: customIcon
tooltipText: {
if (!hasExec) {
return "Custom Button - Configure in settings"
} else {
var lines = []
if (userLeftClickExec !== "") {
lines.push(`Left click: <i>${userLeftClickExec}</i>.`)
if (leftClickExec !== "") {
lines.push(`Left click: <i>${leftClickExec}</i>.`)
}
if (userRightClickExec !== "") {
lines.push(`Right click: <i>${userRightClickExec}</i>.`)
if (rightClickExec !== "") {
lines.push(`Right click: <i>${rightClickExec}</i>.`)
}
if (userMiddleClickExec !== "") {
lines.push(`Middle click: <i>${userMiddleClickExec}</i>.`)
if (middleClickExec !== "") {
lines.push(`Middle click: <i>${middleClickExec}</i>.`)
}
return lines.join("<br/>")
}
@ -61,9 +60,9 @@ NIconButton {
opacity: hasExec ? Style.opacityFull : Style.opacityMedium
onClicked: {
if (userLeftClickExec) {
Quickshell.execDetached(["sh", "-c", userLeftClickExec])
Logger.log("CustomButton", `Executing command: ${userLeftClickExec}`)
if (leftClickExec) {
Quickshell.execDetached(["sh", "-c", leftClickExec])
Logger.log("CustomButton", `Executing command: ${leftClickExec}`)
} else if (!hasExec) {
// No script was defined, open settings
var settingsPanel = PanelService.getPanel("settingsPanel")
@ -73,16 +72,16 @@ NIconButton {
}
onRightClicked: {
if (userRightClickExec) {
Quickshell.execDetached(["sh", "-c", userRightClickExec])
Logger.log("CustomButton", `Executing command: ${userRightClickExec}`)
if (rightClickExec) {
Quickshell.execDetached(["sh", "-c", rightClickExec])
Logger.log("CustomButton", `Executing command: ${rightClickExec}`)
}
}
onMiddleClicked: {
if (userMiddleClickExec) {
Quickshell.execDetached(["sh", "-c", userMiddleClickExec])
Logger.log("CustomButton", `Executing command: ${userMiddleClickExec}`)
if (middleClickExec) {
Quickshell.execDetached(["sh", "-c", middleClickExec])
Logger.log("CustomButton", `Executing command: ${middleClickExec}`)
}
}
}

View file

@ -12,9 +12,6 @@ Item {
property ShellScreen screen
property real scaling: 1.0
property string barSection: ""
property int sectionWidgetIndex: 0
property int sectionWidgetsCount: 0
// Use the shared service for keyboard layout
property string currentLayout: KeyboardLayoutService.currentLayout

View file

@ -12,18 +12,44 @@ RowLayout {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool showAlbumArt: (widgetSettings.showAlbumArt
!== undefined) ? widgetSettings.showAlbumArt : widgetMetadata.showAlbumArt
readonly property bool showVisualizer: (widgetSettings.showVisualizer
!== undefined) ? widgetSettings.showVisualizer : widgetMetadata.showVisualizer
readonly property string visualizerType: (widgetSettings.visualizerType !== undefined && widgetSettings.visualizerType
!== "") ? widgetSettings.visualizerType : widgetMetadata.visualizerType
readonly property real minWidth: 160
readonly property real maxWidth: 400
function getTitle() {
return MediaService.trackTitle + (MediaService.trackArtist !== "" ? ` - ${MediaService.trackArtist}` : "")
}
Layout.alignment: Qt.AlignVCenter
spacing: Style.marginS * scaling
visible: MediaService.currentPlayer !== null && MediaService.canPlay
Layout.preferredWidth: MediaService.currentPlayer !== null && MediaService.canPlay ? implicitWidth : 0
function getTitle() {
return MediaService.trackTitle + (MediaService.trackArtist !== "" ? ` - ${MediaService.trackArtist}` : "")
}
// A hidden text element to safely measure the full title width
NText {
id: fullTitleMetrics
@ -58,8 +84,7 @@ RowLayout {
Loader {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
active: Settings.data.audio.showMiniplayerCava && Settings.data.audio.visualizerType == "linear"
&& MediaService.isPlaying
active: showVisualizer && visualizerType == "linear" && MediaService.isPlaying
z: 0
sourceComponent: LinearSpectrum {
@ -74,8 +99,7 @@ RowLayout {
Loader {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
active: Settings.data.audio.showMiniplayerCava && Settings.data.audio.visualizerType == "mirrored"
&& MediaService.isPlaying
active: showVisualizer && visualizerType == "mirrored" && MediaService.isPlaying
z: 0
sourceComponent: MirroredSpectrum {
@ -90,8 +114,7 @@ RowLayout {
Loader {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
active: Settings.data.audio.showMiniplayerCava && Settings.data.audio.visualizerType == "wave"
&& MediaService.isPlaying
active: showVisualizer && visualizerType == "wave" && MediaService.isPlaying
z: 0
sourceComponent: WaveSpectrum {
@ -115,12 +138,12 @@ RowLayout {
font.pointSize: Style.fontSizeL * scaling
verticalAlignment: Text.AlignVCenter
Layout.alignment: Qt.AlignVCenter
visible: !Settings.data.audio.showMiniplayerAlbumArt && getTitle() !== "" && !trackArt.visible
visible: !showAlbumArt && getTitle() !== "" && !trackArt.visible
}
ColumnLayout {
Layout.alignment: Qt.AlignVCenter
visible: Settings.data.audio.showMiniplayerAlbumArt
visible: showAlbumArt
spacing: 0
Item {

View file

@ -12,10 +12,28 @@ Item {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: 0
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool alwaysShowPercentage: (widgetSettings.alwaysShowPercentage
!== undefined) ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
// Used to avoid opening the pill on Quickshell startup
property bool firstInputVolumeReceived: false
property int wheelAccumulator: 0
@ -78,6 +96,7 @@ Item {
collapsedIconColor: Color.mOnSurface
autoHide: false // Important to be false so we can hover as long as we want
text: Math.floor(AudioService.inputVolume * 100) + "%"
forceOpen: alwaysShowPercentage
tooltipText: "Microphone: " + Math.round(AudioService.inputVolume * 100)
+ "%\nLeft click for advanced settings.\nScroll up/down to change volume.\nRight click to toggle mute."

View file

@ -4,6 +4,7 @@ import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
import qs.Commons
import qs.Modules.SettingsPanel
import qs.Services
import qs.Widgets
@ -14,7 +15,6 @@ NIconButton {
property real scaling: 1.0
sizeRatio: 0.8
colorBg: Color.mSurfaceVariant
colorFg: Color.mOnSurface
colorBorder: Color.transparent
@ -26,7 +26,7 @@ NIconButton {
onRightClicked: {
var settingsPanel = PanelService.getPanel("settingsPanel")
settingsPanel.requestedTab = SettingsPanel.Tab.Display
settingsPanel.requestedTab = SettingsPanel.Tab.Brightness
settingsPanel.open(screen)
}
}

View file

@ -13,6 +13,45 @@ NIconButton {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool showUnreadBadge: (widgetSettings.showUnreadBadge
!== undefined) ? widgetSettings.showUnreadBadge : widgetMetadata.showUnreadBadge
readonly property bool hideWhenZero: (widgetSettings.hideWhenZero
!== undefined) ? widgetSettings.hideWhenZero : widgetMetadata.hideWhenZero
function lastSeenTs() {
return Settings.data.notifications?.lastSeenTs || 0
}
function computeUnreadCount() {
var since = lastSeenTs()
var count = 0
var model = NotificationService.historyModel
for (var i = 0; i < model.count; i++) {
var item = model.get(i)
var ts = item.timestamp instanceof Date ? item.timestamp.getTime() : item.timestamp
if (ts > since)
count++
}
return count
}
sizeRatio: 0.8
icon: Settings.data.notifications.doNotDisturb ? "notifications_off" : "notifications"
tooltipText: Settings.data.notifications.doNotDisturb ? "Notification history.\nRight-click to disable 'Do Not Disturb'." : "Notification history.\nRight-click to enable 'Do Not Disturb'."
@ -21,7 +60,40 @@ NIconButton {
colorBorder: Color.transparent
colorBorderHover: Color.transparent
onClicked: PanelService.getPanel("notificationHistoryPanel")?.toggle(screen, this)
onClicked: {
var panel = PanelService.getPanel("notificationHistoryPanel")
panel?.toggle(screen, this)
Settings.data.notifications.lastSeenTs = Time.timestamp * 1000
}
onRightClicked: Settings.data.notifications.doNotDisturb = !Settings.data.notifications.doNotDisturb
Loader {
anchors.right: parent.right
anchors.top: parent.top
anchors.rightMargin: -4 * scaling
anchors.topMargin: -4 * scaling
z: 2
active: showUnreadBadge && (!hideWhenZero || computeUnreadCount() > 0)
sourceComponent: Rectangle {
id: badge
readonly property int count: computeUnreadCount()
readonly property string label: count <= 99 ? String(count) : "99+"
readonly property real pad: 8 * scaling
height: 16 * scaling
width: Math.max(height, textNode.implicitWidth + pad)
radius: height / 2
color: Color.mError
border.color: Color.mSurface
border.width: 1
visible: count > 0 || !hideWhenZero
NText {
id: textNode
anchors.centerIn: parent
text: badge.label
font.pointSize: Style.fontSizeXXS * scaling
color: Color.mOnError
}
}
}
}

View file

@ -1,3 +1,4 @@
import QtQuick
import Quickshell
import Quickshell.Widgets
import QtQuick.Effects
@ -11,7 +12,28 @@ NIconButton {
property ShellScreen screen
property real scaling: 1.0
icon: Settings.data.bar.useDistroLogo ? "" : "widgets"
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool useDistroLogo: (widgetSettings.useDistroLogo
!== undefined) ? widgetSettings.useDistroLogo : widgetMetadata.useDistroLogo
icon: useDistroLogo ? "" : "widgets"
tooltipText: "Open side panel."
sizeRatio: 0.8
@ -24,14 +46,13 @@ NIconButton {
onClicked: PanelService.getPanel("sidePanel")?.toggle(screen, this)
onRightClicked: PanelService.getPanel("settingsPanel")?.toggle(screen)
// When enabled, draw the distro logo instead of the icon glyph
IconImage {
id: logo
anchors.centerIn: parent
width: root.width * 0.6
height: width
source: Settings.data.bar.useDistroLogo ? DistroLogoService.osLogo : ""
visible: false //Settings.data.bar.useDistroLogo && source !== ""
source: useDistroLogo ? DistroLogoService.osLogo : ""
visible: useDistroLogo && source !== ""
smooth: true
}

View file

@ -12,11 +12,13 @@ Item {
property var screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
// Get user settings from Settings data - make it reactive
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
@ -29,19 +31,10 @@ Item {
}
// Use settings or defaults from BarWidgetRegistry
readonly property int userWidth: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex].width || BarWidgetRegistry.widgetMetadata["Spacer"].width
}
}
return BarWidgetRegistry.widgetMetadata["Spacer"].width
}
readonly property int spacerWidth: widgetSettings.width !== undefined ? widgetSettings.width : widgetMetadata.width
// Set the width based on user settings
implicitWidth: userWidth * scaling
implicitWidth: spacerWidth * scaling
implicitHeight: Style.barHeight * scaling
width: implicitWidth
height: implicitHeight
@ -51,6 +44,6 @@ Item {
anchors.fill: parent
color: Qt.rgba(1, 0, 0, 0.1) // Very subtle red tint
visible: Settings.data.general.debugMode || false
radius: 2 * scaling
radius: Style.radiusXXS * scaling
}
}

View file

@ -11,6 +11,34 @@ RowLayout {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool showCpuUsage: (widgetSettings.showCpuUsage
!== undefined) ? widgetSettings.showCpuUsage : widgetMetadata.showCpuUsage
readonly property bool showCpuTemp: (widgetSettings.showCpuTemp !== undefined) ? widgetSettings.showCpuTemp : widgetMetadata.showCpuTemp
readonly property bool showMemoryUsage: (widgetSettings.showMemoryUsage
!== undefined) ? widgetSettings.showMemoryUsage : widgetMetadata.showMemoryUsage
readonly property bool showMemoryAsPercent: (widgetSettings.showMemoryAsPercent
!== undefined) ? widgetSettings.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent
readonly property bool showNetworkStats: (widgetSettings.showNetworkStats
!== undefined) ? widgetSettings.showNetworkStats : widgetMetadata.showNetworkStats
Layout.alignment: Qt.AlignVCenter
spacing: Style.marginS * scaling
@ -34,6 +62,7 @@ RowLayout {
id: cpuUsageLayout
spacing: Style.marginXS * scaling
Layout.alignment: Qt.AlignVCenter
visible: showCpuUsage
NIcon {
id: cpuUsageIcon
@ -59,6 +88,7 @@ RowLayout {
// spacing is thin here to compensate for the vertical thermometer icon
spacing: Style.marginXXS * scaling
Layout.alignment: Qt.AlignVCenter
visible: showCpuTemp
NIcon {
text: "thermometer"
@ -81,6 +111,7 @@ RowLayout {
id: memoryUsageLayout
spacing: Style.marginXS * scaling
Layout.alignment: Qt.AlignVCenter
visible: showMemoryUsage
NIcon {
text: "memory"
@ -88,7 +119,7 @@ RowLayout {
}
NText {
text: `${SystemStatService.memGb}G`
text: showMemoryAsPercent ? `${SystemStatService.memPercent}%` : `${SystemStatService.memGb}G`
font.family: Settings.data.ui.fontFixed
font.pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightMedium
@ -103,7 +134,7 @@ RowLayout {
id: networkDownloadLayout
spacing: Style.marginXS * scaling
Layout.alignment: Qt.AlignVCenter
visible: Settings.data.bar.showNetworkStats
visible: showNetworkStats
NIcon {
text: "download"
@ -126,7 +157,7 @@ RowLayout {
id: networkUploadLayout
spacing: Style.marginXS * scaling
Layout.alignment: Qt.AlignVCenter
visible: Settings.data.bar.showNetworkStats
visible: showNetworkStats
NIcon {
text: "upload"

View file

@ -15,6 +15,7 @@ Rectangle {
property ShellScreen screen
property real scaling: 1.0
readonly property real itemSize: 24 * scaling
function onLoaded() {

View file

@ -12,10 +12,28 @@ Item {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: 0
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property bool alwaysShowPercentage: (widgetSettings.alwaysShowPercentage
!== undefined) ? widgetSettings.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
// Used to avoid opening the pill on Quickshell startup
property bool firstVolumeReceived: false
property int wheelAccumulator: 0
@ -63,6 +81,7 @@ Item {
collapsedIconColor: Color.mOnSurface
autoHide: false // Important to be false so we can hover as long as we want
text: Math.floor(AudioService.volume * 100) + "%"
forceOpen: alwaysShowPercentage
tooltipText: "Volume: " + Math.round(AudioService.volume * 100)
+ "%\nLeft click for advanced settings.\nScroll up/down to change volume.\nRight click to toggle mute."

View file

@ -14,6 +14,26 @@ Item {
property ShellScreen screen
property real scaling: 1.0
// Widget properties passed from Bar.qml for per-instance settings
property string widgetId: ""
property string barSection: ""
property int sectionWidgetIndex: -1
property int sectionWidgetsCount: 0
property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId]
property var widgetSettings: {
var section = barSection.replace("Section", "").toLowerCase()
if (section && sectionWidgetIndex >= 0) {
var widgets = Settings.data.bar.widgets[section]
if (widgets && sectionWidgetIndex < widgets.length) {
return widgets[sectionWidgetIndex]
}
}
return {}
}
readonly property string labelMode: (widgetSettings.labelMode !== undefined) ? widgetSettings.labelMode : widgetMetadata.labelMode
property bool isDestroying: false
property bool hovered: false
@ -22,8 +42,8 @@ Item {
property bool effectsActive: false
property color effectColor: Color.mPrimary
property int horizontalPadding: Math.round(16 * scaling)
property int spacingBetweenPills: Math.round(8 * scaling)
property int horizontalPadding: Math.round(Style.marginS * scaling)
property int spacingBetweenPills: Math.round(Style.marginXS * scaling)
signal workspaceChanged(int workspaceId, color accentColor)
@ -124,7 +144,7 @@ Item {
Rectangle {
id: workspaceBackground
width: parent.width - Style.marginS * scaling * 2
width: parent.width
height: Math.round(Style.capsuleHeight * scaling)
radius: Math.round(Style.radiusM * scaling)
@ -145,7 +165,7 @@ Item {
model: localWorkspaces
Item {
id: workspacePillContainer
height: (Settings.data.bar.showWorkspaceLabel !== "none") ? Math.round(18 * scaling) : Math.round(14 * scaling)
height: (labelMode !== "none") ? Math.round(18 * scaling) : Math.round(14 * scaling)
width: root.calculatedWsWidth(model)
Rectangle {
@ -153,15 +173,13 @@ Item {
anchors.fill: parent
Loader {
active: (Settings.data.bar.showWorkspaceLabel !== "none")
active: (labelMode !== "none")
sourceComponent: Component {
Text {
// Center horizontally
x: (pill.width - width) / 2
// Center vertically accounting for font metrics
y: (pill.height - height) / 2 + (height - contentHeight) / 2
text: {
if (Settings.data.bar.showWorkspaceLabel === "name" && model.name && model.name.length > 0) {
if (labelMode === "name" && model.name && model.name.length > 0) {
return model.name.substring(0, 2)
} else {
return model.idx.toString()

View file

@ -39,7 +39,7 @@ NBox {
const totalSum = JSON.stringify(widget).split('').reduce((acc, character) => {
return acc + character.charCodeAt(0)
}, 0)
switch (totalSum % 10) {
switch (totalSum % 5) {
case 0:
return Color.mPrimary
case 1:
@ -50,16 +50,6 @@ NBox {
return Color.mError
case 4:
return Color.mOnSurface
case 5:
return Qt.darker(Color.mPrimary, 1.3)
case 6:
return Qt.darker(Color.mSecondary, 1.3)
case 7:
return Qt.darker(Color.mTertiary, 1.3)
case 8:
return Qt.darker(Color.mError, 1.3)
case 9:
return Qt.darker(Color.mOnSurface, 1.3)
}
}
@ -75,7 +65,7 @@ NBox {
text: sectionName + " Section"
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
color: Color.mOnSurface
Layout.alignment: Qt.AlignVCenter
}
@ -89,7 +79,7 @@ NBox {
description: ""
placeholder: "Select a widget to add..."
onSelected: key => comboBox.currentKey = key
popupHeight: 240 * scaling
popupHeight: 340 * scaling
Layout.alignment: Qt.AlignVCenter
}
@ -188,13 +178,33 @@ NBox {
colorBgHover: Qt.alpha(Color.mOnPrimary, Style.opacityLight)
colorFgHover: Color.mOnPrimary
onClicked: {
var dialog = Qt.createComponent("BarWidgetSettingsDialog.qml").createObject(root, {
"widgetIndex": index,
"widgetData": modelData,
"widgetId": modelData.id,
"parent": Overlay.overlay
})
dialog.open()
var component = Qt.createComponent(Qt.resolvedUrl("BarWidgetSettingsDialog.qml"))
function instantiateAndOpen() {
var dialog = component.createObject(root, {
"widgetIndex": index,
"widgetData": modelData,
"widgetId": modelData.id,
"parent": Overlay.overlay
})
if (dialog) {
dialog.open()
} else {
Logger.error("BarSectionEditor", "Failed to create settings dialog instance")
}
}
if (component.status === Component.Ready) {
instantiateAndOpen()
} else if (component.status === Component.Error) {
Logger.error("BarSectionEditor", component.errorString())
} else {
component.statusChanged.connect(function () {
if (component.status === Component.Ready) {
instantiateAndOpen()
} else if (component.status === Component.Error) {
Logger.error("BarSectionEditor", component.errorString())
}
})
}
}
}
}
@ -221,7 +231,7 @@ NBox {
MouseArea {
id: flowDragArea
anchors.fill: parent
z: 999 // Above all widgets to ensure it gets events first
z: -1 // Ensure this mouse area is below the Settings and Close buttons
// Critical properties for proper event handling
acceptedButtons: Qt.LeftButton

View file

@ -0,0 +1,134 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
import "./WidgetSettings" as WidgetSettings
// Widget Settings Dialog Component
Popup {
id: settingsPopup
property int widgetIndex: -1
property var widgetData: null
property string widgetId: ""
// Center popup in parent
x: (parent.width - width) * 0.5
y: (parent.height - height) * 0.5
width: 420 * scaling
height: content.implicitHeight + padding * 2
padding: Style.marginXL * scaling
modal: true
background: Rectangle {
id: bgRect
color: Color.mSurface
radius: Style.radiusL * scaling
border.color: Color.mPrimary
border.width: Style.borderM * scaling
}
// Load settings when popup opens with data
onOpened: {
if (widgetData && widgetId) {
loadWidgetSettings()
}
}
function loadWidgetSettings() {
const widgetSettingsMap = {
"ActiveWindow": "WidgetSettings/ActiveWindowSettings.qml",
"Battery": "WidgetSettings/BatterySettings.qml",
"Brightness": "WidgetSettings/BrightnessSettings.qml",
"Clock": "WidgetSettings/ClockSettings.qml",
"CustomButton": "WidgetSettings/CustomButtonSettings.qml",
"MediaMini": "WidgetSettings/MediaMiniSettings.qml",
"Microphone": "WidgetSettings/MicrophoneSettings.qml",
"NotificationHistory": "WidgetSettings/NotificationHistorySettings.qml",
"Workspace": "WidgetSettings/WorkspaceSettings.qml",
"SidePanelToggle": "WidgetSettings/SidePanelToggleSettings.qml",
"Spacer": "WidgetSettings/SpacerSettings.qml",
"SystemMonitor": "WidgetSettings/SystemMonitorSettings.qml",
"Volume": "WidgetSettings/VolumeSettings.qml"
}
const source = widgetSettingsMap[widgetId]
if (source) {
// Use setSource to pass properties at creation time
settingsLoader.setSource(source, {
"widgetData": widgetData,
"widgetMetadata": BarWidgetRegistry.widgetMetadata[widgetId]
})
}
}
ColumnLayout {
id: content
width: parent.width
spacing: Style.marginM * scaling
// Title
RowLayout {
Layout.fillWidth: true
NText {
text: `${settingsPopup.widgetId} Settings`
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.fillWidth: true
}
NIconButton {
icon: "close"
onClicked: settingsPopup.close()
}
}
// Separator
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Color.mOutline
}
// Settings based on widget type
// Will be triggered via settingsLoader.setSource()
Loader {
id: settingsLoader
Layout.fillWidth: true
}
// Action buttons
RowLayout {
Layout.fillWidth: true
Layout.topMargin: Style.marginM * scaling
Item {
Layout.fillWidth: true
}
NButton {
text: "Cancel"
outlined: true
onClicked: settingsPopup.close()
}
NButton {
text: "Apply"
icon: "check"
onClicked: {
if (settingsLoader.item && settingsLoader.item.saveSettings) {
var newSettings = settingsLoader.item.saveSettings()
root.updateWidgetSettings(sectionId, settingsPopup.widgetIndex, newSettings)
settingsPopup.close()
}
}
}
}
}
}

View file

@ -0,0 +1,32 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueShowIcon: widgetData.showIcon !== undefined ? widgetData.showIcon : widgetMetadata.showIcon
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.showIcon = valueShowIcon
return settings
}
NToggle {
id: showIcon
Layout.fillWidth: true
label: "Show app icon"
checked: root.valueShowIcon
onToggled: checked => root.valueShowIcon = checked
}
}

View file

@ -0,0 +1,31 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage
!== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
return settings
}
NToggle {
label: "Always show percentage"
checked: root.valueAlwaysShowPercentage
onToggled: checked => root.valueAlwaysShowPercentage = checked
}
}

View file

@ -0,0 +1,31 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage
!== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
return settings
}
NToggle {
label: "Always show percentage"
checked: valueAlwaysShowPercentage
onToggled: checked => valueAlwaysShowPercentage = checked
}
}

View file

@ -0,0 +1,54 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueShowDate: widgetData.showDate !== undefined ? widgetData.showDate : widgetMetadata.showDate
property bool valueUse12h: widgetData.use12HourClock !== undefined ? widgetData.use12HourClock : widgetMetadata.use12HourClock
property bool valueShowSeconds: widgetData.showSeconds !== undefined ? widgetData.showSeconds : widgetMetadata.showSeconds
property bool valueReverseDayMonth: widgetData.reverseDayMonth !== undefined ? widgetData.reverseDayMonth : widgetMetadata.reverseDayMonth
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.showDate = valueShowDate
settings.use12HourClock = valueUse12h
settings.showSeconds = valueShowSeconds
settings.reverseDayMonth = valueReverseDayMonth
return settings
}
NToggle {
label: "Show date"
checked: valueShowDate
onToggled: checked => valueShowDate = checked
}
NToggle {
label: "Use 12-hour clock"
checked: valueUse12h
onToggled: checked => valueUse12h = checked
}
NToggle {
label: "Show seconds"
checked: valueShowSeconds
onToggled: checked => valueShowSeconds = checked
}
NToggle {
label: "Reverse day and month"
checked: valueReverseDayMonth
onToggled: checked => valueReverseDayMonth = checked
}
}

View file

@ -0,0 +1,58 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.icon = iconInput.text
settings.leftClickExec = leftClickExecInput.text
settings.rightClickExec = rightClickExecInput.text
settings.middleClickExec = middleClickExecInput.text
return settings
}
// Icon setting
NTextInput {
id: iconInput
Layout.fillWidth: true
label: "Icon Name"
description: "Choose a name from the Material Icon set."
placeholderText: "Enter icon name (e.g., favorite, home, settings)"
text: widgetData?.icon || widgetMetadata.icon
}
NTextInput {
id: leftClickExecInput
Layout.fillWidth: true
label: "Left Click Command"
placeholderText: "Enter command to execute (app or custom script)"
text: widgetData?.leftClickExec || widgetMetadata.leftClickExec
}
NTextInput {
id: rightClickExecInput
Layout.fillWidth: true
label: "Right Click Command"
placeholderText: "Enter command to execute (app or custom script)"
text: widgetData?.rightClickExec || widgetMetadata.rightClickExec
}
NTextInput {
id: middleClickExecInput
Layout.fillWidth: true
label: "Middle Click Command"
placeholderText: "Enter command to execute (app or custom script)"
text: widgetData.middleClickExec || widgetMetadata.middleClickExec
}
}

View file

@ -0,0 +1,62 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueShowAlbumArt: widgetData.showAlbumArt !== undefined ? widgetData.showAlbumArt : widgetMetadata.showAlbumArt
property bool valueShowVisualizer: widgetData.showVisualizer !== undefined ? widgetData.showVisualizer : widgetMetadata.showVisualizer
property string valueVisualizerType: widgetData.visualizerType || widgetMetadata.visualizerType
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.showAlbumArt = valueShowAlbumArt
settings.showVisualizer = valueShowVisualizer
settings.visualizerType = valueVisualizerType
return settings
}
NToggle {
label: "Show album art"
checked: valueShowAlbumArt
onToggled: checked => valueShowAlbumArt = checked
}
NToggle {
label: "Show visualizer"
checked: valueShowVisualizer
onToggled: checked => valueShowVisualizer = checked
}
NComboBox {
visible: valueShowVisualizer
label: "Visualizer type"
model: ListModel {
ListElement {
key: "linear"
name: "Linear"
}
ListElement {
key: "mirrored"
name: "Mirrored"
}
ListElement {
key: "wave"
name: "Wave"
}
}
currentKey: valueVisualizerType
onSelected: key => valueVisualizerType = key
minimumWidth: 200 * scaling
}
}

View file

@ -0,0 +1,31 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage
!== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
return settings
}
NToggle {
label: "Always show percentage"
checked: valueAlwaysShowPercentage
onToggled: checked => valueAlwaysShowPercentage = checked
}
}

View file

@ -0,0 +1,38 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueShowUnreadBadge: widgetData.showUnreadBadge !== undefined ? widgetData.showUnreadBadge : widgetMetadata.showUnreadBadge
property bool valueHideWhenZero: widgetData.hideWhenZero !== undefined ? widgetData.hideWhenZero : widgetMetadata.hideWhenZero
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.showUnreadBadge = valueShowUnreadBadge
settings.hideWhenZero = valueHideWhenZero
return settings
}
NToggle {
label: "Show unread badge"
checked: valueShowUnreadBadge
onToggled: checked => valueShowUnreadBadge = checked
}
NToggle {
label: "Hide badge when zero"
checked: valueHideWhenZero
onToggled: checked => valueHideWhenZero = checked
}
}

View file

@ -0,0 +1,30 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueUseDistroLogo: widgetData.useDistroLogo !== undefined ? widgetData.useDistroLogo : widgetMetadata.useDistroLogo
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.useDistroLogo = valueUseDistroLogo
return settings
}
NToggle {
label: "Use distro logo instead of icon"
checked: valueUseDistroLogo
onToggled: checked => valueUseDistroLogo = checked
}
}

View file

@ -0,0 +1,30 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.width = parseInt(widthInput.text) || widgetMetadata.width
return settings
}
NTextInput {
id: widthInput
Layout.fillWidth: true
label: "Width"
description: "Spacing width in pixels"
text: widgetData.width || widgetMetadata.width
placeholderText: "Enter width in pixels"
}
}

View file

@ -0,0 +1,74 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local, editable state for checkboxes
property bool valueShowCpuUsage: widgetData.showCpuUsage !== undefined ? widgetData.showCpuUsage : widgetMetadata.showCpuUsage
property bool valueShowCpuTemp: widgetData.showCpuTemp !== undefined ? widgetData.showCpuTemp : widgetMetadata.showCpuTemp
property bool valueShowMemoryUsage: widgetData.showMemoryUsage !== undefined ? widgetData.showMemoryUsage : widgetMetadata.showMemoryUsage
property bool valueShowMemoryAsPercent: widgetData.showMemoryAsPercent
!== undefined ? widgetData.showMemoryAsPercent : widgetMetadata.showMemoryAsPercent
property bool valueShowNetworkStats: widgetData.showNetworkStats
!== undefined ? widgetData.showNetworkStats : widgetMetadata.showNetworkStats
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.showCpuUsage = valueShowCpuUsage
settings.showCpuTemp = valueShowCpuTemp
settings.showMemoryUsage = valueShowMemoryUsage
settings.showMemoryAsPercent = valueShowMemoryAsPercent
settings.showNetworkStats = valueShowNetworkStats
return settings
}
NToggle {
id: showCpuUsage
Layout.fillWidth: true
label: "CPU usage"
checked: valueShowCpuUsage
onToggled: checked => valueShowCpuUsage = checked
}
NToggle {
id: showCpuTemp
Layout.fillWidth: true
label: "CPU temperature"
checked: valueShowCpuTemp
onToggled: checked => valueShowCpuTemp = checked
}
NToggle {
id: showMemoryUsage
Layout.fillWidth: true
label: "Memory usage"
checked: valueShowMemoryUsage
onToggled: checked => valueShowMemoryUsage = checked
}
NToggle {
id: showMemoryAsPercent
Layout.fillWidth: true
label: "Show memory as percentage"
checked: valueShowMemoryAsPercent
onToggled: checked => valueShowMemoryAsPercent = checked
}
NToggle {
id: showNetworkStats
Layout.fillWidth: true
label: "Network traffic"
checked: valueShowNetworkStats
onToggled: checked => valueShowNetworkStats = checked
}
}

View file

@ -0,0 +1,31 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
// Local state
property bool valueAlwaysShowPercentage: widgetData.alwaysShowPercentage
!== undefined ? widgetData.alwaysShowPercentage : widgetMetadata.alwaysShowPercentage
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.alwaysShowPercentage = valueAlwaysShowPercentage
return settings
}
NToggle {
label: "Always show percentage"
checked: valueAlwaysShowPercentage
onToggled: checked => valueAlwaysShowPercentage = checked
}
}

View file

@ -0,0 +1,44 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
ColumnLayout {
id: root
spacing: Style.marginM * scaling
// Properties to receive data from parent
property var widgetData: null
property var widgetMetadata: null
function saveSettings() {
var settings = Object.assign({}, widgetData || {})
settings.labelMode = labelModeCombo.currentKey
return settings
}
NComboBox {
id: labelModeCombo
label: "Label Mode"
model: ListModel {
ListElement {
key: "none"
name: "None"
}
ListElement {
key: "index"
name: "Index"
}
ListElement {
key: "name"
name: "Name"
}
}
currentKey: widgetData.labelMode || widgetMetadata.labelMode
onSelected: key => labelModeCombo.currentKey = key
minimumWidth: 200 * scaling
}
}

View file

@ -1,186 +0,0 @@
import QtQuick
import QtQuick.Controls
import QtQuick.Effects
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
import qs.Services
// Widget Settings Dialog Component
Popup {
id: settingsPopup
property int widgetIndex: -1
property var widgetData: null
property string widgetId: ""
// Center popup in parent
x: (parent.width - width) * 0.5
y: (parent.height - height) * 0.5
width: 420 * scaling
height: content.implicitHeight + padding * 2
padding: Style.marginXL * scaling
modal: true
background: Rectangle {
id: bgRect
color: Color.mSurface
radius: Style.radiusL * scaling
border.color: Color.mPrimary
border.width: Style.borderM * scaling
}
ColumnLayout {
id: content
width: parent.width
spacing: Style.marginM * scaling
// Title
RowLayout {
Layout.fillWidth: true
NText {
text: "Widget Settings: " + settingsPopup.widgetId
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mPrimary
Layout.fillWidth: true
}
NIconButton {
icon: "close"
onClicked: settingsPopup.close()
}
}
// Separator
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: Color.mOutline
}
// Settings based on widget type
Loader {
id: settingsLoader
Layout.fillWidth: true
sourceComponent: {
if (settingsPopup.widgetId === "CustomButton") {
return customButtonSettings
} else if (settingsPopup.widgetId === "Spacer") {
return spacerSettings
}
// Add more widget settings components here as needed
return null
}
}
// Action buttons
RowLayout {
Layout.fillWidth: true
Layout.topMargin: Style.marginM * scaling
Item {
Layout.fillWidth: true
}
NButton {
text: "Cancel"
outlined: true
onClicked: settingsPopup.close()
}
NButton {
text: "Save"
onClicked: {
if (settingsLoader.item && settingsLoader.item.saveSettings) {
var newSettings = settingsLoader.item.saveSettings()
root.updateWidgetSettings(sectionId, settingsPopup.widgetIndex, newSettings)
settingsPopup.close()
}
}
}
}
}
// CustomButton settings component
Component {
id: customButtonSettings
ColumnLayout {
spacing: Style.marginM * scaling
function saveSettings() {
var settings = Object.assign({}, settingsPopup.widgetData)
settings.icon = iconInput.text
settings.leftClickExec = leftClickExecInput.text
settings.rightClickExec = rightClickExecInput.text
settings.middleClickExec = middleClickExecInput.text
return settings
}
// Icon setting
NTextInput {
id: iconInput
Layout.fillWidth: true
Layout.bottomMargin: Style.marginXL * scaling
label: "Icon Name"
description: "Use Material Icon names from the icon set."
text: settingsPopup.widgetData.icon || ""
placeholderText: "Enter icon name (e.g., favorite, home, settings)"
}
NTextInput {
id: leftClickExecInput
Layout.fillWidth: true
label: "Left Click Command"
description: "Command or application to run when left clicked."
text: settingsPopup.widgetData.leftClickExec || ""
placeholderText: "Enter command to execute (app or custom script)"
}
NTextInput {
id: rightClickExecInput
Layout.fillWidth: true
label: "Right Click Command"
description: "Command or application to run when right clicked."
text: settingsPopup.widgetData.rightClickExec || ""
placeholderText: "Enter command to execute (app or custom script)"
}
NTextInput {
id: middleClickExecInput
Layout.fillWidth: true
label: "Middle Click Command"
description: "Command or application to run when middle clicked."
text: settingsPopup.widgetData.middleClickExec || ""
placeholderText: "Enter command to execute (app or custom script)"
}
}
}
// Spacer settings component
Component {
id: spacerSettings
ColumnLayout {
spacing: Style.marginM * scaling
function saveSettings() {
var settings = Object.assign({}, settingsPopup.widgetData)
settings.width = parseInt(widthInput.text) || 20
return settings
}
NTextInput {
id: widthInput
Layout.fillWidth: true
label: "Width (pixels)"
description: "Width of the spacer in pixels."
text: settingsPopup.widgetData.width || "20"
placeholderText: "Enter width in pixels"
}
}
}
}

View file

@ -39,7 +39,7 @@ NPanel {
General,
Network,
ScreenRecorder,
TimeWeather,
Weather,
Wallpaper,
WallpaperSelector
}
@ -90,8 +90,8 @@ NPanel {
Tabs.NetworkTab {}
}
Component {
id: timeWeatherTab
Tabs.TimeWeatherTab {}
id: weatherTab
Tabs.WeatherTab {}
}
Component {
id: colorSchemeTab
@ -156,10 +156,10 @@ NPanel {
"icon": "brightness_6",
"source": brightnessTab
}, {
"id": SettingsPanel.Tab.TimeWeather,
"label": "Time & Weather",
"icon": "schedule",
"source": timeWeatherTab
"id": SettingsPanel.Tab.Weather,
"label": "Weather",
"icon": "partly_cloudy_day",
"source": weatherTab
}, {
"id": SettingsPanel.Tab.ColorScheme,
"label": "Color Scheme",

View file

@ -242,21 +242,7 @@ ColumnLayout {
Layout.bottomMargin: Style.marginS * scaling
}
// Miniplayer section
NToggle {
label: "Show Album Art In Bar Media Player"
description: "Show the album art of the currently playing song next to the title."
checked: Settings.data.audio.showMiniplayerAlbumArt
onToggled: checked => Settings.data.audio.showMiniplayerAlbumArt = checked
}
NToggle {
label: "Show Audio Visualizer In Bar Media Player"
description: "Shows an audio visualizer in the background of the miniplayer."
checked: Settings.data.audio.showMiniplayerCava
onToggled: checked => Settings.data.audio.showMiniplayerCava = checked
}
// Preferred player (persistent)
// Preferred player
NTextInput {
label: "Preferred Player"
description: "Substring to match MPRIS player (identity/bus/desktop)."

View file

@ -4,7 +4,7 @@ import QtQuick.Layouts
import qs.Commons
import qs.Services
import qs.Widgets
import qs.Modules.SettingsPanel.Extras
import qs.Modules.SettingsPanel.Bar
ColumnLayout {
id: root
@ -70,57 +70,6 @@ ColumnLayout {
}
}
}
NToggle {
label: "Show Active Window's Icon"
description: "Display the app icon next to the title of the currently focused window."
checked: Settings.data.bar.showActiveWindowIcon
onToggled: checked => Settings.data.bar.showActiveWindowIcon = checked
}
NToggle {
label: "Show Battery Percentage"
description: "Display battery percentage at all times."
checked: Settings.data.bar.alwaysShowBatteryPercentage
onToggled: checked => Settings.data.bar.alwaysShowBatteryPercentage = checked
}
NToggle {
label: "Show Network Statistics"
description: "Display network upload and download speeds in the system monitor."
checked: Settings.data.bar.showNetworkStats
onToggled: checked => Settings.data.bar.showNetworkStats = checked
}
NToggle {
label: "Replace SidePanel toggle with distro logo"
description: "Show distro logo instead of the SidePanel toggle button in the bar."
checked: Settings.data.bar.useDistroLogo
onToggled: checked => {
Settings.data.bar.useDistroLogo = checked
}
}
NComboBox {
label: "Show Workspaces Labels"
description: "Show the workspace name or index within the workspace indicator."
model: ListModel {
ListElement {
key: "none"
name: "None"
}
ListElement {
key: "index"
name: "Index"
}
ListElement {
key: "name"
name: "Name"
}
}
currentKey: Settings.data.bar.showWorkspaceLabel
onSelected: key => Settings.data.bar.showWorkspaceLabel = key
}
}
NDivider {
@ -138,7 +87,7 @@ ColumnLayout {
text: "Widgets Positioning"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
}

View file

@ -299,8 +299,7 @@ ColumnLayout {
currentKey: Settings.data.nightLight.manualSunrise
placeholder: "Select start time"
onSelected: key => Settings.data.nightLight.manualSunrise = key
preferredWidth: 120 * scaling
minimumWidth: 120 * scaling
}
Item {// add a little more spacing
@ -316,8 +315,7 @@ ColumnLayout {
currentKey: Settings.data.nightLight.manualSunset
placeholder: "Select stop time"
onSelected: key => Settings.data.nightLight.manualSunset = key
preferredWidth: 120 * scaling
minimumWidth: 120 * scaling
}
}
}

View file

@ -8,6 +8,7 @@ import qs.Widgets
ColumnLayout {
id: root
spacing: 0
// Cache for scheme JSON (can be flat or {dark, light})
property var schemeColorsCache: ({})
@ -103,225 +104,218 @@ ColumnLayout {
}
}
// Main Toggles - Dark Mode / Matugen
ColumnLayout {
spacing: 0
spacing: Style.marginL * scaling
Layout.fillWidth: true
Item {
Layout.fillWidth: true
Layout.preferredHeight: 0
// Dark Mode Toggle (affects both Matugen and predefined schemes that provide variants)
NToggle {
label: "Dark Mode"
description: Settings.data.colorSchemes.useWallpaperColors ? "Generate dark theme colors when using Matugen." : "Use a dark variant if available."
checked: Settings.data.colorSchemes.darkMode
enabled: true
onToggled: checked => Settings.data.colorSchemes.darkMode = checked
}
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
// Use Matugen
NToggle {
label: "Enable Matugen"
description: "Automatically generate colors based on your active wallpaper."
checked: Settings.data.colorSchemes.useWallpaperColors
onToggled: checked => {
if (checked) {
// Check if matugen is installed
matugenCheck.running = true
} else {
Settings.data.colorSchemes.useWallpaperColors = false
ToastService.showNotice("Matugen", "Disabled")
// Dark Mode Toggle (affects both Matugen and predefined schemes that provide variants)
NToggle {
label: "Dark Mode"
description: Settings.data.colorSchemes.useWallpaperColors ? "Generate dark theme colors when using Matugen." : "Use a dark variant if available."
checked: Settings.data.colorSchemes.darkMode
enabled: true
onToggled: checked => Settings.data.colorSchemes.darkMode = checked
}
if (Settings.data.colorSchemes.predefinedScheme) {
// Use Matugen
NToggle {
label: "Enable Matugen"
description: "Automatically generate colors based on your active wallpaper."
checked: Settings.data.colorSchemes.useWallpaperColors
onToggled: checked => {
if (checked) {
// Check if matugen is installed
matugenCheck.running = true
} else {
Settings.data.colorSchemes.useWallpaperColors = false
ToastService.showNotice("Matugen", "Disabled")
if (Settings.data.colorSchemes.predefinedScheme) {
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
}
ColorSchemeService.applyScheme(Settings.data.colorSchemes.predefinedScheme)
}
}
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
ColumnLayout {
spacing: Style.marginS * scaling
Layout.fillWidth: true
// Predefined Color Schemes
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: "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
}
// Color Schemes Grid
GridLayout {
columns: 3
rowSpacing: Style.marginM * scaling
columnSpacing: Style.marginM * scaling
Layout.fillWidth: true
Repeater {
model: ColorSchemeService.schemes
Rectangle {
id: schemeCard
property string schemePath: modelData
NText {
text: "These color schemes are only active when 'Use Matugen' is turned off. With Matugen enabled, colors will be automatically generated from your wallpaper. You can still switch between light and dark themes while using Matugen."
font.pointSize: Style.fontSizeM * scaling
color: Color.mOnSurfaceVariant
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
}
Layout.preferredHeight: 120 * scaling
radius: Style.radiusM * scaling
color: getSchemeColor(modelData, "mSurface")
border.width: Math.max(1, Style.borderL * scaling)
border.color: (!Settings.data.colorSchemes.useWallpaperColors
&& (Settings.data.colorSchemes.predefinedScheme === modelData)) ? Color.mPrimary : Color.mOutline
scale: root.cardScaleLow
// Color Schemes Grid
GridLayout {
columns: 3
rowSpacing: Style.marginM * scaling
columnSpacing: Style.marginM * scaling
Layout.fillWidth: true
// Mouse area for selection
MouseArea {
anchors.fill: parent
onClicked: {
// Disable useWallpaperColors when picking a predefined color scheme
Settings.data.colorSchemes.useWallpaperColors = false
Logger.log("ColorSchemeTab", "Disabled matugen setting")
Repeater {
model: ColorSchemeService.schemes
Settings.data.colorSchemes.predefinedScheme = schemePath
ColorSchemeService.applyScheme(schemePath)
}
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
Rectangle {
id: schemeCard
property string schemePath: modelData
Layout.fillWidth: true
Layout.preferredHeight: 120 * scaling
radius: Style.radiusM * scaling
color: getSchemeColor(modelData, "mSurface")
border.width: Math.max(1, Style.borderL * scaling)
border.color: (!Settings.data.colorSchemes.useWallpaperColors
&& (Settings.data.colorSchemes.predefinedScheme === modelData)) ? Color.mPrimary : Color.mOutline
scale: root.cardScaleLow
// Mouse area for selection
MouseArea {
anchors.fill: parent
onClicked: {
// Disable useWallpaperColors when picking a predefined color scheme
Settings.data.colorSchemes.useWallpaperColors = false
Logger.log("ColorSchemeTab", "Disabled matugen setting")
Settings.data.colorSchemes.predefinedScheme = schemePath
ColorSchemeService.applyScheme(schemePath)
}
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: {
schemeCard.scale = root.cardScaleHigh
}
onExited: {
schemeCard.scale = root.cardScaleLow
}
onEntered: {
schemeCard.scale = root.cardScaleHigh
}
// Card content
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginXL * scaling
onExited: {
schemeCard.scale = root.cardScaleLow
}
}
// Card content
ColumnLayout {
anchors.fill: parent
anchors.margins: Style.marginXL * scaling
spacing: Style.marginS * scaling
// Scheme name
NText {
text: {
// Remove json and the full path
var chunks = schemePath.replace(".json", "").split("/")
return chunks[chunks.length - 1]
}
font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: getSchemeColor(modelData, "mOnSurface")
Layout.fillWidth: true
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
}
// Color swatches
RowLayout {
id: swatches
spacing: Style.marginS * scaling
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
// Scheme name
NText {
text: {
// Remove json and the full path
var chunks = schemePath.replace(".json", "").split("/")
return chunks[chunks.length - 1]
}
font.pointSize: Style.fontSizeM * scaling
font.weight: Style.fontWeightBold
color: getSchemeColor(modelData, "mOnSurface")
Layout.fillWidth: true
elide: Text.ElideRight
horizontalAlignment: Text.AlignHCenter
readonly property int swatchSize: 20 * scaling
// Primary color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mPrimary")
}
// Color swatches
RowLayout {
id: swatches
// Secondary color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mSecondary")
}
spacing: Style.marginS * scaling
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
// Tertiary color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mTertiary")
}
readonly property int swatchSize: 20 * scaling
// Primary color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mPrimary")
}
// Secondary color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mSecondary")
}
// Tertiary color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mTertiary")
}
// Error color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mError")
}
// Error color swatch
Rectangle {
width: swatches.swatchSize
height: swatches.swatchSize
radius: width * 0.5
color: getSchemeColor(modelData, "mError")
}
}
}
// Selection indicator (Checkmark)
Rectangle {
visible: !Settings.data.colorSchemes.useWallpaperColors
&& (Settings.data.colorSchemes.predefinedScheme === schemePath)
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Style.marginS * scaling
width: 24 * scaling
height: 24 * scaling
radius: width * 0.5
color: Color.mPrimary
// Selection indicator (Checkmark)
Rectangle {
visible: !Settings.data.colorSchemes.useWallpaperColors
&& (Settings.data.colorSchemes.predefinedScheme === schemePath)
anchors.right: parent.right
anchors.top: parent.top
anchors.margins: Style.marginS * scaling
width: 24 * scaling
height: 24 * scaling
radius: width * 0.5
color: Color.mPrimary
NText {
anchors.centerIn: parent
text: "✓"
font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
color: Color.mOnPrimary
}
NText {
anchors.centerIn: parent
text: "✓"
font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
color: Color.mOnPrimary
}
}
// Smooth animations
Behavior on scale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
// Smooth animations
Behavior on scale {
NumberAnimation {
duration: Style.animationNormal
easing.type: Easing.OutCubic
}
}
Behavior on border.color {
ColorAnimation {
duration: Style.animationNormal
}
Behavior on border.color {
ColorAnimation {
duration: Style.animationNormal
}
}
Behavior on border.width {
NumberAnimation {
duration: Style.animationFast
}
Behavior on border.width {
NumberAnimation {
duration: Style.animationFast
}
}
}

View file

@ -70,52 +70,6 @@ ColumnLayout {
onToggled: checked => Settings.data.general.dimDesktop = checked
}
NToggle {
label: "Auto-hide Dock"
description: "Automatically hide the dock when not in use."
checked: Settings.data.dock.autoHide
onToggled: checked => Settings.data.dock.autoHide = checked
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NText {
text: "Dock Background Opacity"
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
}
NText {
text: "Adjust the background opacity of the dock."
font.pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.backgroundOpacity
onMoved: Settings.data.dock.backgroundOpacity = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
}
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
@ -175,7 +129,70 @@ ColumnLayout {
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
// Dock
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Dock"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
Layout.bottomMargin: Style.marginS * scaling
}
NToggle {
label: "Auto-hide Dock"
description: "Automatically hide the dock when not in use."
checked: Settings.data.dock.autoHide
onToggled: checked => Settings.data.dock.autoHide = checked
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
NText {
text: "Dock Background Opacity"
font.pointSize: Style.fontSizeL * scaling
font.weight: Style.fontWeightBold
color: Color.mOnSurface
}
NText {
text: "Adjust the background opacity of the dock."
font.pointSize: Style.fontSizeXS * scaling
color: Color.mOnSurfaceVariant
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
RowLayout {
NSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.backgroundOpacity
onMoved: Settings.data.dock.backgroundOpacity = value
cutoutColor: Color.mSurface
}
NText {
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: Style.marginS * scaling
color: Color.mOnSurface
}
}
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
@ -206,6 +223,7 @@ ColumnLayout {
currentKey: Settings.data.ui.fontDefault
placeholder: "Select default font..."
popupHeight: 420 * scaling
minimumWidth: 300 * scaling
onSelected: function (key) {
Settings.data.ui.fontDefault = key
}
@ -218,6 +236,7 @@ ColumnLayout {
currentKey: Settings.data.ui.fontFixed
placeholder: "Select monospace font..."
popupHeight: 320 * scaling
minimumWidth: 300 * scaling
onSelected: function (key) {
Settings.data.ui.fontFixed = key
}
@ -230,6 +249,7 @@ ColumnLayout {
currentKey: Settings.data.ui.fontBillboard
placeholder: "Select display font..."
popupHeight: 320 * scaling
minimumWidth: 300 * scaling
onSelected: function (key) {
Settings.data.ui.fontBillboard = key
}

View file

@ -52,20 +52,6 @@ ColumnLayout {
}
}
NToggle {
label: "Enable Clipboard History"
description: "Show clipboard history in the launcher."
checked: Settings.data.appLauncher.enableClipboardHistory
onToggled: checked => Settings.data.appLauncher.enableClipboardHistory = checked
}
NToggle {
label: "Use App2Unit for Launching"
description: "Use app2unit -- 'desktop-entry' when launching applications for better systemd integration."
checked: Settings.data.appLauncher.useApp2Unit
onToggled: checked => Settings.data.appLauncher.useApp2Unit = checked
}
ColumnLayout {
spacing: Style.marginXXS * scaling
Layout.fillWidth: true
@ -105,6 +91,20 @@ ColumnLayout {
}
}
}
NToggle {
label: "Enable Clipboard History"
description: "Show clipboard history in the launcher."
checked: Settings.data.appLauncher.enableClipboardHistory
onToggled: checked => Settings.data.appLauncher.enableClipboardHistory = checked
}
NToggle {
label: "Use App2Unit for Launching"
description: "Use app2unit -- 'desktop-entry' when launching applications for better systemd integration."
checked: Settings.data.appLauncher.useApp2Unit
onToggled: checked => Settings.data.appLauncher.useApp2Unit = checked
}
}
NDivider {

View file

@ -52,46 +52,6 @@ ColumnLayout {
Layout.bottomMargin: Style.marginXL * scaling
}
// Time section
ColumnLayout {
spacing: Style.marginL * scaling
Layout.fillWidth: true
NText {
text: "Time Format"
font.pointSize: Style.fontSizeXXL * scaling
font.weight: Style.fontWeightBold
color: Color.mSecondary
}
NToggle {
label: "Use 12-Hour Clock"
description: "Display time in 12-hour format (AM/PM) instead of 24-hour."
checked: Settings.data.location.use12HourClock
onToggled: checked => Settings.data.location.use12HourClock = checked
}
NToggle {
label: "Reverse Day/Month"
description: "Display date as dd/mm instead of mm/dd."
checked: Settings.data.location.reverseDayMonth
onToggled: checked => Settings.data.location.reverseDayMonth = checked
}
NToggle {
label: "Show Date with Clock"
description: "Display date alongside time (e.g., 18:12 - Sat, 23 Aug)."
checked: Settings.data.location.showDateWithClock
onToggled: checked => Settings.data.location.showDateWithClock = checked
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
// Weather section
ColumnLayout {
spacing: Style.marginM * scaling
@ -111,10 +71,4 @@ ColumnLayout {
onToggled: checked => Settings.data.location.useFahrenheit = checked
}
}
NDivider {
Layout.fillWidth: true
Layout.topMargin: Style.marginXL * scaling
Layout.bottomMargin: Style.marginXL * scaling
}
}

View file

@ -38,6 +38,26 @@ Singleton {
})
property var widgetMetadata: ({
"ActiveWindow": {
"allowUserSettings": true,
"showIcon": true
},
"Battery": {
"allowUserSettings": true,
"alwaysShowPercentage": false,
"warningThreshold": 30
},
"Brightness": {
"allowUserSettings": true,
"alwaysShowPercentage": false
},
"Clock": {
"allowUserSettings": true,
"showDate": false,
"use12HourClock": false,
"showSeconds": false,
"reverseDayMonth": true
},
"CustomButton": {
"allowUserSettings": true,
"icon": "favorite",
@ -45,10 +65,44 @@ Singleton {
"rightClickExec": "",
"middleClickExec": ""
},
"Microphone": {
"allowUserSettings": true,
"alwaysShowPercentage": false
},
"NotificationHistory": {
"allowUserSettings": true,
"showUnreadBadge": true,
"hideWhenZero": true
},
"Spacer": {
"allowUserSettings": true,
"icon": "space_bar",
"width": 20
},
"SystemMonitor": {
"allowUserSettings": true,
"showCpuUsage": true,
"showCpuTemp": true,
"showMemoryUsage": true,
"showMemoryAsPercent": false,
"showNetworkStats": false
},
"Workspace": {
"allowUserSettings": true,
"labelMode": "index"
},
"MediaMini": {
"allowUserSettings": true,
"showAlbumArt": false,
"showVisualizer": false,
"visualizerType": "linear"
},
"SidePanelToggle": {
"allowUserSettings": true,
"useDistroLogo": false
},
"Volume": {
"allowUserSettings": true,
"alwaysShowPercentage": false
}
})

View file

@ -37,9 +37,7 @@ Singleton {
Process {
id: process
stdinEnabled: true
running: (Settings.data.audio.visualizerType !== "none")
&& (PanelService.getPanel("sidePanel").active || Settings.data.audio.showMiniplayerCava
|| (PanelService.lockScreen && PanelService.lockScreen.active))
running: true
command: ["cava", "-p", "/dev/stdin"]
onExited: {
stdinEnabled = true

View file

@ -45,7 +45,7 @@ Singleton {
property string version: "Unknown"
property var contributors: []
property double timestamp: 0
property real timestamp: 0
}
}

View file

@ -80,7 +80,7 @@ Singleton {
JsonAdapter {
id: historyAdapter
property var history: []
property double timestamp: 0
property real timestamp: 0
}
}
@ -201,12 +201,17 @@ Singleton {
const items = historyAdapter.history || []
for (var i = 0; i < items.length; i++) {
const it = items[i]
// Coerce legacy second-based timestamps to milliseconds
var ts = it.timestamp
if (typeof ts === "number" && ts < 1e12) {
ts = ts * 1000
}
historyModel.append({
"summary": it.summary || "",
"body": it.body || "",
"appName": it.appName || "",
"urgency": it.urgency,
"timestamp": it.timestamp ? new Date(it.timestamp) : new Date()
"timestamp": ts ? new Date(ts) : new Date()
})
}
} catch (e) {
@ -225,7 +230,10 @@ Singleton {
"body": n.body,
"appName": n.appName,
"urgency": n.urgency,
"timestamp": (n.timestamp instanceof Date) ? n.timestamp.getTime() : n.timestamp
"timestamp"// Always persist in milliseconds
: (n.timestamp instanceof Date) ? n.timestamp.getTime(
) : (typeof n.timestamp === "number"
&& n.timestamp < 1e12 ? n.timestamp * 1000 : n.timestamp)
})
}
historyAdapter.history = arr

View file

@ -216,7 +216,11 @@ Singleton {
// -------------------------------------------------------------------
// Get specific monitor wallpaper - now from cache
function getWallpaper(screenName) {
return currentWallpapers[screenName] || ""
var path = currentWallpapers[screenName] || ""
if (path === "") {
return Settings.data.wallpaper.defaultWallpaper || ""
}
return path
}
// -------------------------------------------------------------------

View file

@ -77,10 +77,12 @@ Rectangle {
RowLayout {
id: contentRow
anchors.centerIn: parent
spacing: Style.marginS * scaling
spacing: Style.marginXS * scaling
// Icon (optional)
NIcon {
Layout.alignment: Qt.AlignVCenter
layoutTopMargin: 1 * scaling
visible: root.icon !== ""
text: root.icon
font.pointSize: root.iconSize
@ -105,6 +107,7 @@ Rectangle {
// Text
NText {
Layout.alignment: Qt.AlignVCenter
visible: root.text !== ""
text: root.text
font.pointSize: root.fontSize

View file

@ -27,6 +27,11 @@ RowLayout {
visible: root.label !== "" || root.description !== ""
}
// Spacer to push the checkbox to the far right
Item {
Layout.fillWidth: true
}
Rectangle {
id: box
@ -39,13 +44,13 @@ RowLayout {
Behavior on color {
ColorAnimation {
duration: Style.animationNormal
duration: Style.animationFast
}
}
Behavior on border.color {
ColorAnimation {
duration: Style.animationNormal
duration: Style.animationFast
}
}

View file

@ -1,34 +0,0 @@
import QtQuick
import qs.Commons
import qs.Services
import qs.Widgets
Rectangle {
id: root
signal entered
signal exited
signal clicked
width: textItem.paintedWidth
height: textItem.paintedHeight
color: Color.transparent
NText {
id: textItem
text: Time.time
anchors.centerIn: parent
font.pointSize: Style.fontSizeS * scaling
font.weight: Style.fontWeightBold
}
MouseArea {
id: clockMouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onEntered: root.entered()
onExited: root.exited()
onClicked: root.clicked()
}
}

View file

@ -8,8 +8,7 @@ import qs.Widgets
RowLayout {
id: root
readonly property real preferredHeight: Style.baseWidgetSize * 1.1 * scaling
property real preferredWidth: 320 * scaling
property real minimumWidth: 280 * scaling
property real popupHeight: 180 * scaling
property string label: ""
@ -20,9 +19,11 @@ RowLayout {
property string currentKey: ""
property string placeholder: ""
readonly property real preferredHeight: Style.baseWidgetSize * 1.1 * scaling
signal selected(string key)
spacing: Style.marginS * scaling
spacing: Style.marginL * scaling
Layout.fillWidth: true
function findIndexByKey(key) {
@ -39,11 +40,15 @@ RowLayout {
description: root.description
}
Item {
Layout.fillWidth: true
}
ComboBox {
id: combo
Layout.preferredWidth: root.preferredWidth
Layout.preferredHeight: height
Layout.minimumWidth: root.minimumWidth
Layout.preferredHeight: root.preferredHeight
model: model
currentIndex: findIndexByKey(currentKey)
onActivated: {

View file

@ -1,8 +1,11 @@
import QtQuick
import qs.Commons
import qs.Widgets
import QtQuick.Layouts
Text {
// Optional layout nudge for optical alignment when used inside Layouts
property real layoutTopMargin: 0
text: "question_mark"
font.family: "Material Symbols Rounded"
font.pointSize: Style.fontSizeL * scaling
@ -12,4 +15,5 @@ Text {
}
color: Color.mOnSurface
verticalAlignment: Text.AlignVCenter
Layout.topMargin: layoutTopMargin
}

View file

@ -20,6 +20,7 @@ ColumnLayout {
font.capitalization: Font.Capitalize
color: labelColor
visible: label !== ""
Layout.fillWidth: true
}
NText {

View file

@ -39,13 +39,13 @@ Item {
property bool shouldAnimateHide: false
// Exposed width logic
readonly property int pillHeight: Style.baseWidgetSize * sizeRatio * scaling
readonly property int iconSize: Style.baseWidgetSize * sizeRatio * scaling
readonly property int pillPaddingHorizontal: Style.marginM * scaling
readonly property int iconSize: Math.round(Style.baseWidgetSize * sizeRatio * scaling)
readonly property int pillHeight: iconSize
readonly property int pillPaddingHorizontal: Style.marginS * scaling
readonly property int pillOverlap: iconSize * 0.5
readonly property int maxPillWidth: Math.max(1, textItem.implicitWidth + pillPaddingHorizontal * 2 + pillOverlap)
width: iconSize + (effectiveShown ? maxPillWidth - pillOverlap : 0)
width: iconSize + Math.max(0, pill.width - pillOverlap)
height: pillHeight
Rectangle {
@ -67,7 +67,13 @@ Item {
NText {
id: textItem
anchors.centerIn: parent
anchors.verticalCenter: parent.verticalCenter
x: {
// Little tweak to have a better text horizontal centering
var centerX = (parent.width - width) / 2
var offset = rightOpen ? Style.marginXS * scaling : -Style.marginXS * scaling
return centerX + offset
}
text: root.text
font.pointSize: Style.fontSizeXS * scaling
font.weight: Style.fontWeightBold
@ -102,8 +108,7 @@ Item {
border.width: Math.max(1, Style.borderS * scaling)
border.color: forceOpen ? Qt.alpha(Color.mOutline, 0.5) : Color.transparent
anchors.left: rightOpen ? parent.left : undefined
anchors.right: rightOpen ? undefined : parent.right
x: rightOpen ? 0 : (parent.width - width)
Behavior on color {
ColorAnimation {

View file

@ -13,4 +13,5 @@ Text {
font.kerning: true
color: Color.mOnSurface
renderType: Text.QtRendering
verticalAlignment: Text.AlignVCenter
}