dashboard: dynamic dashboard tabs + fix performance settings updating (#1253)

* [CI] chore: update flake

* Dashboard perf settings save, visibility on none enabled

* Dashboard heigh stutter fixed, persist current tab

* restore binding

* wrapper async=false

* ScriptModel, centralized tabs/panes, individual toggle

* fixes, missed mediaUpdateInterval, passing values

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
This commit is contained in:
Robin Seger 2026-03-10 15:22:23 +01:00 committed by GitHub
parent 0d56db3b6c
commit 3e0360401b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 220 additions and 104 deletions

View file

@ -258,7 +258,8 @@ Singleton {
return { return {
enabled: dashboard.enabled, enabled: dashboard.enabled,
showOnHover: dashboard.showOnHover, showOnHover: dashboard.showOnHover,
updateInterval: dashboard.updateInterval, mediaUpdateInterval: dashboard.mediaUpdateInterval,
resourceUpdateInterval: dashboard.resourceUpdateInterval,
dragThreshold: dashboard.dragThreshold, dragThreshold: dashboard.dragThreshold,
performance: { performance: {
showBattery: dashboard.performance.showBattery, showBattery: dashboard.performance.showBattery,

View file

@ -6,6 +6,10 @@ JsonObject {
property int mediaUpdateInterval: 500 property int mediaUpdateInterval: 500
property int resourceUpdateInterval: 1000 property int resourceUpdateInterval: 1000
property int dragThreshold: 50 property int dragThreshold: 50
property bool showDashboard: true
property bool showMedia: true
property bool showPerformance: true
property bool showWeather: true
property Sizes sizes: Sizes {} property Sizes sizes: Sizes {}
property Performance performance: Performance {} property Performance performance: Performance {}

View file

@ -22,9 +22,16 @@ Item {
// General Settings // General Settings
property bool enabled: Config.dashboard.enabled ?? true property bool enabled: Config.dashboard.enabled ?? true
property bool showOnHover: Config.dashboard.showOnHover ?? true property bool showOnHover: Config.dashboard.showOnHover ?? true
property int updateInterval: Config.dashboard.updateInterval ?? 1000 property int mediaUpdateInterval: Config.dashboard.mediaUpdateInterval ?? 1000
property int resourceUpdateInterval: Config.dashboard.resourceUpdateInterval ?? 1000
property int dragThreshold: Config.dashboard.dragThreshold ?? 50 property int dragThreshold: Config.dashboard.dragThreshold ?? 50
// Dashboard Tabs
property bool showDashboard: Config.dashboard.showDashboard ?? true
property bool showMedia: Config.dashboard.showMedia ?? true
property bool showPerformance: Config.dashboard.showPerformance ?? true
property bool showWeather: Config.dashboard.showWeather ?? true
// Performance Resources // Performance Resources
property bool showBattery: Config.dashboard.performance.showBattery ?? false property bool showBattery: Config.dashboard.performance.showBattery ?? false
property bool showGpu: Config.dashboard.performance.showGpu ?? true property bool showGpu: Config.dashboard.performance.showGpu ?? true
@ -38,8 +45,13 @@ Item {
function saveConfig() { function saveConfig() {
Config.dashboard.enabled = root.enabled; Config.dashboard.enabled = root.enabled;
Config.dashboard.showOnHover = root.showOnHover; Config.dashboard.showOnHover = root.showOnHover;
Config.dashboard.updateInterval = root.updateInterval; Config.dashboard.mediaUpdateInterval = root.mediaUpdateInterval;
Config.dashboard.resourceUpdateInterval = root.resourceUpdateInterval;
Config.dashboard.dragThreshold = root.dragThreshold; Config.dashboard.dragThreshold = root.dragThreshold;
Config.dashboard.showDashboard = root.showDashboard;
Config.dashboard.showMedia = root.showMedia;
Config.dashboard.showPerformance = root.showPerformance;
Config.dashboard.showWeather = root.showWeather;
Config.dashboard.performance.showBattery = root.showBattery; Config.dashboard.performance.showBattery = root.showBattery;
Config.dashboard.performance.showGpu = root.showGpu; Config.dashboard.performance.showGpu = root.showGpu;
Config.dashboard.performance.showCpu = root.showCpu; Config.dashboard.performance.showCpu = root.showCpu;

View file

@ -38,24 +38,69 @@ SectionContainer {
} }
} }
SectionContainer { RowLayout {
contentSpacing: Appearance.spacing.normal Layout.fillWidth: true
spacing: Appearance.spacing.normal
SwitchRow {
Layout.fillWidth: true
label: qsTr("Show Dashboard tab")
checked: root.rootItem.showDashboard
onToggled: checked => {
root.rootItem.showDashboard = checked;
root.rootItem.saveConfig();
}
}
SwitchRow {
Layout.fillWidth: true
label: qsTr("Show Media tab")
checked: root.rootItem.showMedia
onToggled: checked => {
root.rootItem.showMedia = checked;
root.rootItem.saveConfig();
}
}
SwitchRow {
Layout.fillWidth: true
label: qsTr("Show Performance tab")
checked: root.rootItem.showPerformance
onToggled: checked => {
root.rootItem.showPerformance = checked;
root.rootItem.saveConfig();
}
}
SwitchRow {
Layout.fillWidth: true
label: qsTr("Show Weather tab")
checked: root.rootItem.showWeather
onToggled: checked => {
root.rootItem.showWeather = checked;
root.rootItem.saveConfig();
}
}
}
SliderInput { SliderInput {
Layout.fillWidth: true Layout.fillWidth: true
label: qsTr("Update interval") label: qsTr("Media update interval")
value: root.rootItem.updateInterval value: root.rootItem.mediaUpdateInterval
from: 100 from: 100
to: 10000 to: 10000
stepSize: 100 stepSize: 100
suffix: "ms" suffix: "ms"
validator: IntValidator { bottom: 100; top: 10000 } validator: IntValidator {
formatValueFunction: (val) => Math.round(val).toString() bottom: 100
parseValueFunction: (text) => parseInt(text) top: 10000
}
formatValueFunction: val => Math.round(val).toString()
parseValueFunction: text => parseInt(text)
onValueModified: (newValue) => { onValueModified: newValue => {
root.rootItem.updateInterval = Math.round(newValue); root.rootItem.mediaUpdateInterval = Math.round(newValue);
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
} }
@ -68,14 +113,16 @@ SectionContainer {
from: 0 from: 0
to: 100 to: 100
suffix: "px" suffix: "px"
validator: IntValidator { bottom: 0; top: 100 } validator: IntValidator {
formatValueFunction: (val) => Math.round(val).toString() bottom: 0
parseValueFunction: (text) => parseInt(text) top: 100
}
formatValueFunction: val => Math.round(val).toString()
parseValueFunction: text => parseInt(text)
onValueModified: (newValue) => { onValueModified: newValue => {
root.rootItem.dragThreshold = Math.round(newValue); root.rootItem.dragThreshold = Math.round(newValue);
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
} }
}
} }

View file

@ -33,7 +33,7 @@ SectionContainer {
opts.push({ opts.push({
"label": qsTr("Battery"), "label": qsTr("Battery"),
"propertyName": "showBattery", "propertyName": "showBattery",
"onToggled": function(checked) { "onToggled": function (checked) {
root.rootItem.showBattery = checked; root.rootItem.showBattery = checked;
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
@ -43,7 +43,7 @@ SectionContainer {
opts.push({ opts.push({
"label": qsTr("GPU"), "label": qsTr("GPU"),
"propertyName": "showGpu", "propertyName": "showGpu",
"onToggled": function(checked) { "onToggled": function (checked) {
root.rootItem.showGpu = checked; root.rootItem.showGpu = checked;
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
@ -52,28 +52,28 @@ SectionContainer {
opts.push({ opts.push({
"label": qsTr("CPU"), "label": qsTr("CPU"),
"propertyName": "showCpu", "propertyName": "showCpu",
"onToggled": function(checked) { "onToggled": function (checked) {
root.rootItem.showCpu = checked; root.rootItem.showCpu = checked;
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
}, { }, {
"label": qsTr("Memory"), "label": qsTr("Memory"),
"propertyName": "showMemory", "propertyName": "showMemory",
"onToggled": function(checked) { "onToggled": function (checked) {
root.rootItem.showMemory = checked; root.rootItem.showMemory = checked;
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
}, { }, {
"label": qsTr("Storage"), "label": qsTr("Storage"),
"propertyName": "showStorage", "propertyName": "showStorage",
"onToggled": function(checked) { "onToggled": function (checked) {
root.rootItem.showStorage = checked; root.rootItem.showStorage = checked;
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
}, { }, {
"label": qsTr("Network"), "label": qsTr("Network"),
"propertyName": "showNetwork", "propertyName": "showNetwork",
"onToggled": function(checked) { "onToggled": function (checked) {
root.rootItem.showNetwork = checked; root.rootItem.showNetwork = checked;
root.rootItem.saveConfig(); root.rootItem.saveConfig();
} }
@ -82,4 +82,25 @@ SectionContainer {
} }
} }
SliderInput {
Layout.fillWidth: true
label: qsTr("Resource update interval")
value: root.rootItem.resourceUpdateInterval
from: 100
to: 10000
stepSize: 100
suffix: "ms"
validator: IntValidator {
bottom: 100
top: 10000
}
formatValueFunction: val => Math.round(val).toString()
parseValueFunction: text => parseInt(text)
onValueModified: newValue => {
root.rootItem.resourceUpdateInterval = Math.round(newValue);
root.rootItem.saveConfig();
}
}
} }

View file

@ -14,6 +14,37 @@ Item {
required property PersistentProperties visibilities required property PersistentProperties visibilities
required property PersistentProperties state required property PersistentProperties state
required property FileDialog facePicker required property FileDialog facePicker
readonly property var dashboardTabs: {
const allTabs = [
{
component: dashComponent,
iconName: "dashboard",
text: qsTr("Dashboard"),
enabled: Config.dashboard.showDashboard
},
{
component: mediaComponent,
iconName: "queue_music",
text: qsTr("Media"),
enabled: Config.dashboard.showMedia
},
{
component: performanceComponent,
iconName: "speed",
text: qsTr("Performance"),
enabled: Config.dashboard.showPerformance && (Config.dashboard.performance.showCpu || Config.dashboard.performance.showGpu || Config.dashboard.performance.showMemory || Config.dashboard.performance.showStorage || Config.dashboard.performance.showNetwork || Config.dashboard.performance.showBattery)
},
{
component: weatherComponent,
iconName: "cloud",
text: qsTr("Weather"),
enabled: Config.dashboard.showWeather
}
];
return allTabs.filter(tab => tab.enabled);
}
readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2 readonly property real nonAnimWidth: view.implicitWidth + viewWrapper.anchors.margins * 2
readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins * 2 readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + viewWrapper.anchors.margins * 2
@ -31,6 +62,7 @@ Item {
nonAnimWidth: root.nonAnimWidth - anchors.margins * 2 nonAnimWidth: root.nonAnimWidth - anchors.margins * 2
state: root.state state: root.state
tabs: root.dashboardTabs
} }
ClippingRectangle { ClippingRectangle {
@ -86,31 +118,56 @@ Item {
RowLayout { RowLayout {
id: row id: row
Pane { Repeater {
index: 0 model: ScriptModel {
sourceComponent: Dash { values: root.dashboardTabs
}
delegate: Loader {
id: paneLoader
required property int index
required property var modelData
Layout.alignment: Qt.AlignTop
sourceComponent: modelData.component
Component.onCompleted: active = Qt.binding(() => {
if (index === view.currentIndex)
return true;
const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth);
const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth);
return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth);
})
}
}
}
Component {
id: dashComponent
Dash {
visibilities: root.visibilities visibilities: root.visibilities
state: root.state state: root.state
facePicker: root.facePicker facePicker: root.facePicker
} }
} }
Pane { Component {
index: 1 id: mediaComponent
sourceComponent: Media { Media {
visibilities: root.visibilities visibilities: root.visibilities
} }
} }
Pane { Component {
index: 2 id: performanceComponent
sourceComponent: Performance {} Performance {}
} }
Pane { Component {
index: 3 id: weatherComponent
sourceComponent: Weather {} Weather {}
}
} }
Behavior on contentX { Behavior on contentX {
@ -132,21 +189,4 @@ Item {
easing.bezierCurve: Appearance.anim.curves.emphasized easing.bezierCurve: Appearance.anim.curves.emphasized
} }
} }
component Pane: Loader {
id: pane
required property int index
Layout.alignment: Qt.AlignTop
Component.onCompleted: active = Qt.binding(() => {
// Always keep current tab loaded
if (pane.index === view.currentIndex)
return true;
const vx = Math.floor(view.visibleArea.xPosition * view.contentWidth);
const vex = Math.floor(vx + view.visibleArea.widthRatio * view.contentWidth);
return (vx >= x && vx <= x + implicitWidth) || (vex >= x && vex <= x + implicitWidth);
})
}
} }

View file

@ -14,6 +14,8 @@ Item {
required property real nonAnimWidth required property real nonAnimWidth
required property PersistentProperties state required property PersistentProperties state
required property var tabs
readonly property alias count: bar.count readonly property alias count: bar.count
implicitHeight: bar.implicitHeight + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight implicitHeight: bar.implicitHeight + indicator.implicitHeight + indicator.anchors.topMargin + separator.implicitHeight
@ -30,30 +32,18 @@ Item {
onCurrentIndexChanged: root.state.currentTab = currentIndex onCurrentIndexChanged: root.state.currentTab = currentIndex
Tab { Repeater {
iconName: "dashboard" model: ScriptModel {
text: qsTr("Dashboard") values: root.tabs
} }
Tab { delegate: Tab {
iconName: "queue_music" required property var modelData
text: qsTr("Media")
}
Tab { iconName: modelData.iconName
iconName: "speed" text: modelData.text
text: qsTr("Performance")
} }
Tab {
iconName: "cloud"
text: qsTr("Weather")
} }
// Tab {
// iconName: "workspaces"
// text: qsTr("Workspaces")
// }
} }
Item { Item {
@ -62,11 +52,13 @@ Item {
anchors.top: bar.bottom anchors.top: bar.bottom
anchors.topMargin: 5 anchors.topMargin: 5
implicitWidth: bar.currentItem.implicitWidth implicitWidth: bar.currentItem?.implicitWidth ?? 0
implicitHeight: 3 implicitHeight: 3
x: { x: {
const tab = bar.currentItem; const tab = bar.currentItem;
if (!tab)
return 0;
const width = (root.nonAnimWidth - bar.spacing * (bar.count - 1)) / bar.count; const width = (root.nonAnimWidth - bar.spacing * (bar.count - 1)) / bar.count;
return width * tab.TabBar.index + (width - tab.implicitWidth) / 2; return width * tab.TabBar.index + (width - tab.implicitWidth) / 2;
} }

View file

@ -82,7 +82,6 @@ Item {
running: true running: true
interval: Appearance.anim.durations.extraLarge interval: Appearance.anim.durations.extraLarge
onTriggered: { onTriggered: {
content.active = Qt.binding(() => (root.visibilities.dashboard && Config.dashboard.enabled) || root.visible);
content.visible = true; content.visible = true;
} }
} }