fix: column index desync after reorder

This commit is contained in:
2026-09-30 14:26:50 +02:00
parent 359f832609
commit 3373f11193
28 changed files with 859 additions and 464 deletions
+27 -26
View File
@@ -12,7 +12,7 @@ Item {
id: root
readonly property real nonAnimWidth: view.implicitWidth + clipWrapper.anchors.margins * 2
readonly property real nonAnimHeight: tabs.implicitHeight + clipWrapper.anchors.topMargin + view.implicitHeight + clipWrapper.anchors.margins * 2
readonly property real nonAnimHeight: tabs.implicitHeight + tabs.anchors.topMargin + view.implicitHeight + clipWrapper.anchors.margins + clipWrapper.anchors.topMargin
required property ScreenState screenState
@@ -22,25 +22,25 @@ Item {
component: dashComponent,
iconName: "dashboard",
text: qsTr("Dashboard"),
enabled: true
enabled: Config.dashboard.showDashboard
},
{
component: weatherComponent,
iconName: "cloud",
text: qsTr("Weather"),
enabled: true
enabled: Config.dashboard.showWeather
},
{
component: mediaComponent,
iconName: "queue_music",
text: qsTr("Media"),
enabled: true
enabled: Config.dashboard.showMedia
},
{
component: procsComponent,
iconName: "speed",
text: qsTr("Processes"),
enabled: true
enabled: Config.dashboard.showProcesses
},
];
return allTabs.filter(tab => tab.enabled);
@@ -60,33 +60,34 @@ Item {
}
}
Item {
id: clipWrapper
anchors.fill: parent
anchors.margins: Tokens.padding.small
// radius: Tokens.rounding.extraLarge - anchors.margins
Tabs {
id: tabs
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
nonAnimWidth: view.implicitWidth
screenState: root.screenState
tabs: root.dashboardTabs
anchors.topMargin: ZUtils.clamp(anchors.margins - Config.bar.border, 0, anchors.margins)
anchors.margins: Tokens.padding.small
}
Tabs {
id: tabs
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
nonAnimWidth: view.implicitWidth
screenState: root.screenState
tabs: root.dashboardTabs
}
CustomClippingRect {
id: clipWrapper
anchors.top: tabs.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.topMargin: Tokens.spacing.small
anchors.margins: Tokens.padding.small
radius: Tokens.rounding.extraLarge - anchors.margins
Item {
id: viewWrapper
anchors.top: tabs.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.topMargin: Tokens.spacing.small
anchors.fill: parent
Flickable {
id: view
+1 -1
View File
@@ -67,7 +67,7 @@ GridLayout {
Rect {
Layout.columnSpan: 2
Layout.fillHeight: true
Layout.preferredWidth: Config.dashboard.sizes.weatherWidth
Layout.preferredWidth: Tokens.sizes.dashboard.weatherWidth
Layout.row: 0
radius: root.radius
+4 -4
View File
@@ -51,7 +51,7 @@ Row {
id: icon
anchors.left: parent.left
anchors.leftMargin: (Config.dashboard.sizes.infoIconSize - implicitWidth) / 2
anchors.leftMargin: (Tokens.sizes.dashboard.infoIconSize - implicitWidth) / 2
color: Colors.palette.m3primary
implicitSize: Math.floor(13 * 1.34)
source: SystemInfo.osLogo
@@ -65,7 +65,7 @@ Row {
anchors.verticalCenter: icon.verticalCenter
elide: Text.ElideRight
text: `: ${SystemInfo.osPrettyName || SystemInfo.osName}`
width: Config.dashboard.sizes.infoWidth
width: Tokens.sizes.dashboard.infoWidth
}
}
@@ -98,7 +98,7 @@ Row {
id: icon
anchors.left: parent.left
anchors.leftMargin: (Config.dashboard.sizes.infoIconSize - implicitWidth) / 2
anchors.leftMargin: (Tokens.sizes.dashboard.infoIconSize - implicitWidth) / 2
color: line.colour
fill: 1
fontStyle: Tokens.font.icon.small
@@ -113,7 +113,7 @@ Row {
anchors.verticalCenter: icon.verticalCenter
elide: Text.ElideNone
text: `: ${line.text}`
width: Config.dashboard.sizes.infoWidth
width: Tokens.sizes.dashboard.infoWidth
}
}
}
@@ -3,6 +3,7 @@ pragma Singleton
import QtQuick
import Quickshell
import ZShell.Config
import ZShell.Services
Singleton {
id: root
@@ -88,6 +89,18 @@ Singleton {
}
]
readonly property var desktopEntries: DesktopEntries.applications.values.map(e => ({
icon: e.icon,
command: e.command
}))
Component.onCompleted: {
Processes.setDesktopEntries(root.desktopEntries);
Qt.callLater(ensurePinnedFirst);
}
onDesktopEntriesChanged: Processes.setDesktopEntries(root.desktopEntries)
readonly property string pinnedColumnId: "command"
readonly property var hiddenColumns: {
@@ -113,8 +126,6 @@ Singleton {
});
}
Component.onCompleted: Qt.callLater(ensurePinnedFirst)
function defFor(id: string): var {
for (const def of root.columnDefs)
if (def.id === id)
+53 -100
View File
@@ -64,109 +64,83 @@ Item {
function onWidthChanged() {
root.fillMissingWidths();
}
function onCountChanged() {
resizeOverlay.geometryTick++;
root.fillMissingWidths();
}
function onContentWidthChanged() {
resizeOverlay.geometryTick++;
}
}
Connections {
target: columnsData
function onValuesChanged() {
root.fillMissingWidths();
resizeOverlay.geometryTick++;
}
}
Component.onCompleted: Qt.callLater(fillMissingWidths)
Component.onDestruction: Cursor.clearOverride()
RowLayout {
id: rowLayout
CustomListView {
id: columnList
anchors.fill: parent
orientation: ListView.Horizontal
interactive: false
spacing: Tokens.spacing.extraSmall / 2
CustomListView {
id: columnList
Layout.fillWidth: true
orientation: ListView.Horizontal
interactive: false
spacing: parent.spacing
implicitHeight: root.headerHeight
add: Transition {
Anim {
from: 0
properties: "opacity"
to: 1
type: Anim.DefaultEffects
}
add: Transition {
Anim {
from: 0
properties: "opacity"
to: 1
type: Anim.DefaultEffects
}
displaced: Transition {
Anim {
properties: "opacity"
to: 1
type: Anim.DefaultEffects
}
Anim {
properties: "x"
}
}
displaced: Transition {
Anim {
properties: "opacity"
to: 1
type: Anim.DefaultEffects
}
move: Transition {
Anim {
properties: "opacity"
to: 1
type: Anim.DefaultEffects
}
Anim {
properties: "x"
}
Anim {
properties: "x"
}
remove: Transition {
Anim {
to: 0
properties: "opacity"
type: Anim.DefaultEffects
}
}
move: Transition {
Anim {
properties: "opacity"
to: 1
type: Anim.DefaultEffects
}
model: DelegateModel {
id: columnsVisual
delegate: ColumnHeaderCell {
parentModel: columnsVisual
}
model: ScriptModel {
id: columnsData
values: Config.dashboard.processColumns.values
}
Anim {
properties: "x"
}
}
remove: Transition {
Anim {
to: 0
properties: "opacity"
type: Anim.DefaultEffects
}
}
IconButton {
id: addColumnBtn
model: DelegateModel {
id: columnsVisual
type: IconButton.Text
icon: "add"
font: Tokens.font.icon.medium
Layout.alignment: Qt.AlignVCenter
visible: ProcessesColumns.hiddenColumns.length > 0
onClicked: addMenu.expanded = true
}
delegate: ColumnHeaderCell {
parentModel: columnsVisual
}
Variants {
id: hiddenColumnList
model: ProcessesColumns.hiddenColumns
HiddenColumnItem {}
}
Menu {
id: addMenu
attachTo: addColumnBtn
items: hiddenColumnList.instances
onItemSelected: item => ProcessesColumns.addColumn(item.modelData.id)
model: ScriptModel {
id: columnsData
values: Config.dashboard.processColumns.values
}
}
}
@@ -178,26 +152,9 @@ Item {
property int geometryTick: 0
property int activeGapIndex: -1
anchors.fill: rowLayout
anchors.fill: columnList
z: 10
Connections {
target: columnsData
function onValuesChanged() {
resizeOverlay.geometryTick++;
}
}
Connections {
target: columnList
function onCountChanged() {
resizeOverlay.geometryTick++;
}
function onContentWidthChanged() {
resizeOverlay.geometryTick++;
}
}
Component.onCompleted: Qt.callLater(() => geometryTick++)
Repeater {
@@ -571,8 +528,6 @@ Item {
const rightMid = rightItem.x + rightItem.width / 2;
if (refX > rightMid) {
columnsVisual.items.move(myIndex, myIndex + 1);
resizeOverlay.geometryTick++;
Qt.callLater(() => resizeOverlay.geometryTick++);
return;
}
}
@@ -583,8 +538,6 @@ Item {
const pinnedAtZero = columnList.itemAtIndex(0)?.modelData?.id === ProcessesColumns.pinnedColumnId;
if (refX < leftMid && !(myIndex === 1 && pinnedAtZero)) {
columnsVisual.items.move(myIndex, myIndex - 1);
resizeOverlay.geometryTick++;
Qt.callLater(() => resizeOverlay.geometryTick++);
}
}
}
@@ -0,0 +1,86 @@
pragma Singleton
import QtQuick
import Quickshell
import ZShell.Internal
import ZShell.Services
Singleton {
id: root
readonly property int historyLength: 30
readonly property CircularBuffer cpuBuffer: _cpuBuffer
readonly property CircularBuffer memoryBuffer: _memoryBuffer
readonly property list<var> cpuInfo: [
{
label: qsTr("Usage"),
value: `${Math.round(Cpu.percentage * 100)}%`
},
{
label: qsTr("Temp"),
value: `${Math.ceil(Cpu.temperature)}°${"C"}`
}
]
readonly property list<var> memoryInfo: [
{
label: qsTr("Used"),
value: memoryUsageFormat(Memory.used, Memory.total)
},
{
label: qsTr("Cached"),
value: memoryTotalFormat(Memory.cached)
},
{
label: qsTr("Writes"),
value: memoryTotalFormat(Memory.writeTotal)
},
{
label: qsTr("Reads"),
value: memoryTotalFormat(Memory.readTotal)
}
]
function memoryTotalFormat(kib: int): string {
const fmt = UsageFmt.formatKib(kib);
return `${fmt.value.toFixed(1)} ${fmt.unit}`;
}
function memoryUsageFormat(used: int, total: int): string {
const fmt = UsageFmt.formatKib(used, total);
return `${fmt.value.toFixed(1)} / ${Math.floor(fmt.total)} ${fmt.unit}`;
}
Connections {
function onUpdateTriggered(): void {
if (Cpu.percentage >= 0 && isFinite(Cpu.percentage))
_cpuBuffer.push(Cpu.percentage);
}
target: Cpu
}
Connections {
function onUpdateTriggered(): void {
if (Memory.percentage >= 0 && isFinite(Memory.percentage))
_memoryBuffer.push(Memory.percentage);
}
target: Memory
}
CircularBuffer {
id: _cpuBuffer
capacity: root.historyLength + 1
smoothingRadius: 2
}
CircularBuffer {
id: _memoryBuffer
capacity: root.historyLength + 1
smoothingRadius: 2
}
}
@@ -7,7 +7,6 @@ import ZShell.Components
import ZShell.Services
import qs.Components
import qs.Services
import qs.Helpers
Item {
id: root
-13
View File
@@ -9,11 +9,6 @@ import qs.Services
Item {
id: root
readonly property var desktopEntries: DesktopEntries.applications.values.map(e => ({
icon: e.icon,
command: e.command
}))
implicitWidth: Tokens.sizes.dashboard.processesWidth
implicitHeight: Tokens.sizes.dashboard.processesHeight
@@ -21,14 +16,6 @@ Item {
service: Processes
}
Connections {
target: DesktopEntries.applications
function onValuesChanged(): void {
Processes.setDesktopEntries(root.desktopEntries);
}
}
Component.onCompleted: Processes.setDesktopEntries(root.desktopEntries)
ProcessesHeader {
id: header
+8 -8
View File
@@ -1,8 +1,8 @@
import QtQuick
import QtQuick.Layouts
import ZShell.Components
import ZShell.Services
import ZShell.Config
import qs.Helpers
import qs.Components
import qs.Services
@@ -14,7 +14,7 @@ CustomRect {
implicitWidth: 300
radius: Tokens.rounding.largeIncreased
Ref {
ServiceRef {
service: NetworkUsage
}
@@ -129,8 +129,8 @@ CustomRect {
CustomText {
color: Colors.palette.m3tertiary
text: {
const fmt = NetworkUsage.formatBytes(NetworkUsage.downloadSpeed ?? 0);
return fmt ? `${fmt.value.toFixed(1)} ${fmt.unit}` : "0.0 B/s";
const fmt = UsageFmt.formatBytes(NetworkUsage.downloadSpeed ?? 0);
return fmt ? `${fmt.value.toFixed(1)} ${fmt.unit}/s` : "0.0 B/s";
}
}
}
@@ -157,8 +157,8 @@ CustomRect {
CustomText {
color: Colors.palette.m3secondary
text: {
const fmt = NetworkUsage.formatBytes(NetworkUsage.uploadSpeed ?? 0);
return fmt ? `${fmt.value.toFixed(1)} ${fmt.unit}` : "0.0 B/s";
const fmt = UsageFmt.formatBytes(NetworkUsage.uploadSpeed ?? 0);
return fmt ? `${fmt.value.toFixed(1)} ${fmt.unit}/s` : "0.0 B/s";
}
}
}
@@ -185,8 +185,8 @@ CustomRect {
CustomText {
color: Colors.palette.m3onSurfaceVariant
text: {
const down = NetworkUsage.formatBytesTotal(NetworkUsage.downloadTotal ?? 0);
const up = NetworkUsage.formatBytesTotal(NetworkUsage.uploadTotal ?? 0);
const down = UsageFmt.formatBytes(NetworkUsage.downloadTotal ?? 0);
const up = UsageFmt.formatBytes(NetworkUsage.uploadTotal ?? 0);
return (down && up) ? `↓${down.value.toFixed(1)}${down.unit} ↑${up.value.toFixed(1)}${up.unit}` : "↓0.0B ↑0.0B";
}
}
@@ -34,6 +34,45 @@ PageBase {
onToggled: Config.dashboard.enabled = checked
}
// Tabs
SectionHeader {
text: qsTr("Tabs")
}
ToggleRow {
first: true
checked: Config.dashboard.showDashboard
settingAnchor: "dashboard-show-dashboard"
text: qsTr("Dashboard")
onToggled: Config.dashboard.showDashboard = checked
}
ToggleRow {
checked: Config.dashboard.showWeather
settingAnchor: "dashboard-show-weather"
text: qsTr("Weather")
onToggled: Config.dashboard.showWeather = checked
}
ToggleRow {
checked: Config.dashboard.showMedia
settingAnchor: "dashboard-show-media"
text: qsTr("Media")
onToggled: Config.dashboard.showMedia = checked
}
ToggleRow {
checked: Config.dashboard.showProcesses
settingAnchor: "dashboard-show-processes"
text: qsTr("Processes")
last: true
onToggled: Config.dashboard.showProcesses = checked
}
// Behavior
SectionHeader {
text: qsTr("Behavior")
@@ -58,6 +97,7 @@ PageBase {
text: qsTr("Drag threshold")
to: 100
value: Config.dashboard.dragThreshold
last: true
onMoved: v => Config.dashboard.dragThreshold = v
}