1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-29 16:02:51 -05:00

Add disk usage component for TopBar and ControlCenter

This commit is contained in:
bbedward
2025-09-24 18:06:46 -04:00
parent 9be7d44765
commit b893694977
14 changed files with 676 additions and 12 deletions

View File

@@ -171,7 +171,8 @@ Singleton {
"enabled": true, "enabled": true,
"size": 20, "size": 20,
"selectedGpuIndex": 0, "selectedGpuIndex": 0,
"pciId": "" "pciId": "",
"mountPath": "/"
} }
leftWidgetsModel.append(dummyItem) leftWidgetsModel.append(dummyItem)
centerWidgetsModel.append(dummyItem) centerWidgetsModel.append(dummyItem)
@@ -741,6 +742,7 @@ Singleton {
var size = typeof order[i] === "string" ? undefined : order[i].size var size = typeof order[i] === "string" ? undefined : order[i].size
var selectedGpuIndex = typeof order[i] === "string" ? undefined : order[i].selectedGpuIndex var selectedGpuIndex = typeof order[i] === "string" ? undefined : order[i].selectedGpuIndex
var pciId = typeof order[i] === "string" ? undefined : order[i].pciId var pciId = typeof order[i] === "string" ? undefined : order[i].pciId
var mountPath = typeof order[i] === "string" ? undefined : order[i].mountPath
var item = { var item = {
"widgetId": widgetId, "widgetId": widgetId,
"enabled": enabled "enabled": enabled
@@ -751,6 +753,8 @@ Singleton {
item.selectedGpuIndex = selectedGpuIndex item.selectedGpuIndex = selectedGpuIndex
if (pciId !== undefined) if (pciId !== undefined)
item.pciId = pciId item.pciId = pciId
if (mountPath !== undefined)
item.mountPath = mountPath
listModel.append(item) listModel.append(item)
} }

View File

@@ -6,12 +6,14 @@ Item {
id: root id: root
property string expandedSection: "" property string expandedSection: ""
property var expandedWidgetData: null
Loader { Loader {
width: parent.width width: parent.width
height: 250 height: 250
y: Theme.spacingS y: Theme.spacingS
active: parent.height > 0 active: parent.height > 0
property string sectionKey: root.expandedSection
sourceComponent: { sourceComponent: {
switch (root.expandedSection) { switch (root.expandedSection) {
case "network": case "network":
@@ -20,9 +22,17 @@ Item {
case "audioOutput": return audioOutputDetailComponent case "audioOutput": return audioOutputDetailComponent
case "audioInput": return audioInputDetailComponent case "audioInput": return audioInputDetailComponent
case "battery": return batteryDetailComponent case "battery": return batteryDetailComponent
default: return null default:
if (root.expandedSection.startsWith("diskUsage_")) {
return diskUsageDetailComponent
}
return null
} }
} }
onSectionKeyChanged: {
active = false
active = true
}
} }
Component { Component {
@@ -49,4 +59,28 @@ Item {
id: batteryDetailComponent id: batteryDetailComponent
BatteryDetail {} BatteryDetail {}
} }
Component {
id: diskUsageDetailComponent
DiskUsageDetail {
currentMountPath: root.expandedWidgetData?.mountPath || "/"
instanceId: root.expandedWidgetData?.instanceId || ""
onMountPathChanged: (newMountPath) => {
if (root.expandedWidgetData && root.expandedWidgetData.id === "diskUsage") {
const widgets = SettingsData.controlCenterWidgets || []
const newWidgets = widgets.map(w => {
if (w.id === "diskUsage" && w.instanceId === root.expandedWidgetData.instanceId) {
const updatedWidget = Object.assign({}, w)
updatedWidget.mountPath = newMountPath
return updatedWidget
}
return w
})
SettingsData.setControlCenterWidgets(newWidgets)
}
}
}
}
} }

View File

@@ -12,6 +12,7 @@ Column {
property string expandedSection: "" property string expandedSection: ""
property int expandedWidgetIndex: -1 property int expandedWidgetIndex: -1
property var model: null property var model: null
property var expandedWidgetData: null
signal expandClicked(var widgetData, int globalIndex) signal expandClicked(var widgetData, int globalIndex)
signal removeWidget(int index) signal removeWidget(int index)
@@ -28,12 +29,17 @@ Column {
return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex) return LayoutUtils.calculateRowsAndWidgets(root, expandedSection, expandedWidgetIndex)
} }
property var layoutResult: {
const dummy = [expandedSection, expandedWidgetIndex, model?.controlCenterWidgets]
return calculateRowsAndWidgets()
}
onLayoutResultChanged: {
expandedRowIndex = layoutResult.expandedRowIndex
}
Repeater { Repeater {
model: { model: root.layoutResult.rows
const result = root.calculateRowsAndWidgets()
root.expandedRowIndex = result.expandedRowIndex
return result.rows
}
Column { Column {
width: root.width width: root.width
@@ -102,6 +108,8 @@ Column {
return inputAudioSliderComponent return inputAudioSliderComponent
} else if (id === "battery") { } else if (id === "battery") {
return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent return widgetWidth <= 25 ? smallBatteryComponent : batteryPillComponent
} else if (id === "diskUsage") {
return diskUsagePillComponent
} else { } else {
return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent return widgetWidth <= 25 ? smallToggleComponent : toggleButtonComponent
} }
@@ -115,9 +123,19 @@ Column {
DetailHost { DetailHost {
width: parent.width width: parent.width
height: active ? (250 + Theme.spacingS) : 0 height: active ? (250 + Theme.spacingS) : 0
property bool active: root.expandedSection !== "" && rowIndex === root.expandedRowIndex property bool active: {
if (root.expandedSection === "") return false
if (root.expandedSection.startsWith("diskUsage_") && root.expandedWidgetData) {
const expandedInstanceId = root.expandedWidgetData.instanceId
return rowWidgets.some(w => w.id === "diskUsage" && w.instanceId === expandedInstanceId)
}
return rowIndex === root.expandedRowIndex
}
visible: active visible: active
expandedSection: root.expandedSection expandedSection: root.expandedSection
expandedWidgetData: root.expandedWidgetData
} }
} }
} }
@@ -676,4 +694,35 @@ Column {
} }
} }
} }
Component {
id: diskUsagePillComponent
DiskUsagePill {
property var widgetData: parent.widgetData || {}
property int widgetIndex: parent.widgetIndex || 0
width: parent.width
height: 60
mountPath: widgetData.mountPath || "/"
instanceId: widgetData.instanceId || ""
onExpandClicked: {
if (!root.editMode) {
root.expandClicked(widgetData, widgetIndex)
}
}
EditModeOverlay {
anchors.fill: parent
editMode: root.editMode
widgetData: parent.widgetData
widgetIndex: parent.widgetIndex
showSizeControls: true
isSlider: false
onRemoveWidget: (index) => root.removeWidget(index)
onToggleWidgetSize: (index) => root.toggleWidgetSize(index)
onMoveWidget: (fromIndex, toIndex) => root.moveWidget(fromIndex, toIndex)
}
}
}
} }

View File

@@ -26,10 +26,29 @@ DankPopout {
property var triggerScreen: null property var triggerScreen: null
property bool editMode: false property bool editMode: false
property int expandedWidgetIndex: -1 property int expandedWidgetIndex: -1
property var expandedWidgetData: null
signal powerActionRequested(string action, string title, string message) signal powerActionRequested(string action, string title, string message)
signal lockRequested signal lockRequested
function collapseAll() {
expandedSection = ""
expandedWidgetIndex = -1
expandedWidgetData = null
}
onEditModeChanged: {
if (editMode) {
collapseAll()
}
}
onVisibleChanged: {
if (!visible) {
collapseAll()
}
}
readonly property color _containerBg: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60) readonly property color _containerBg: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.60)
function setTriggerPosition(x, y, width, section, screen) { function setTriggerPosition(x, y, width, section, screen) {
@@ -132,10 +151,16 @@ DankPopout {
editMode: root.editMode editMode: root.editMode
expandedSection: root.expandedSection expandedSection: root.expandedSection
expandedWidgetIndex: root.expandedWidgetIndex expandedWidgetIndex: root.expandedWidgetIndex
expandedWidgetData: root.expandedWidgetData
model: widgetModel model: widgetModel
onExpandClicked: (widgetData, globalIndex) => { onExpandClicked: (widgetData, globalIndex) => {
root.expandedWidgetIndex = globalIndex root.expandedWidgetIndex = globalIndex
root.toggleSection(widgetData.id) root.expandedWidgetData = widgetData
if (widgetData.id === "diskUsage") {
root.toggleSection("diskUsage_" + (widgetData.instanceId || "default"))
} else {
root.toggleSection(widgetData.id)
}
} }
onRemoveWidget: (index) => widgetModel.removeWidget(index) onRemoveWidget: (index) => widgetModel.removeWidget(index)
onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex) onMoveWidget: (fromIndex, toIndex) => widgetModel.moveWidget(fromIndex, toIndex)
@@ -147,7 +172,7 @@ DankPopout {
visible: editMode visible: editMode
availableWidgets: { availableWidgets: {
const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id) const existingIds = (SettingsData.controlCenterWidgets || []).map(w => w.id)
return widgetModel.baseWidgetDefinitions.filter(w => !existingIds.includes(w.id)) return widgetModel.baseWidgetDefinitions.filter(w => w.allowMultiple || !existingIds.includes(w.id))
} }
onAddWidget: (widgetId) => widgetModel.addWidget(widgetId) onAddWidget: (widgetId) => widgetModel.addWidget(widgetId)
onResetToDefault: () => widgetModel.resetToDefault() onResetToDefault: () => widgetModel.resetToDefault()

View File

@@ -0,0 +1,169 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
property string currentMountPath: "/"
property string instanceId: ""
signal mountPathChanged(string newMountPath)
implicitHeight: diskContent.height + Theme.spacingM
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, Theme.getContentBackgroundAlpha() * 0.6)
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
border.width: 1
Component.onCompleted: {
DgopService.addRef(["diskmounts"])
}
Component.onDestruction: {
DgopService.removeRef(["diskmounts"])
}
DankFlickable {
id: diskContent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: Theme.spacingM
anchors.topMargin: Theme.spacingM
contentHeight: diskColumn.height
clip: true
Column {
id: diskColumn
width: parent.width
spacing: Theme.spacingS
Item {
width: parent.width
height: 100
visible: !DgopService.dgopAvailable || !DgopService.diskMounts || DgopService.diskMounts.length === 0
Column {
anchors.centerIn: parent
spacing: Theme.spacingM
DankIcon {
anchors.horizontalCenter: parent.horizontalCenter
name: DgopService.dgopAvailable ? "storage" : "error"
size: 32
color: DgopService.dgopAvailable ? Theme.primary : Theme.error
}
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
text: DgopService.dgopAvailable ? "No disk data available" : "dgop not available"
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
horizontalAlignment: Text.AlignHCenter
}
}
}
Repeater {
model: DgopService.diskMounts || []
delegate: Rectangle {
required property var modelData
required property int index
width: parent.width
height: 80
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, index % 2 === 0 ? 0.3 : 0.2)
border.color: modelData.mount === currentMountPath ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.12)
border.width: modelData.mount === currentMountPath ? 2 : 1
Row {
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
anchors.leftMargin: Theme.spacingM
spacing: Theme.spacingM
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 2
DankIcon {
name: "storage"
size: Theme.iconSize
color: {
const percentStr = modelData.percent?.replace("%", "") || "0"
const percent = parseFloat(percentStr) || 0
if (percent > 90) return Theme.error
if (percent > 75) return Theme.warning
return modelData.mount === currentMountPath ? Theme.primary : Theme.surfaceText
}
anchors.horizontalCenter: parent.horizontalCenter
}
StyledText {
text: {
const percentStr = modelData.percent?.replace("%", "") || "0"
const percent = parseFloat(percentStr) || 0
return percent.toFixed(0) + "%"
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
width: parent.parent.width - parent.parent.anchors.leftMargin - parent.spacing - 50 - Theme.spacingM
StyledText {
text: modelData.mount === "/" ? "Root Filesystem" : modelData.mount
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
font.weight: modelData.mount === currentMountPath ? Font.Medium : Font.Normal
elide: Text.ElideRight
width: parent.width
}
StyledText {
text: modelData.mount
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideRight
width: parent.width
visible: modelData.mount !== "/"
}
StyledText {
text: `${modelData.used || "?"} / ${modelData.size || "?"}`
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
elide: Text.ElideRight
width: parent.width
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
if (modelData.mount !== currentMountPath) {
currentMountPath = modelData.mount
mountPathChanged(modelData.mount)
}
}
}
Behavior on border.color {
ColorAnimation { duration: Theme.shortDuration }
}
}
}
}
}
}

View File

@@ -106,6 +106,16 @@ QtObject {
"icon": "battery_std", "icon": "battery_std",
"type": "action", "type": "action",
"enabled": true "enabled": true
},
{
"id": "diskUsage",
"text": "Disk Usage",
"description": "Filesystem usage monitoring",
"icon": "storage",
"type": "action",
"enabled": DgopService.dgopAvailable,
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined,
"allowMultiple": true
} }
] ]

View File

@@ -0,0 +1,78 @@
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.ControlCenter.Widgets
CompoundPill {
id: root
property string mountPath: "/"
property string instanceId: ""
iconName: "storage"
property var selectedMount: {
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
return null
}
const targetMount = DgopService.diskMounts.find(mount => mount.mount === mountPath)
return targetMount || DgopService.diskMounts.find(mount => mount.mount === "/") || DgopService.diskMounts[0]
}
property real usagePercent: {
if (!selectedMount || !selectedMount.percent) {
return 0
}
const percentStr = selectedMount.percent.replace("%", "")
return parseFloat(percentStr) || 0
}
isActive: DgopService.dgopAvailable && selectedMount !== null
primaryText: {
if (!DgopService.dgopAvailable) {
return "Disk Usage"
}
if (!selectedMount) {
return "No disk data"
}
return `Disk Usage ${selectedMount.mount}`
}
secondaryText: {
if (!DgopService.dgopAvailable) {
return "dgop not available"
}
if (!selectedMount) {
return "No disk data available"
}
return `${selectedMount.used} / ${selectedMount.size} (${usagePercent.toFixed(0)}%)`
}
iconColor: {
if (!DgopService.dgopAvailable || !selectedMount) {
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
}
if (usagePercent > 90) {
return Theme.error
}
if (usagePercent > 75) {
return Theme.warning
}
return Theme.surfaceText
}
Component.onCompleted: {
DgopService.addRef(["diskmounts"])
}
Component.onDestruction: {
DgopService.removeRef(["diskmounts"])
}
onToggled: {
expandClicked()
}
}

View File

@@ -32,7 +32,7 @@ function calculateRowsAndWidgets(controlCenterColumn, expandedSection, expandedW
currentWidth += (currentRow.length > 1 ? spacing : 0) + itemWidth currentWidth += (currentRow.length > 1 ? spacing : 0) + itemWidth
} }
if (widget.id === expandedSection && expandedWidgetIndex === i) { if (expandedWidgetIndex === i) {
expandedRow = rows.length expandedRow = rows.length
} }
} }

View File

@@ -9,10 +9,20 @@ function addWidget(widgetId) {
"enabled": true, "enabled": true,
"width": 50 "width": 50
} }
if (widgetId === "diskUsage") {
widget.instanceId = generateUniqueId()
widget.mountPath = "/"
}
widgets.push(widget) widgets.push(widget)
SettingsData.setControlCenterWidgets(widgets) SettingsData.setControlCenterWidgets(widgets)
} }
function generateUniqueId() {
return Date.now().toString(36) + Math.random().toString(36).substr(2)
}
function removeWidget(index) { function removeWidget(index) {
var widgets = SettingsData.controlCenterWidgets.slice() var widgets = SettingsData.controlCenterWidgets.slice()
if (index >= 0 && index < widgets.length) { if (index >= 0 && index < widgets.length) {

View File

@@ -67,6 +67,13 @@ Item {
"id": "memUsage", "id": "memUsage",
"text": "Memory Usage", "text": "Memory Usage",
"description": "Memory usage indicator", "description": "Memory usage indicator",
"icon": "developer_board",
"enabled": DgopService.dgopAvailable,
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined
}, {
"id": "diskUsage",
"text": "Disk Usage",
"description": "Percentage",
"icon": "storage", "icon": "storage",
"enabled": DgopService.dgopAvailable, "enabled": DgopService.dgopAvailable,
"warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined "warning": !DgopService.dgopAvailable ? "Requires 'dgop' tool" : undefined
@@ -223,6 +230,9 @@ Item {
widgetObj.showBluetoothIcon = true widgetObj.showBluetoothIcon = true
widgetObj.showAudioIcon = true widgetObj.showAudioIcon = true
} }
if (widgetId === "diskUsage") {
widgetObj.mountPath = "/"
}
var widgets = [] var widgets = []
if (targetSection === "left") { if (targetSection === "left") {
@@ -412,6 +422,52 @@ Item {
SettingsData.setTopBarRightWidgets(widgets) SettingsData.setTopBarRightWidgets(widgets)
} }
function handleDiskMountSelectionChanged(sectionId, widgetIndex, mountPath) {
var widgets = []
if (sectionId === "left")
widgets = SettingsData.topBarLeftWidgets.slice()
else if (sectionId === "center")
widgets = SettingsData.topBarCenterWidgets.slice()
else if (sectionId === "right")
widgets = SettingsData.topBarRightWidgets.slice()
if (widgetIndex >= 0 && widgetIndex < widgets.length) {
var widget = widgets[widgetIndex]
if (typeof widget === "string") {
widgets[widgetIndex] = {
"id": widget,
"enabled": true,
"mountPath": mountPath
}
} else {
var newWidget = {
"id": widget.id,
"enabled": widget.enabled,
"mountPath": mountPath
}
if (widget.size !== undefined)
newWidget.size = widget.size
if (widget.selectedGpuIndex !== undefined)
newWidget.selectedGpuIndex = widget.selectedGpuIndex
if (widget.pciId !== undefined)
newWidget.pciId = widget.pciId
if (widget.id === "controlCenterButton") {
newWidget.showNetworkIcon = widget.showNetworkIcon !== undefined ? widget.showNetworkIcon : true
newWidget.showBluetoothIcon = widget.showBluetoothIcon !== undefined ? widget.showBluetoothIcon : true
newWidget.showAudioIcon = widget.showAudioIcon !== undefined ? widget.showAudioIcon : true
}
widgets[widgetIndex] = newWidget
}
}
if (sectionId === "left")
SettingsData.setTopBarLeftWidgets(widgets)
else if (sectionId === "center")
SettingsData.setTopBarCenterWidgets(widgets)
else if (sectionId === "right")
SettingsData.setTopBarRightWidgets(widgets)
}
function handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value) { function handleControlCenterSettingChanged(sectionId, widgetIndex, settingName, value) {
// Control Center settings are global, not per-widget instance // Control Center settings are global, not per-widget instance
if (settingName === "showNetworkIcon") { if (settingName === "showNetworkIcon") {
@@ -441,6 +497,8 @@ Item {
=== "string" ? undefined : widget.selectedGpuIndex === "string" ? undefined : widget.selectedGpuIndex
var widgetPciId = typeof widget var widgetPciId = typeof widget
=== "string" ? undefined : widget.pciId === "string" ? undefined : widget.pciId
var widgetMountPath = typeof widget
=== "string" ? undefined : widget.mountPath
var widgetShowNetworkIcon = typeof widget === "string" ? undefined : widget.showNetworkIcon var widgetShowNetworkIcon = typeof widget === "string" ? undefined : widget.showNetworkIcon
var widgetShowBluetoothIcon = typeof widget === "string" ? undefined : widget.showBluetoothIcon var widgetShowBluetoothIcon = typeof widget === "string" ? undefined : widget.showBluetoothIcon
var widgetShowAudioIcon = typeof widget === "string" ? undefined : widget.showAudioIcon var widgetShowAudioIcon = typeof widget === "string" ? undefined : widget.showAudioIcon
@@ -456,6 +514,8 @@ Item {
item.selectedGpuIndex = widgetSelectedGpuIndex item.selectedGpuIndex = widgetSelectedGpuIndex
if (widgetPciId !== undefined) if (widgetPciId !== undefined)
item.pciId = widgetPciId item.pciId = widgetPciId
if (widgetMountPath !== undefined)
item.mountPath = widgetMountPath
if (widgetShowNetworkIcon !== undefined) if (widgetShowNetworkIcon !== undefined)
item.showNetworkIcon = widgetShowNetworkIcon item.showNetworkIcon = widgetShowNetworkIcon
if (widgetShowBluetoothIcon !== undefined) if (widgetShowBluetoothIcon !== undefined)
@@ -1045,6 +1105,10 @@ Item {
sectionId, widgetIndex, sectionId, widgetIndex,
selectedIndex) selectedIndex)
} }
onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => {
topBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath)
}
} }
} }
@@ -1115,6 +1179,10 @@ Item {
sectionId, widgetIndex, sectionId, widgetIndex,
selectedIndex) selectedIndex)
} }
onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => {
topBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath)
}
} }
} }
@@ -1185,6 +1253,10 @@ Item {
sectionId, widgetIndex, sectionId, widgetIndex,
selectedIndex) selectedIndex)
} }
onDiskMountSelectionChanged: (sectionId, widgetIndex, mountPath) => {
topBarTab.handleDiskMountSelectionChanged(
sectionId, widgetIndex, mountPath)
}
} }
} }
} }

View File

@@ -20,6 +20,7 @@ Column {
signal spacerSizeChanged(string sectionId, int widgetIndex, int newSize) signal spacerSizeChanged(string sectionId, int widgetIndex, int newSize)
signal compactModeChanged(string widgetId, var value) signal compactModeChanged(string widgetId, var value)
signal gpuSelectionChanged(string sectionId, int widgetIndex, int selectedIndex) signal gpuSelectionChanged(string sectionId, int widgetIndex, int selectedIndex)
signal diskMountSelectionChanged(string sectionId, int widgetIndex, string mountPath)
signal controlCenterSettingChanged(string sectionId, int widgetIndex, string settingName, bool value) signal controlCenterSettingChanged(string sectionId, int widgetIndex, string settingName, bool value)
width: parent.width width: parent.width
@@ -187,6 +188,38 @@ Column {
} }
} }
Item {
width: 120
height: 32
visible: modelData.id === "diskUsage"
DankDropdown {
id: diskMountDropdown
anchors.fill: parent
currentValue: {
const mountPath = modelData.mountPath || "/"
if (mountPath === "/") {
return "root (/)"
}
return mountPath
}
options: {
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
return ["root (/)"]
}
return DgopService.diskMounts.map(mount => {
if (mount.mount === "/") {
return "root (/)"
}
return mount.mount
})
}
onValueChanged: value => {
const newPath = value === "root (/)" ? "/" : value
root.diskMountSelectionChanged(root.sectionId, index, newPath)
}
}
}
Item { Item {
width: 32 width: 32
height: 32 height: 32

View File

@@ -0,0 +1,171 @@
import QtQuick
import QtQuick.Controls
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property var widgetData: null
property real widgetHeight: 30
property string mountPath: (widgetData && widgetData.mountPath !== undefined) ? widgetData.mountPath : "/"
readonly property real horizontalPadding: SettingsData.topBarNoBackground ? 0 : Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
property var selectedMount: {
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
return null
}
// Force re-evaluation when mountPath changes
const currentMountPath = root.mountPath || "/"
// First try to find exact match
for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === currentMountPath) {
return DgopService.diskMounts[i]
}
}
// Fallback to root
for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === "/") {
return DgopService.diskMounts[i]
}
}
// Last resort - first mount
return DgopService.diskMounts[0] || null
}
property real diskUsagePercent: {
if (!selectedMount || !selectedMount.percent) {
return 0
}
const percentStr = selectedMount.percent.replace("%", "")
return parseFloat(percentStr) || 0
}
width: diskContent.implicitWidth + horizontalPadding * 2
height: widgetHeight
radius: SettingsData.topBarNoBackground ? 0 : Theme.cornerRadius
color: {
if (SettingsData.topBarNoBackground) {
return "transparent"
}
const baseColor = Theme.widgetBaseBackgroundColor
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b, baseColor.a * Theme.widgetTransparency)
}
Component.onCompleted: {
DgopService.addRef(["diskmounts"])
}
Component.onDestruction: {
DgopService.removeRef(["diskmounts"])
}
Connections {
function onWidgetDataChanged() {
// Force property re-evaluation by triggering change detection
root.mountPath = Qt.binding(() => {
return (root.widgetData && root.widgetData.mountPath !== undefined) ? root.widgetData.mountPath : "/"
})
root.selectedMount = Qt.binding(() => {
if (!DgopService.diskMounts || DgopService.diskMounts.length === 0) {
return null
}
const currentMountPath = root.mountPath || "/"
// First try to find exact match
for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === currentMountPath) {
return DgopService.diskMounts[i]
}
}
// Fallback to root
for (let i = 0; i < DgopService.diskMounts.length; i++) {
if (DgopService.diskMounts[i].mount === "/") {
return DgopService.diskMounts[i]
}
}
// Last resort - first mount
return DgopService.diskMounts[0] || null
})
}
target: SettingsData
}
Row {
id: diskContent
anchors.centerIn: parent
spacing: 3
DankIcon {
name: "storage"
size: Theme.iconSize - 8
color: {
if (root.diskUsagePercent > 90) {
return Theme.tempDanger
}
if (root.diskUsagePercent > 75) {
return Theme.tempWarning
}
return Theme.surfaceText
}
anchors.verticalCenter: parent.verticalCenter
}
StyledText {
text: {
if (!root.selectedMount) {
return "--"
}
return root.selectedMount.mount
}
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignLeft
elide: Text.ElideNone
}
StyledText {
text: {
if (root.diskUsagePercent === undefined || root.diskUsagePercent === null || root.diskUsagePercent === 0) {
return "--%"
}
return root.diskUsagePercent.toFixed(0) + "%"
}
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
horizontalAlignment: Text.AlignLeft
elide: Text.ElideNone
StyledTextMetrics {
id: diskBaseline
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
text: "100%"
}
width: Math.max(diskBaseline.width, paintedWidth)
Behavior on width {
NumberAnimation {
duration: 120
easing.type: Easing.OutCubic
}
}
}
}
}

View File

@@ -483,6 +483,7 @@ PanelWindow {
"clipboard": clipboardComponent, "clipboard": clipboardComponent,
"cpuUsage": cpuUsageComponent, "cpuUsage": cpuUsageComponent,
"memUsage": memUsageComponent, "memUsage": memUsageComponent,
"diskUsage": diskUsageComponent,
"cpuTemp": cpuTempComponent, "cpuTemp": cpuTempComponent,
"gpuTemp": gpuTempComponent, "gpuTemp": gpuTempComponent,
"notificationButton": notificationButtonComponent, "notificationButton": notificationButtonComponent,
@@ -1003,6 +1004,15 @@ PanelWindow {
} }
} }
Component {
id: diskUsageComponent
DiskUsage {
widgetHeight: root.widgetHeight
widgetData: parent.widgetData
}
}
Component { Component {
id: cpuTempComponent id: cpuTempComponent

View File

@@ -335,7 +335,6 @@ Rectangle {
onClicked: { onClicked: {
root.currentValue = modelData root.currentValue = modelData
root.valueChanged(modelData) root.valueChanged(modelData)
dropdownMenu.close()
} }
} }
} }