mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-24 21:42:51 -05:00
system monitor: overhaul popout and app with new design
This commit is contained in:
@@ -11,7 +11,8 @@ FloatingWindow {
|
|||||||
id: processListModal
|
id: processListModal
|
||||||
|
|
||||||
property int currentTab: 0
|
property int currentTab: 0
|
||||||
property var tabNames: ["Processes", "Performance", "System"]
|
property string searchText: ""
|
||||||
|
property string expandedPid: ""
|
||||||
property bool shouldHaveFocus: visible
|
property bool shouldHaveFocus: visible
|
||||||
property alias shouldBeVisible: processListModal.visible
|
property alias shouldBeVisible: processListModal.visible
|
||||||
|
|
||||||
@@ -27,10 +28,9 @@ FloatingWindow {
|
|||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
visible = false;
|
visible = false;
|
||||||
if (processContextMenu.visible) {
|
if (processContextMenu.visible)
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function toggle() {
|
function toggle() {
|
||||||
if (!DgopService.dgopAvailable) {
|
if (!DgopService.dgopAvailable) {
|
||||||
@@ -61,46 +61,39 @@ FloatingWindow {
|
|||||||
show();
|
show();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatBytes(bytes) {
|
||||||
|
if (bytes < 1024)
|
||||||
|
return bytes.toFixed(0) + " B/s";
|
||||||
|
if (bytes < 1024 * 1024)
|
||||||
|
return (bytes / 1024).toFixed(1) + " KB/s";
|
||||||
|
if (bytes < 1024 * 1024 * 1024)
|
||||||
|
return (bytes / (1024 * 1024)).toFixed(1) + " MB/s";
|
||||||
|
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB/s";
|
||||||
|
}
|
||||||
|
|
||||||
objectName: "processListModal"
|
objectName: "processListModal"
|
||||||
title: I18n.tr("System Monitor", "sysmon window title")
|
title: I18n.tr("System Monitor", "sysmon window title")
|
||||||
minimumSize: Qt.size(650, 400)
|
minimumSize: Qt.size(750, 550)
|
||||||
implicitWidth: 900
|
implicitWidth: 1000
|
||||||
implicitHeight: 680
|
implicitHeight: 720
|
||||||
color: Theme.surfaceContainer
|
color: Theme.surfaceContainer
|
||||||
visible: false
|
visible: false
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
if (!visible) {
|
if (!visible) {
|
||||||
closingModal();
|
closingModal();
|
||||||
|
searchText = "";
|
||||||
|
expandedPid = "";
|
||||||
|
DgopService.removeRef(["cpu", "memory", "network", "disk", "system"]);
|
||||||
} else {
|
} else {
|
||||||
|
DgopService.addRef(["cpu", "memory", "network", "disk", "system"]);
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => {
|
||||||
if (contentFocusScope) {
|
if (contentFocusScope)
|
||||||
contentFocusScope.forceActiveFocus();
|
contentFocusScope.forceActiveFocus();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: processesTabComponent
|
|
||||||
|
|
||||||
ProcessesTab {
|
|
||||||
contextMenu: processContextMenu
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: performanceTabComponent
|
|
||||||
|
|
||||||
PerformanceTab {}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: systemTabComponent
|
|
||||||
|
|
||||||
SystemTab {}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessContextMenu {
|
ProcessContextMenu {
|
||||||
id: processContextMenu
|
id: processContextMenu
|
||||||
}
|
}
|
||||||
@@ -128,6 +121,26 @@ FloatingWindow {
|
|||||||
currentTab = 2;
|
currentTab = 2;
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
return;
|
return;
|
||||||
|
case Qt.Key_4:
|
||||||
|
currentTab = 3;
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
case Qt.Key_Escape:
|
||||||
|
if (searchText.length > 0) {
|
||||||
|
searchText = "";
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
hide();
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
case Qt.Key_F:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
searchField.forceActiveFocus();
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +174,7 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: I18n.tr("The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature.")
|
text: I18n.tr("The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature.", "dgop unavailable error message")
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
color: Theme.surfaceText
|
color: Theme.surfaceText
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
@@ -171,14 +184,14 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Column {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
spacing: 0
|
spacing: 0
|
||||||
visible: DgopService.dgopAvailable
|
visible: DgopService.dgopAvailable
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
width: parent.width
|
Layout.fillWidth: true
|
||||||
height: 48
|
Layout.preferredHeight: 48
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
@@ -233,36 +246,38 @@ FloatingWindow {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
RowLayout {
|
||||||
width: parent.width
|
Layout.fillWidth: true
|
||||||
height: parent.height - 48
|
Layout.preferredHeight: 52
|
||||||
|
Layout.leftMargin: Theme.spacingL
|
||||||
ColumnLayout {
|
Layout.rightMargin: Theme.spacingL
|
||||||
anchors.fill: parent
|
|
||||||
anchors.leftMargin: Theme.spacingL
|
|
||||||
anchors.rightMargin: Theme.spacingL
|
|
||||||
anchors.bottomMargin: Theme.spacingL
|
|
||||||
anchors.topMargin: 0
|
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
height: 52
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
border.color: Theme.outlineLight
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Row {
|
Row {
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 4
|
|
||||||
spacing: 2
|
spacing: 2
|
||||||
|
|
||||||
Repeater {
|
Repeater {
|
||||||
model: tabNames
|
model: [
|
||||||
|
{
|
||||||
|
text: I18n.tr("Processes"),
|
||||||
|
icon: "list_alt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: I18n.tr("Performance"),
|
||||||
|
icon: "analytics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: I18n.tr("Disks"),
|
||||||
|
icon: "storage"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: I18n.tr("System"),
|
||||||
|
icon: "computer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length
|
width: 120
|
||||||
height: 44
|
height: 44
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
|
color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent")
|
||||||
@@ -274,47 +289,28 @@ FloatingWindow {
|
|||||||
spacing: Theme.spacingXS
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
DankIcon {
|
DankIcon {
|
||||||
name: {
|
name: modelData.icon
|
||||||
const tabIcons = ["list_alt", "analytics", "settings"];
|
|
||||||
return tabIcons[index] || "tab";
|
|
||||||
}
|
|
||||||
size: Theme.iconSize - 2
|
size: Theme.iconSize - 2
|
||||||
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
||||||
opacity: currentTab === index ? 1 : 0.7
|
opacity: currentTab === index ? 1 : 0.7
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StyledText {
|
StyledText {
|
||||||
text: modelData
|
text: modelData.text
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
font.weight: Font.Medium
|
font.weight: Font.Medium
|
||||||
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
color: currentTab === index ? Theme.primary : Theme.surfaceText
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
anchors.verticalCenterOffset: -1
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: tabMouseArea
|
id: tabMouseArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
onClicked: () => {
|
onClicked: currentTab = index
|
||||||
currentTab = index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Behavior on color {
|
Behavior on color {
|
||||||
@@ -322,77 +318,204 @@ FloatingWindow {
|
|||||||
duration: Theme.shortDuration
|
duration: Theme.shortDuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Behavior on border.color {
|
Item {
|
||||||
ColorAnimation {
|
Layout.fillWidth: true
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: searchField
|
||||||
|
Layout.preferredWidth: 250
|
||||||
|
Layout.preferredHeight: 40
|
||||||
|
placeholderText: I18n.tr("Search processes...", "process search placeholder")
|
||||||
|
leftIconName: "search"
|
||||||
|
showClearButton: true
|
||||||
|
text: searchText
|
||||||
|
visible: currentTab === 0
|
||||||
|
onTextChanged: searchText = text
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
Layout.margins: Theme.spacingL
|
||||||
|
Layout.topMargin: Theme.spacingM
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
border.color: Theme.outlineLight
|
border.color: Theme.outlineLight
|
||||||
border.width: 1
|
border.width: 1
|
||||||
|
clip: true
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: processesTab
|
id: processesTabLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
active: processListModal.visible && currentTab === 0
|
active: processListModal.visible && currentTab === 0
|
||||||
visible: currentTab === 0
|
visible: currentTab === 0
|
||||||
opacity: currentTab === 0 ? 1 : 0
|
sourceComponent: ProcessesView {
|
||||||
sourceComponent: processesTabComponent
|
searchText: processListModal.searchText
|
||||||
|
expandedPid: processListModal.expandedPid
|
||||||
Behavior on opacity {
|
contextMenu: processContextMenu
|
||||||
NumberAnimation {
|
onExpandedPidChanged: processListModal.expandedPid = expandedPid
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: performanceTab
|
id: performanceTabLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
active: processListModal.visible && currentTab === 1
|
active: processListModal.visible && currentTab === 1
|
||||||
visible: currentTab === 1
|
visible: currentTab === 1
|
||||||
opacity: currentTab === 1 ? 1 : 0
|
sourceComponent: PerformanceView {}
|
||||||
sourceComponent: performanceTabComponent
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
easing.type: Theme.emphasizedEasing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
Loader {
|
||||||
id: systemTab
|
id: disksTabLoader
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
active: processListModal.visible && currentTab === 2
|
active: processListModal.visible && currentTab === 2
|
||||||
visible: currentTab === 2
|
visible: currentTab === 2
|
||||||
opacity: currentTab === 2 ? 1 : 0
|
sourceComponent: DisksView {}
|
||||||
sourceComponent: systemTabComponent
|
}
|
||||||
|
|
||||||
Behavior on opacity {
|
Loader {
|
||||||
NumberAnimation {
|
id: systemTabLoader
|
||||||
duration: Theme.mediumDuration
|
anchors.fill: parent
|
||||||
easing.type: Theme.emphasizedEasing
|
anchors.margins: Theme.spacingS
|
||||||
|
active: processListModal.visible && currentTab === 3
|
||||||
|
visible: currentTab === 3
|
||||||
|
sourceComponent: SystemView {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 32
|
||||||
|
Layout.leftMargin: Theme.spacingL
|
||||||
|
Layout.rightMargin: Theme.spacingL
|
||||||
|
Layout.bottomMargin: Theme.spacingM
|
||||||
|
color: "transparent"
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Processes:", "process count label in footer")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.processCount.toString()
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Uptime:", "uptime label in footer")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.shortUptime || "--"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "swap_horiz"
|
||||||
|
size: 14
|
||||||
|
color: Theme.info
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "↓" + formatBytes(DgopService.networkRxRate) + " ↑" + formatBytes(DgopService.networkTxRate)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "storage"
|
||||||
|
size: 14
|
||||||
|
color: Theme.warning
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "↓" + formatBytes(DgopService.diskReadRate) + " ↑" + formatBytes(DgopService.diskWriteRate)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "memory"
|
||||||
|
size: 14
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.cpuUsage.toFixed(1) + "%"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: DgopService.cpuUsage > 80 ? Theme.error : Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "sd_card"
|
||||||
|
size: 14
|
||||||
|
color: Theme.secondary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.formatSystemMemory(DgopService.usedMemoryKB) + " / " + DgopService.formatSystemMemory(DgopService.totalMemoryKB)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: DgopService.memoryUsage > 90 ? Theme.error : Theme.surfaceText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
346
quickshell/Modules/ProcessList/DisksView.qml
Normal file
346
quickshell/Modules/ProcessList/DisksView.qml
Normal file
@@ -0,0 +1,346 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
function formatSpeed(bytesPerSec) {
|
||||||
|
if (bytesPerSec < 1024)
|
||||||
|
return bytesPerSec.toFixed(0) + " B/s";
|
||||||
|
if (bytesPerSec < 1024 * 1024)
|
||||||
|
return (bytesPerSec / 1024).toFixed(1) + " KB/s";
|
||||||
|
if (bytesPerSec < 1024 * 1024 * 1024)
|
||||||
|
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s";
|
||||||
|
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(2) + " GB/s";
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
DgopService.addRef(["disk", "diskmounts"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
DgopService.removeRef(["disk", "diskmounts"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 80
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingXL
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "storage"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Disk I/O", "disk io header in system monitor")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Read:", "disk read label")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: root.formatSpeed(DgopService.diskReadRate)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Write:", "disk write label")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: root.formatSpeed(DgopService.diskWriteRate)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.warning
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "folder"
|
||||||
|
size: Theme.iconSize - 2
|
||||||
|
color: Theme.secondary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Mount Points", "mount points header in system monitor")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
color: Theme.outlineLight
|
||||||
|
}
|
||||||
|
|
||||||
|
DankListView {
|
||||||
|
id: mountListView
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
model: DgopService.diskMounts
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: mountListView.width
|
||||||
|
height: 60
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: mountMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
||||||
|
|
||||||
|
readonly property real usedPct: {
|
||||||
|
const pctStr = modelData?.percent ?? "0%";
|
||||||
|
return parseFloat(pctStr.replace("%", "")) / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: mountMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: {
|
||||||
|
const mp = modelData?.mount ?? "";
|
||||||
|
if (mp === "/")
|
||||||
|
return "home";
|
||||||
|
if (mp === "/home")
|
||||||
|
return "person";
|
||||||
|
if (mp.includes("boot"))
|
||||||
|
return "memory";
|
||||||
|
if (mp.includes("media") || mp.includes("mnt"))
|
||||||
|
return "usb";
|
||||||
|
return "folder";
|
||||||
|
}
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: Theme.surfaceText
|
||||||
|
opacity: 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.mount ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.device ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.fstype ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Layout.preferredWidth: 200
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width
|
||||||
|
height: 8
|
||||||
|
radius: 4
|
||||||
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: parent.width * Math.min(1, parent.parent.parent.parent.usedPct)
|
||||||
|
height: parent.height
|
||||||
|
radius: 4
|
||||||
|
color: {
|
||||||
|
const pct = parent.parent.parent.parent.usedPct;
|
||||||
|
if (pct > 0.95)
|
||||||
|
return Theme.error;
|
||||||
|
if (pct > 0.85)
|
||||||
|
return Theme.warning;
|
||||||
|
return Theme.primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on width {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.right: parent.right
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.used ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "/"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.size ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
Layout.preferredWidth: 50
|
||||||
|
text: modelData?.percent ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: {
|
||||||
|
const pct = parent.parent.usedPct;
|
||||||
|
if (pct > 0.95)
|
||||||
|
return Theme.error;
|
||||||
|
if (pct > 0.85)
|
||||||
|
return Theme.warning;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 300
|
||||||
|
height: 80
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
visible: DgopService.diskMounts.length === 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "storage"
|
||||||
|
size: 32
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("No mount points found", "empty state in disk mounts list")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,451 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Column {
|
|
||||||
function formatNetworkSpeed(bytesPerSec) {
|
|
||||||
if (bytesPerSec < 1024) {
|
|
||||||
return bytesPerSec.toFixed(0) + " B/s";
|
|
||||||
} else if (bytesPerSec < 1024 * 1024) {
|
|
||||||
return (bytesPerSec / 1024).toFixed(1) + " KB/s";
|
|
||||||
} else if (bytesPerSec < 1024 * 1024 * 1024) {
|
|
||||||
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s";
|
|
||||||
} else {
|
|
||||||
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDiskSpeed(bytesPerSec) {
|
|
||||||
if (bytesPerSec < 1024 * 1024) {
|
|
||||||
return (bytesPerSec / 1024).toFixed(1) + " KB/s";
|
|
||||||
} else if (bytesPerSec < 1024 * 1024 * 1024) {
|
|
||||||
return (bytesPerSec / (1024 * 1024)).toFixed(1) + " MB/s";
|
|
||||||
} else {
|
|
||||||
return (bytesPerSec / (1024 * 1024 * 1024)).toFixed(1) + " GB/s";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
Component.onCompleted: {
|
|
||||||
DgopService.addRef(["cpu", "memory", "network", "disk"]);
|
|
||||||
}
|
|
||||||
Component.onDestruction: {
|
|
||||||
DgopService.removeRef(["cpu", "memory", "network", "disk"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 200
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: Theme.spacingM
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
height: 32
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "CPU"
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 80
|
|
||||||
height: 24
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `${DgopService.cpuUsage.toFixed(1)}%`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
width: parent.width - 280
|
|
||||||
height: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `${DgopService.cpuCores} cores`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DankFlickable {
|
|
||||||
clip: true
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height - 40
|
|
||||||
contentHeight: coreUsageColumn.implicitHeight
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: coreUsageColumn
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
spacing: 6
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: DgopService.perCoreCpuUsage
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
height: 20
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `C${index}`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
width: 24
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width - 80
|
|
||||||
height: 6
|
|
||||||
radius: 3
|
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width * Math.min(1, modelData / 100)
|
|
||||||
height: parent.height
|
|
||||||
radius: parent.radius
|
|
||||||
color: {
|
|
||||||
const usage = modelData;
|
|
||||||
if (usage > 80) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
if (usage > 60) {
|
|
||||||
return Theme.warning;
|
|
||||||
}
|
|
||||||
return Theme.primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData ? `${modelData.toFixed(0)}%` : "0%"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: 32
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
height: 80
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
|
||||||
height: 80
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Memory")
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `${DgopService.formatSystemMemory(DgopService.usedMemoryKB)} / ${DgopService.formatSystemMemory(DgopService.totalMemoryKB)}`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 4
|
|
||||||
width: 120
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 16
|
|
||||||
radius: 8
|
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: DgopService.totalMemoryKB > 0 ? parent.width * (DgopService.usedMemoryKB / DgopService.totalMemoryKB) : 0
|
|
||||||
height: parent.height
|
|
||||||
radius: parent.radius
|
|
||||||
color: {
|
|
||||||
const usage = DgopService.totalMemoryKB > 0 ? (DgopService.usedMemoryKB / DgopService.totalMemoryKB) : 0;
|
|
||||||
if (usage > 0.9) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
if (usage > 0.7) {
|
|
||||||
return Theme.warning;
|
|
||||||
}
|
|
||||||
return Theme.secondary;
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.totalMemoryKB > 0 ? `${((DgopService.usedMemoryKB / DgopService.totalMemoryKB) * 100).toFixed(1)}% used` : "No data"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
|
||||||
height: 80
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Swap")
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.totalSwapKB > 0 ? `${DgopService.formatSystemMemory(DgopService.usedSwapKB)} / ${DgopService.formatSystemMemory(DgopService.totalSwapKB)}` : "No swap"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceVariantText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 4
|
|
||||||
width: 120
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 16
|
|
||||||
radius: 8
|
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: DgopService.totalSwapKB > 0 ? parent.width * (DgopService.usedSwapKB / DgopService.totalSwapKB) : 0
|
|
||||||
height: parent.height
|
|
||||||
radius: parent.radius
|
|
||||||
color: {
|
|
||||||
if (!DgopService.totalSwapKB) {
|
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3);
|
|
||||||
}
|
|
||||||
const usage = DgopService.usedSwapKB / DgopService.totalSwapKB;
|
|
||||||
if (usage > 0.9) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
if (usage > 0.7) {
|
|
||||||
return Theme.warning;
|
|
||||||
}
|
|
||||||
return Theme.info;
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on width {
|
|
||||||
NumberAnimation {
|
|
||||||
duration: Theme.mediumDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.totalSwapKB > 0 ? `${((DgopService.usedSwapKB / DgopService.totalSwapKB) * 100).toFixed(1)}% used` : "N/A"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
height: 80
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
|
||||||
height: 80
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Network")
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "↓"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.info
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.networkRxRate > 0 ? formatNetworkSpeed(DgopService.networkRxRate) : "0 B/s"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "↑"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.error
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.networkTxRate > 0 ? formatNetworkSpeed(DgopService.networkTxRate) : "0 B/s"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingM) / 2
|
|
||||||
height: 80
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.06)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Disk")
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "R"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.primary
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: formatDiskSpeed(DgopService.diskReadRate)
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: 4
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "W"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.warning
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: formatDiskSpeed(DgopService.diskWriteRate)
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
304
quickshell/Modules/ProcessList/PerformanceView.qml
Normal file
304
quickshell/Modules/ProcessList/PerformanceView.qml
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
readonly property int historySize: 60
|
||||||
|
|
||||||
|
property var cpuHistory: []
|
||||||
|
property var memoryHistory: []
|
||||||
|
property var networkRxHistory: []
|
||||||
|
property var networkTxHistory: []
|
||||||
|
property var diskReadHistory: []
|
||||||
|
property var diskWriteHistory: []
|
||||||
|
|
||||||
|
function formatBytes(bytes) {
|
||||||
|
if (bytes < 1024)
|
||||||
|
return bytes.toFixed(0) + " B/s";
|
||||||
|
if (bytes < 1024 * 1024)
|
||||||
|
return (bytes / 1024).toFixed(1) + " KB/s";
|
||||||
|
if (bytes < 1024 * 1024 * 1024)
|
||||||
|
return (bytes / (1024 * 1024)).toFixed(1) + " MB/s";
|
||||||
|
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB/s";
|
||||||
|
}
|
||||||
|
|
||||||
|
function addToHistory(arr, val) {
|
||||||
|
const newArr = arr.slice();
|
||||||
|
newArr.push(val);
|
||||||
|
if (newArr.length > historySize)
|
||||||
|
newArr.shift();
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sampleData() {
|
||||||
|
cpuHistory = addToHistory(cpuHistory, DgopService.cpuUsage);
|
||||||
|
memoryHistory = addToHistory(memoryHistory, DgopService.memoryUsage);
|
||||||
|
networkRxHistory = addToHistory(networkRxHistory, DgopService.networkRxRate);
|
||||||
|
networkTxHistory = addToHistory(networkTxHistory, DgopService.networkTxRate);
|
||||||
|
diskReadHistory = addToHistory(diskReadHistory, DgopService.diskReadRate);
|
||||||
|
diskWriteHistory = addToHistory(diskWriteHistory, DgopService.diskWriteRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
DgopService.addRef(["cpu", "memory", "network", "disk", "diskmounts", "system"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
DgopService.removeRef(["cpu", "memory", "network", "disk", "diskmounts", "system"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemClock {
|
||||||
|
id: sampleClock
|
||||||
|
precision: SystemClock.Seconds
|
||||||
|
onDateChanged: {
|
||||||
|
if (date.getSeconds() % 1 === 0)
|
||||||
|
root.sampleData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: (root.height - Theme.spacingM * 2) / 2
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
PerformanceCard {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
title: "CPU"
|
||||||
|
icon: "memory"
|
||||||
|
value: DgopService.cpuUsage.toFixed(1) + "%"
|
||||||
|
subtitle: DgopService.cpuModel || (DgopService.cpuCores + " cores")
|
||||||
|
accentColor: Theme.primary
|
||||||
|
history: root.cpuHistory
|
||||||
|
maxValue: 100
|
||||||
|
showSecondary: false
|
||||||
|
extraInfo: DgopService.cpuTemperature > 0 ? (DgopService.cpuTemperature.toFixed(0) + "°C") : ""
|
||||||
|
extraInfoColor: DgopService.cpuTemperature > 80 ? Theme.error : (DgopService.cpuTemperature > 60 ? Theme.warning : Theme.surfaceVariantText)
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceCard {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
title: I18n.tr("Memory")
|
||||||
|
icon: "sd_card"
|
||||||
|
value: DgopService.memoryUsage.toFixed(1) + "%"
|
||||||
|
subtitle: DgopService.formatSystemMemory(DgopService.usedMemoryKB) + " / " + DgopService.formatSystemMemory(DgopService.totalMemoryKB)
|
||||||
|
accentColor: Theme.secondary
|
||||||
|
history: root.memoryHistory
|
||||||
|
maxValue: 100
|
||||||
|
showSecondary: false
|
||||||
|
extraInfo: DgopService.totalSwapKB > 0 ? ("Swap: " + DgopService.formatSystemMemory(DgopService.usedSwapKB)) : ""
|
||||||
|
extraInfoColor: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: (root.height - Theme.spacingM * 2) / 2
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
PerformanceCard {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
title: I18n.tr("Network")
|
||||||
|
icon: "swap_horiz"
|
||||||
|
value: "↓ " + root.formatBytes(DgopService.networkRxRate)
|
||||||
|
subtitle: "↑ " + root.formatBytes(DgopService.networkTxRate)
|
||||||
|
accentColor: Theme.info
|
||||||
|
history: root.networkRxHistory
|
||||||
|
history2: root.networkTxHistory
|
||||||
|
maxValue: 0
|
||||||
|
showSecondary: true
|
||||||
|
extraInfo: ""
|
||||||
|
extraInfoColor: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
PerformanceCard {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
title: I18n.tr("Disk")
|
||||||
|
icon: "storage"
|
||||||
|
value: "R: " + root.formatBytes(DgopService.diskReadRate)
|
||||||
|
subtitle: "W: " + root.formatBytes(DgopService.diskWriteRate)
|
||||||
|
accentColor: Theme.warning
|
||||||
|
history: root.diskReadHistory
|
||||||
|
history2: root.diskWriteHistory
|
||||||
|
maxValue: 0
|
||||||
|
showSecondary: true
|
||||||
|
extraInfo: {
|
||||||
|
const rootMount = DgopService.diskMounts.find(m => m.mountpoint === "/");
|
||||||
|
if (rootMount) {
|
||||||
|
const usedPct = ((rootMount.used || 0) / Math.max(1, rootMount.total || 1) * 100).toFixed(0);
|
||||||
|
return "/ " + usedPct + "% used";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
extraInfoColor: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component PerformanceCard: Rectangle {
|
||||||
|
id: card
|
||||||
|
|
||||||
|
property string title: ""
|
||||||
|
property string icon: ""
|
||||||
|
property string value: ""
|
||||||
|
property string subtitle: ""
|
||||||
|
property color accentColor: Theme.primary
|
||||||
|
property var history: []
|
||||||
|
property var history2: null
|
||||||
|
property real maxValue: 100
|
||||||
|
property bool showSecondary: false
|
||||||
|
property string extraInfo: ""
|
||||||
|
property color extraInfoColor: Theme.surfaceVariantText
|
||||||
|
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
border.color: Theme.outlineLight
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: graphCanvas
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 4
|
||||||
|
renderStrategy: Canvas.Cooperative
|
||||||
|
|
||||||
|
property var hist: card.history
|
||||||
|
property var hist2: card.history2
|
||||||
|
|
||||||
|
onHistChanged: requestPaint()
|
||||||
|
onHist2Changed: requestPaint()
|
||||||
|
onWidthChanged: requestPaint()
|
||||||
|
onHeightChanged: requestPaint()
|
||||||
|
|
||||||
|
onPaint: {
|
||||||
|
const ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
|
||||||
|
if (!hist || hist.length < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
|
let max = card.maxValue;
|
||||||
|
if (max <= 0) {
|
||||||
|
max = 1;
|
||||||
|
for (let k = 0; k < hist.length; k++)
|
||||||
|
max = Math.max(max, hist[k]);
|
||||||
|
if (hist2) {
|
||||||
|
for (let l = 0; l < hist2.length; l++)
|
||||||
|
max = Math.max(max, hist2[l]);
|
||||||
|
}
|
||||||
|
max *= 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const c = card.accentColor;
|
||||||
|
const grad = ctx.createLinearGradient(0, 0, 0, height);
|
||||||
|
grad.addColorStop(0, Qt.rgba(c.r, c.g, c.b, 0.25));
|
||||||
|
grad.addColorStop(1, Qt.rgba(c.r, c.g, c.b, 0.02));
|
||||||
|
|
||||||
|
ctx.fillStyle = grad;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(0, height);
|
||||||
|
for (let i = 0; i < hist.length; i++) {
|
||||||
|
const x = (width / (root.historySize - 1)) * i;
|
||||||
|
const y = height - (hist[i] / max) * height * 0.8;
|
||||||
|
ctx.lineTo(x, y);
|
||||||
|
}
|
||||||
|
ctx.lineTo((width / (root.historySize - 1)) * (hist.length - 1), height);
|
||||||
|
ctx.closePath();
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.8);
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let j = 0; j < hist.length; j++) {
|
||||||
|
const px = (width / (root.historySize - 1)) * j;
|
||||||
|
const py = height - (hist[j] / max) * height * 0.8;
|
||||||
|
j === 0 ? ctx.moveTo(px, py) : ctx.lineTo(px, py);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
if (hist2 && hist2.length >= 2 && card.showSecondary) {
|
||||||
|
ctx.strokeStyle = Qt.rgba(c.r, c.g, c.b, 0.4);
|
||||||
|
ctx.lineWidth = 1.5;
|
||||||
|
ctx.setLineDash([4, 4]);
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let m = 0; m < hist2.length; m++) {
|
||||||
|
const sx = (width / (root.historySize - 1)) * m;
|
||||||
|
const sy = height - (hist2[m] / max) * height * 0.8;
|
||||||
|
m === 0 ? ctx.moveTo(sx, sy) : ctx.lineTo(sx, sy);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: card.icon
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: card.accentColor
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: card.title
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: card.extraInfo
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: card.extraInfoColor
|
||||||
|
visible: card.extraInfo.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillHeight: true
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: card.value
|
||||||
|
font.pixelSize: Theme.fontSizeXLarge
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: card.subtitle
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,39 +19,30 @@ Popup {
|
|||||||
const menuWidth = processContextMenu.width;
|
const menuWidth = processContextMenu.width;
|
||||||
const menuHeight = processContextMenu.height;
|
const menuHeight = processContextMenu.height;
|
||||||
|
|
||||||
if (finalX + menuWidth > parentWidth) {
|
if (finalX + menuWidth > parentWidth)
|
||||||
finalX = Math.max(0, parentWidth - menuWidth);
|
finalX = Math.max(0, parentWidth - menuWidth);
|
||||||
}
|
if (finalY + menuHeight > parentHeight)
|
||||||
|
|
||||||
if (finalY + menuHeight > parentHeight) {
|
|
||||||
finalY = Math.max(0, parentHeight - menuHeight);
|
finalY = Math.max(0, parentHeight - menuHeight);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
processContextMenu.x = finalX;
|
processContextMenu.x = finalX;
|
||||||
processContextMenu.y = finalY;
|
processContextMenu.y = finalY;
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
width: 180
|
width: 200
|
||||||
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
height: menuColumn.implicitHeight + Theme.spacingS * 2
|
||||||
padding: 0
|
padding: 0
|
||||||
modal: false
|
modal: false
|
||||||
closePolicy: Popup.CloseOnEscape
|
closePolicy: Popup.CloseOnEscape
|
||||||
onClosed: {
|
|
||||||
closePolicy = Popup.CloseOnEscape;
|
onClosed: closePolicy = Popup.CloseOnEscape
|
||||||
}
|
onOpened: outsideClickTimer.start()
|
||||||
onOpened: {
|
|
||||||
outsideClickTimer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: outsideClickTimer
|
id: outsideClickTimer
|
||||||
|
|
||||||
interval: 100
|
interval: 100
|
||||||
onTriggered: {
|
onTriggered: processContextMenu.closePolicy = Popup.CloseOnEscape | Popup.CloseOnPressOutside
|
||||||
processContextMenu.closePolicy = Popup.CloseOnEscape | Popup.CloseOnPressOutside;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
background: Rectangle {
|
background: Rectangle {
|
||||||
@@ -59,8 +50,6 @@ Popup {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contentItem: Rectangle {
|
contentItem: Rectangle {
|
||||||
id: menuContent
|
|
||||||
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency)
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
||||||
@@ -68,157 +57,140 @@ Popup {
|
|||||||
|
|
||||||
Column {
|
Column {
|
||||||
id: menuColumn
|
id: menuColumn
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
spacing: 1
|
spacing: 1
|
||||||
|
|
||||||
Rectangle {
|
MenuItem {
|
||||||
width: parent.width
|
|
||||||
height: 28
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: copyPidArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Theme.spacingS
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: I18n.tr("Copy PID")
|
text: I18n.tr("Copy PID")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
iconName: "tag"
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: copyPidArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (processContextMenu.processData) {
|
if (processContextMenu.processData)
|
||||||
Quickshell.execDetached(["dms", "cl", "copy", processContextMenu.processData.pid.toString()]);
|
Quickshell.execDetached(["dms", "cl", "copy", processContextMenu.processData.pid.toString()]);
|
||||||
}
|
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
MenuItem {
|
||||||
width: parent.width
|
text: I18n.tr("Copy Name")
|
||||||
height: 28
|
iconName: "content_copy"
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: copyNameArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Theme.spacingS
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: I18n.tr("Copy Process Name")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
color: Theme.surfaceText
|
|
||||||
font.weight: Font.Normal
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: copyNameArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (processContextMenu.processData) {
|
if (processContextMenu.processData) {
|
||||||
const processName = processContextMenu.processData.displayName || processContextMenu.processData.command;
|
const name = processContextMenu.processData.command || "";
|
||||||
Quickshell.execDetached(["dms", "cl", "copy", processName]);
|
Quickshell.execDetached(["dms", "cl", "copy", name]);
|
||||||
}
|
}
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: I18n.tr("Copy Full Command")
|
||||||
|
iconName: "code"
|
||||||
|
onClicked: {
|
||||||
|
if (processContextMenu.processData) {
|
||||||
|
const fullCmd = processContextMenu.processData.fullCommand || processContextMenu.processData.command || "";
|
||||||
|
Quickshell.execDetached(["dms", "cl", "copy", fullCmd]);
|
||||||
|
}
|
||||||
|
processContextMenu.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: parent.width - Theme.spacingS * 2
|
width: parent.width - Theme.spacingS * 2
|
||||||
height: 5
|
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
|
||||||
color: "transparent"
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
anchors.centerIn: parent
|
|
||||||
width: parent.width
|
|
||||||
height: 1
|
height: 1
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
}
|
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.15)
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
MenuItem {
|
||||||
width: parent.width
|
|
||||||
height: 28
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: killArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
|
||||||
enabled: processContextMenu.processData
|
|
||||||
opacity: enabled ? 1 : 0.5
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Theme.spacingS
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
text: I18n.tr("Kill Process")
|
text: I18n.tr("Kill Process")
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
iconName: "close"
|
||||||
color: parent.enabled ? (killArea.containsMouse ? Theme.error : Theme.surfaceText) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
dangerous: true
|
||||||
font.weight: Font.Normal
|
enabled: processContextMenu.processData
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: killArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: parent.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
enabled: parent.enabled
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (processContextMenu.processData) {
|
if (processContextMenu.processData)
|
||||||
Quickshell.execDetached(["kill", processContextMenu.processData.pid.toString()]);
|
Quickshell.execDetached(["kill", processContextMenu.processData.pid.toString()]);
|
||||||
|
processContextMenu.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MenuItem {
|
||||||
|
text: I18n.tr("Force Kill (SIGKILL)")
|
||||||
|
iconName: "dangerous"
|
||||||
|
dangerous: true
|
||||||
|
enabled: processContextMenu.processData && processContextMenu.processData.pid > 1000
|
||||||
|
onClicked: {
|
||||||
|
if (processContextMenu.processData)
|
||||||
|
Quickshell.execDetached(["kill", "-9", processContextMenu.processData.pid.toString()]);
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component MenuItem: Rectangle {
|
||||||
|
id: menuItem
|
||||||
|
|
||||||
|
property string text: ""
|
||||||
|
property string iconName: ""
|
||||||
|
property bool dangerous: false
|
||||||
|
property bool enabled: true
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
width: parent.width
|
||||||
height: 28
|
height: 32
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: forceKillArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"
|
color: {
|
||||||
enabled: processContextMenu.processData && processContextMenu.processData.pid > 1000
|
if (!enabled)
|
||||||
|
return "transparent";
|
||||||
|
if (dangerous)
|
||||||
|
return menuItemArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent";
|
||||||
|
return menuItemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent";
|
||||||
|
}
|
||||||
opacity: enabled ? 1 : 0.5
|
opacity: enabled ? 1 : 0.5
|
||||||
|
|
||||||
StyledText {
|
Row {
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.leftMargin: Theme.spacingS
|
anchors.leftMargin: Theme.spacingS
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
text: I18n.tr("Force Kill Process")
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: menuItem.iconName
|
||||||
|
size: 16
|
||||||
|
color: {
|
||||||
|
if (!menuItem.enabled)
|
||||||
|
return Theme.surfaceVariantText;
|
||||||
|
if (menuItem.dangerous && menuItemArea.containsMouse)
|
||||||
|
return Theme.error;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: menuItem.text
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
color: parent.enabled ? (forceKillArea.containsMouse ? Theme.error : Theme.surfaceText) : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
|
|
||||||
font.weight: Font.Normal
|
font.weight: Font.Normal
|
||||||
|
color: {
|
||||||
|
if (!menuItem.enabled)
|
||||||
|
return Theme.surfaceVariantText;
|
||||||
|
if (menuItem.dangerous && menuItemArea.containsMouse)
|
||||||
|
return Theme.error;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
id: forceKillArea
|
id: menuItemArea
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: parent.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
cursorShape: menuItem.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||||
enabled: parent.enabled
|
enabled: menuItem.enabled
|
||||||
onClicked: {
|
onClicked: menuItem.clicked()
|
||||||
if (processContextMenu.processData) {
|
|
||||||
Quickshell.execDetached(["kill", "-9", processContextMenu.processData.pid.toString()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
processContextMenu.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,200 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: processItem
|
|
||||||
|
|
||||||
property var process: null
|
|
||||||
property var contextMenu: null
|
|
||||||
|
|
||||||
width: parent ? parent.width : 0
|
|
||||||
height: 40
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08) : Theme.withAlpha(Theme.primary, 0)
|
|
||||||
border.color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : Theme.withAlpha(Theme.primary, 0)
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: processMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
onClicked: (mouse) => {
|
|
||||||
if (mouse.button === Qt.RightButton) {
|
|
||||||
if (process && process.pid > 0 && contextMenu) {
|
|
||||||
contextMenu.processData = process;
|
|
||||||
const globalPos = processMouseArea.mapToGlobal(mouse.x, mouse.y);
|
|
||||||
const localPos = contextMenu.parent ? contextMenu.parent.mapFromGlobal(globalPos.x, globalPos.y) : globalPos;
|
|
||||||
contextMenu.show(localPos.x, localPos.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onPressAndHold: {
|
|
||||||
if (process && process.pid > 0 && contextMenu) {
|
|
||||||
contextMenu.processData = process;
|
|
||||||
const globalPos = processMouseArea.mapToGlobal(processMouseArea.width / 2, processMouseArea.height / 2);
|
|
||||||
contextMenu.show(globalPos.x, globalPos.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.margins: 8
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
id: processIcon
|
|
||||||
|
|
||||||
name: DgopService.getProcessIcon(process ? process.command : "")
|
|
||||||
size: Theme.iconSize - 4
|
|
||||||
color: {
|
|
||||||
if (process && process.cpu > 80) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
opacity: 0.8
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: process ? process.displayName : ""
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: 250
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.left: processIcon.right
|
|
||||||
anchors.leftMargin: 8
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: cpuBadge
|
|
||||||
|
|
||||||
width: 80
|
|
||||||
height: 20
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (process && process.cpu > 80) {
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
|
||||||
}
|
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08);
|
|
||||||
}
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 194
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.formatCpuUsage(process ? process.cpu : 0)
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: {
|
|
||||||
if (process && process.cpu > 80) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: memoryBadge
|
|
||||||
|
|
||||||
width: 80
|
|
||||||
height: 20
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (process && process.memoryKB > 1024 * 1024) {
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
|
||||||
}
|
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08);
|
|
||||||
}
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 102
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.formatMemoryUsage(process ? process.memoryKB : 0)
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: {
|
|
||||||
if (process && process.memoryKB > 1024 * 1024) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: process ? process.pid.toString() : ""
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: 0.7
|
|
||||||
width: 50
|
|
||||||
horizontalAlignment: Text.AlignRight
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 40
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
id: menuButton
|
|
||||||
|
|
||||||
width: 28
|
|
||||||
height: 28
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: menuButtonArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0)
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "more_vert"
|
|
||||||
size: Theme.iconSize - 2
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: 0.6
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: menuButtonArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
if (process && process.pid > 0 && contextMenu) {
|
|
||||||
contextMenu.processData = process;
|
|
||||||
const globalPos = menuButtonArea.mapToGlobal(menuButtonArea.width / 2, menuButtonArea.height);
|
|
||||||
const localPos = contextMenu.parent ? contextMenu.parent.mapFromGlobal(globalPos.x, globalPos.y) : globalPos;
|
|
||||||
contextMenu.show(localPos.x, localPos.y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -12,32 +12,39 @@ DankPopout {
|
|||||||
|
|
||||||
property var parentWidget: null
|
property var parentWidget: null
|
||||||
property var triggerScreen: null
|
property var triggerScreen: null
|
||||||
|
property string searchText: ""
|
||||||
|
property string expandedPid: ""
|
||||||
|
|
||||||
function hide() {
|
function hide() {
|
||||||
close();
|
close();
|
||||||
if (processContextMenu.visible) {
|
if (processContextMenu.visible)
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function show() {
|
function show() {
|
||||||
open();
|
open();
|
||||||
}
|
}
|
||||||
|
|
||||||
popupWidth: 600
|
popupWidth: 650
|
||||||
popupHeight: 600
|
popupHeight: 550
|
||||||
triggerWidth: 55
|
triggerWidth: 55
|
||||||
positioning: ""
|
positioning: ""
|
||||||
screen: triggerScreen
|
screen: triggerScreen
|
||||||
shouldBeVisible: false
|
shouldBeVisible: false
|
||||||
|
|
||||||
onBackgroundClicked: {
|
onBackgroundClicked: {
|
||||||
if (processContextMenu.visible) {
|
if (processContextMenu.visible)
|
||||||
processContextMenu.close();
|
processContextMenu.close();
|
||||||
}
|
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onShouldBeVisibleChanged: {
|
||||||
|
if (!shouldBeVisible) {
|
||||||
|
searchText = "";
|
||||||
|
expandedPid = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ref {
|
Ref {
|
||||||
service: DgopService
|
service: DgopService
|
||||||
}
|
}
|
||||||
@@ -55,55 +62,247 @@ DankPopout {
|
|||||||
|
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: "transparent"
|
color: "transparent"
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
|
||||||
border.width: 0
|
|
||||||
clip: true
|
clip: true
|
||||||
antialiasing: true
|
|
||||||
smooth: true
|
|
||||||
focus: true
|
focus: true
|
||||||
|
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
if (processListPopout.shouldBeVisible) {
|
if (processListPopout.shouldBeVisible)
|
||||||
forceActiveFocus();
|
forceActiveFocus();
|
||||||
}
|
|
||||||
processContextMenu.parent = processListContent;
|
processContextMenu.parent = processListContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
Keys.onPressed: event => {
|
Keys.onPressed: event => {
|
||||||
if (event.key === Qt.Key_Escape) {
|
switch (event.key) {
|
||||||
|
case Qt.Key_Escape:
|
||||||
|
if (processListPopout.searchText.length > 0) {
|
||||||
|
processListPopout.searchText = "";
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
processListPopout.close();
|
processListPopout.close();
|
||||||
event.accepted = true;
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
case Qt.Key_F:
|
||||||
|
if (event.modifiers & Qt.ControlModifier) {
|
||||||
|
searchField.forceActiveFocus();
|
||||||
|
event.accepted = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
|
target: processListPopout
|
||||||
function onShouldBeVisibleChanged() {
|
function onShouldBeVisibleChanged() {
|
||||||
if (processListPopout.shouldBeVisible) {
|
if (processListPopout.shouldBeVisible) {
|
||||||
Qt.callLater(() => {
|
Qt.callLater(() => processListContent.forceActiveFocus());
|
||||||
processListContent.forceActiveFocus();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
target: processListPopout
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingL
|
anchors.margins: Theme.spacingS
|
||||||
spacing: Theme.spacingL
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "analytics"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("Processes")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
}
|
||||||
|
|
||||||
|
DankTextField {
|
||||||
|
id: searchField
|
||||||
|
Layout.preferredWidth: Theme.fontSizeMedium * 14
|
||||||
|
Layout.preferredHeight: Theme.fontSizeMedium * 2.5
|
||||||
|
placeholderText: I18n.tr("Search...")
|
||||||
|
leftIconName: "search"
|
||||||
|
showClearButton: true
|
||||||
|
text: processListPopout.searchText
|
||||||
|
onTextChanged: processListPopout.searchText = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: statsContainer
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: Math.max(leftInfo.height, gaugesRow.height) + Theme.spacingS
|
||||||
|
|
||||||
|
function compactMem(kb) {
|
||||||
|
if (kb < 1024 * 1024) {
|
||||||
|
const mb = kb / 1024;
|
||||||
|
return mb >= 100 ? mb.toFixed(0) + " MB" : mb.toFixed(1) + " MB";
|
||||||
|
}
|
||||||
|
const gb = kb / (1024 * 1024);
|
||||||
|
return gb >= 10 ? gb.toFixed(0) + " GB" : gb.toFixed(1) + " GB";
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property real gaugeSize: Theme.fontSizeMedium * 6
|
||||||
|
|
||||||
|
readonly property var enabledGpusWithTemp: {
|
||||||
|
if (!SessionData.enabledGpuPciIds || SessionData.enabledGpuPciIds.length === 0)
|
||||||
|
return [];
|
||||||
|
const result = [];
|
||||||
|
for (const gpu of DgopService.availableGpus) {
|
||||||
|
if (SessionData.enabledGpuPciIds.indexOf(gpu.pciId) !== -1 && gpu.temperature > 0)
|
||||||
|
result.push(gpu);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
readonly property bool hasGpu: enabledGpusWithTemp.length > 0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: leftInfo
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
Layout.fillWidth: true
|
width: Theme.fontSizeMedium * 3
|
||||||
height: systemOverview.height + Theme.spacingM * 2
|
height: width
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08)
|
|
||||||
border.width: 0
|
|
||||||
|
|
||||||
SystemOverview {
|
|
||||||
id: systemOverview
|
|
||||||
|
|
||||||
|
SystemLogo {
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
width: parent.width - Theme.spacingM * 2
|
width: parent.width * 0.7
|
||||||
|
height: width
|
||||||
|
colorOverride: Theme.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingXS / 2
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.hostname || "localhost"
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.distribution || "Linux"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "schedule"
|
||||||
|
size: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.shortUptime || "--"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 1
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 1
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: DgopService.processCount + " " + I18n.tr("procs", "short for processes")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 1
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: gaugesRow
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
CircleGauge {
|
||||||
|
width: statsContainer.gaugeSize
|
||||||
|
height: statsContainer.gaugeSize
|
||||||
|
value: DgopService.cpuUsage / 100
|
||||||
|
label: DgopService.cpuUsage.toFixed(0) + "%"
|
||||||
|
sublabel: "CPU"
|
||||||
|
detail: DgopService.cpuTemperature > 0 ? (DgopService.cpuTemperature.toFixed(0) + "°") : ""
|
||||||
|
accentColor: DgopService.cpuUsage > 80 ? Theme.error : (DgopService.cpuUsage > 50 ? Theme.warning : Theme.primary)
|
||||||
|
detailColor: DgopService.cpuTemperature > 85 ? Theme.error : (DgopService.cpuTemperature > 70 ? Theme.warning : Theme.surfaceVariantText)
|
||||||
|
}
|
||||||
|
|
||||||
|
CircleGauge {
|
||||||
|
width: statsContainer.gaugeSize
|
||||||
|
height: statsContainer.gaugeSize
|
||||||
|
value: DgopService.memoryUsage / 100
|
||||||
|
label: statsContainer.compactMem(DgopService.usedMemoryKB)
|
||||||
|
sublabel: I18n.tr("Memory")
|
||||||
|
detail: DgopService.totalSwapKB > 0 ? ("+" + statsContainer.compactMem(DgopService.usedSwapKB)) : ""
|
||||||
|
accentColor: DgopService.memoryUsage > 90 ? Theme.error : (DgopService.memoryUsage > 70 ? Theme.warning : Theme.secondary)
|
||||||
|
}
|
||||||
|
|
||||||
|
CircleGauge {
|
||||||
|
width: statsContainer.gaugeSize
|
||||||
|
height: statsContainer.gaugeSize
|
||||||
|
visible: statsContainer.hasGpu
|
||||||
|
|
||||||
|
readonly property var gpu: statsContainer.enabledGpusWithTemp[0] ?? null
|
||||||
|
readonly property color vendorColor: {
|
||||||
|
const vendor = (gpu?.vendor ?? "").toLowerCase();
|
||||||
|
if (vendor.includes("nvidia"))
|
||||||
|
return Theme.success;
|
||||||
|
if (vendor.includes("amd"))
|
||||||
|
return Theme.error;
|
||||||
|
if (vendor.includes("intel"))
|
||||||
|
return Theme.info;
|
||||||
|
return Theme.info;
|
||||||
|
}
|
||||||
|
|
||||||
|
value: Math.min(1, (gpu?.temperature ?? 0) / 100)
|
||||||
|
label: (gpu?.temperature ?? 0) > 0 ? ((gpu?.temperature ?? 0).toFixed(0) + "°C") : "--"
|
||||||
|
sublabel: "GPU"
|
||||||
|
accentColor: {
|
||||||
|
const temp = gpu?.temperature ?? 0;
|
||||||
|
if (temp > 85)
|
||||||
|
return Theme.error;
|
||||||
|
if (temp > 70)
|
||||||
|
return Theme.warning;
|
||||||
|
return vendorColor;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,16 +311,168 @@ DankPopout {
|
|||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
radius: Theme.cornerRadius
|
radius: Theme.cornerRadius
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.05)
|
clip: true
|
||||||
border.width: 0
|
|
||||||
|
|
||||||
ProcessListView {
|
ProcessesView {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
anchors.margins: Theme.spacingS
|
anchors.margins: Theme.spacingS
|
||||||
|
searchText: processListPopout.searchText
|
||||||
|
expandedPid: processListPopout.expandedPid
|
||||||
contextMenu: processContextMenu
|
contextMenu: processContextMenu
|
||||||
|
onExpandedPidChanged: processListPopout.expandedPid = expandedPid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component CircleGauge: Item {
|
||||||
|
id: gaugeRoot
|
||||||
|
|
||||||
|
property real value: 0
|
||||||
|
property string label: ""
|
||||||
|
property string sublabel: ""
|
||||||
|
property string detail: ""
|
||||||
|
property color accentColor: Theme.primary
|
||||||
|
property color detailColor: Theme.surfaceVariantText
|
||||||
|
|
||||||
|
readonly property real thickness: Math.max(3, Math.min(width, height) / 17)
|
||||||
|
readonly property real glowExtra: thickness * 1.6
|
||||||
|
readonly property real arcPadding: thickness / 1.5
|
||||||
|
|
||||||
|
property real animValue: 0
|
||||||
|
|
||||||
|
onValueChanged: animValue = Math.min(1, Math.max(0, value))
|
||||||
|
|
||||||
|
Behavior on animValue {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.mediumDuration
|
||||||
|
easing.type: Easing.OutCubic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: animValue = Math.min(1, Math.max(0, value))
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: glowCanvas
|
||||||
|
anchors.fill: parent
|
||||||
|
onPaint: {
|
||||||
|
const ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
const cx = width / 2;
|
||||||
|
const cy = height / 2;
|
||||||
|
const radius = (Math.min(width, height) / 2) - gaugeRoot.arcPadding;
|
||||||
|
const startAngle = -Math.PI * 0.5;
|
||||||
|
const endAngle = Math.PI * 1.5;
|
||||||
|
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
|
||||||
|
if (gaugeRoot.animValue > 0) {
|
||||||
|
const prog = startAngle + (endAngle - startAngle) * gaugeRoot.animValue;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, radius, startAngle, prog);
|
||||||
|
ctx.strokeStyle = Qt.rgba(gaugeRoot.accentColor.r, gaugeRoot.accentColor.g, gaugeRoot.accentColor.b, 0.2);
|
||||||
|
ctx.lineWidth = gaugeRoot.thickness + gaugeRoot.glowExtra;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: gaugeRoot
|
||||||
|
function onAnimValueChanged() {
|
||||||
|
glowCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
function onAccentColorChanged() {
|
||||||
|
glowCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
function onWidthChanged() {
|
||||||
|
glowCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
function onHeightChanged() {
|
||||||
|
glowCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: requestPaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
Canvas {
|
||||||
|
id: arcCanvas
|
||||||
|
anchors.fill: parent
|
||||||
|
onPaint: {
|
||||||
|
const ctx = getContext("2d");
|
||||||
|
ctx.reset();
|
||||||
|
const cx = width / 2;
|
||||||
|
const cy = height / 2;
|
||||||
|
const radius = (Math.min(width, height) / 2) - gaugeRoot.arcPadding;
|
||||||
|
const startAngle = -Math.PI * 0.5;
|
||||||
|
const endAngle = Math.PI * 1.5;
|
||||||
|
|
||||||
|
ctx.lineCap = "round";
|
||||||
|
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, radius, startAngle, endAngle);
|
||||||
|
ctx.strokeStyle = Qt.rgba(gaugeRoot.accentColor.r, gaugeRoot.accentColor.g, gaugeRoot.accentColor.b, 0.1);
|
||||||
|
ctx.lineWidth = gaugeRoot.thickness;
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
if (gaugeRoot.animValue > 0) {
|
||||||
|
const prog = startAngle + (endAngle - startAngle) * gaugeRoot.animValue;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(cx, cy, radius, startAngle, prog);
|
||||||
|
ctx.strokeStyle = gaugeRoot.accentColor;
|
||||||
|
ctx.lineWidth = gaugeRoot.thickness;
|
||||||
|
ctx.stroke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: gaugeRoot
|
||||||
|
function onAnimValueChanged() {
|
||||||
|
arcCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
function onAccentColorChanged() {
|
||||||
|
arcCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
function onWidthChanged() {
|
||||||
|
arcCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
function onHeightChanged() {
|
||||||
|
arcCanvas.requestPaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: requestPaint()
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: gaugeRoot.label
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: gaugeRoot.sublabel
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
color: gaugeRoot.accentColor
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: gaugeRoot.detail
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 3
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: gaugeRoot.detailColor
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: gaugeRoot.detail.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,264 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import Quickshell
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: root
|
|
||||||
|
|
||||||
property var contextMenu: null
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
DgopService.addRef(["processes"]);
|
|
||||||
}
|
|
||||||
Component.onDestruction: {
|
|
||||||
DgopService.removeRef(["processes"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
id: columnHeaders
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
anchors.leftMargin: 8
|
|
||||||
height: 24
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 60
|
|
||||||
height: 20
|
|
||||||
color: {
|
|
||||||
if (DgopService.currentSort === "name") {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
|
||||||
}
|
|
||||||
return processHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
|
|
||||||
}
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: 0
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Process")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: DgopService.currentSort === "name" ? Font.Bold : Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: DgopService.currentSort === "name" ? 1 : 0.7
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: processHeaderArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
DgopService.setSortBy("name");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 80
|
|
||||||
height: 20
|
|
||||||
color: {
|
|
||||||
if (DgopService.currentSort === "cpu") {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
|
||||||
}
|
|
||||||
return cpuHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
|
|
||||||
}
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 200
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "CPU"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: DgopService.currentSort === "cpu" ? Font.Bold : Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: DgopService.currentSort === "cpu" ? 1 : 0.7
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: cpuHeaderArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
DgopService.setSortBy("cpu");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 80
|
|
||||||
height: 20
|
|
||||||
color: {
|
|
||||||
if (DgopService.currentSort === "memory") {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
|
||||||
}
|
|
||||||
return memoryHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
|
|
||||||
}
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 112
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "RAM"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: DgopService.currentSort === "memory" ? Font.Bold : Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: DgopService.currentSort === "memory" ? 1 : 0.7
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: memoryHeaderArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
DgopService.setSortBy("memory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 50
|
|
||||||
height: 20
|
|
||||||
color: {
|
|
||||||
if (DgopService.currentSort === "pid") {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
|
||||||
}
|
|
||||||
return pidHeaderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0);
|
|
||||||
}
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 53
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "PID"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: DgopService.currentSort === "pid" ? Font.Bold : Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: DgopService.currentSort === "pid" ? 1 : 0.7
|
|
||||||
horizontalAlignment: Text.AlignHCenter
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: pidHeaderArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
DgopService.setSortBy("pid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 28
|
|
||||||
height: 28
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: sortOrderArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.08) : Theme.withAlpha(Theme.surfaceText, 0)
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: 8
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.sortDescending ? "↓" : "↑"
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.centerIn: parent
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
// TODO: Re-implement sort order toggle
|
|
||||||
|
|
||||||
id: sortOrderArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DankListView {
|
|
||||||
id: processListView
|
|
||||||
|
|
||||||
property string keyRoleName: "pid"
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
height: parent.height - columnHeaders.height
|
|
||||||
clip: true
|
|
||||||
spacing: 4
|
|
||||||
model: ScriptModel {
|
|
||||||
values: DgopService.processes
|
|
||||||
objectProp: "pid"
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: ProcessListItem {
|
|
||||||
process: modelData
|
|
||||||
contextMenu: root.contextMenu
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Layouts
|
|
||||||
import qs.Common
|
|
||||||
import qs.Modules.ProcessList
|
|
||||||
import qs.Services
|
|
||||||
|
|
||||||
ColumnLayout {
|
|
||||||
id: processesTab
|
|
||||||
|
|
||||||
property var contextMenu: null
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
SystemOverview {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessListView {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.fillHeight: true
|
|
||||||
contextMenu: processesTab.contextMenu
|
|
||||||
}
|
|
||||||
|
|
||||||
ProcessContextMenu {
|
|
||||||
id: localContextMenu
|
|
||||||
}
|
|
||||||
}
|
|
||||||
607
quickshell/Modules/ProcessList/ProcessesView.qml
Normal file
607
quickshell/Modules/ProcessList/ProcessesView.qml
Normal file
@@ -0,0 +1,607 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import Quickshell
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string searchText: ""
|
||||||
|
property string expandedPid: ""
|
||||||
|
property var contextMenu: null
|
||||||
|
property bool hoveringExpandedItem: false
|
||||||
|
|
||||||
|
readonly property bool pauseUpdates: hoveringExpandedItem || (contextMenu?.visible ?? false)
|
||||||
|
property var cachedProcesses: []
|
||||||
|
|
||||||
|
onFilteredProcessesChanged: {
|
||||||
|
if (!pauseUpdates)
|
||||||
|
cachedProcesses = filteredProcesses;
|
||||||
|
}
|
||||||
|
|
||||||
|
onPauseUpdatesChanged: {
|
||||||
|
if (!pauseUpdates)
|
||||||
|
cachedProcesses = filteredProcesses;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property var filteredProcesses: {
|
||||||
|
if (!DgopService.allProcesses || DgopService.allProcesses.length === 0)
|
||||||
|
return [];
|
||||||
|
|
||||||
|
let procs = DgopService.allProcesses.slice();
|
||||||
|
|
||||||
|
if (searchText.length > 0) {
|
||||||
|
const search = searchText.toLowerCase();
|
||||||
|
procs = procs.filter(p => {
|
||||||
|
const cmd = (p.command || "").toLowerCase();
|
||||||
|
const fullCmd = (p.fullCommand || "").toLowerCase();
|
||||||
|
const pid = p.pid.toString();
|
||||||
|
return cmd.includes(search) || fullCmd.includes(search) || pid.includes(search);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const asc = DgopService.sortAscending;
|
||||||
|
procs.sort((a, b) => {
|
||||||
|
let valueA, valueB, result;
|
||||||
|
switch (DgopService.currentSort) {
|
||||||
|
case "cpu":
|
||||||
|
valueA = a.cpu || 0;
|
||||||
|
valueB = b.cpu || 0;
|
||||||
|
result = valueB - valueA;
|
||||||
|
break;
|
||||||
|
case "memory":
|
||||||
|
valueA = a.memoryKB || 0;
|
||||||
|
valueB = b.memoryKB || 0;
|
||||||
|
result = valueB - valueA;
|
||||||
|
break;
|
||||||
|
case "name":
|
||||||
|
valueA = (a.command || "").toLowerCase();
|
||||||
|
valueB = (b.command || "").toLowerCase();
|
||||||
|
result = valueA.localeCompare(valueB);
|
||||||
|
break;
|
||||||
|
case "pid":
|
||||||
|
valueA = a.pid || 0;
|
||||||
|
valueB = b.pid || 0;
|
||||||
|
result = valueA - valueB;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return asc ? -result : result;
|
||||||
|
});
|
||||||
|
|
||||||
|
return procs;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
DgopService.addRef(["processes", "cpu", "memory", "system"]);
|
||||||
|
cachedProcesses = filteredProcesses;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
DgopService.removeRef(["processes", "cpu", "memory", "system"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 36
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
SortableHeader {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 200
|
||||||
|
text: I18n.tr("Name")
|
||||||
|
sortKey: "name"
|
||||||
|
currentSort: DgopService.currentSort
|
||||||
|
sortAscending: DgopService.sortAscending
|
||||||
|
onClicked: DgopService.setSortBy("name")
|
||||||
|
alignment: Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
SortableHeader {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
text: "CPU"
|
||||||
|
sortKey: "cpu"
|
||||||
|
currentSort: DgopService.currentSort
|
||||||
|
sortAscending: DgopService.sortAscending
|
||||||
|
onClicked: DgopService.setSortBy("cpu")
|
||||||
|
}
|
||||||
|
|
||||||
|
SortableHeader {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
text: I18n.tr("Memory")
|
||||||
|
sortKey: "memory"
|
||||||
|
currentSort: DgopService.currentSort
|
||||||
|
sortAscending: DgopService.sortAscending
|
||||||
|
onClicked: DgopService.setSortBy("memory")
|
||||||
|
}
|
||||||
|
|
||||||
|
SortableHeader {
|
||||||
|
Layout.preferredWidth: 80
|
||||||
|
text: "PID"
|
||||||
|
sortKey: "pid"
|
||||||
|
currentSort: DgopService.currentSort
|
||||||
|
sortAscending: DgopService.sortAscending
|
||||||
|
onClicked: DgopService.setSortBy("pid")
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 40
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
color: Theme.outlineLight
|
||||||
|
}
|
||||||
|
|
||||||
|
DankListView {
|
||||||
|
id: processListView
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
spacing: 2
|
||||||
|
|
||||||
|
model: ScriptModel {
|
||||||
|
values: root.cachedProcesses
|
||||||
|
objectProp: "pid"
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: ProcessItem {
|
||||||
|
required property var modelData
|
||||||
|
|
||||||
|
width: processListView.width
|
||||||
|
process: modelData
|
||||||
|
isExpanded: root.expandedPid === (modelData?.pid ?? -1).toString()
|
||||||
|
contextMenu: root.contextMenu
|
||||||
|
onToggleExpand: {
|
||||||
|
const pidStr = (modelData?.pid ?? -1).toString();
|
||||||
|
root.expandedPid = (root.expandedPid === pidStr) ? "" : pidStr;
|
||||||
|
}
|
||||||
|
onHoveringExpandedChanged: {
|
||||||
|
if (hoveringExpanded)
|
||||||
|
root.hoveringExpandedItem = true;
|
||||||
|
else
|
||||||
|
Qt.callLater(() => {
|
||||||
|
root.hoveringExpandedItem = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 300
|
||||||
|
height: 100
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
visible: root.cachedProcesses.length === 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: root.searchText.length > 0 ? "search_off" : "hourglass_empty"
|
||||||
|
size: 32
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("No matching processes", "empty state in process list")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
visible: root.searchText.length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component SortableHeader: Item {
|
||||||
|
id: headerItem
|
||||||
|
|
||||||
|
property string text: ""
|
||||||
|
property string sortKey: ""
|
||||||
|
property string currentSort: ""
|
||||||
|
property bool sortAscending: false
|
||||||
|
property int alignment: Text.AlignHCenter
|
||||||
|
|
||||||
|
signal clicked
|
||||||
|
|
||||||
|
readonly property bool isActive: sortKey === currentSort
|
||||||
|
|
||||||
|
height: 36
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: headerItem.isActive ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : (headerMouseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06) : "transparent")
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
spacing: 4
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: headerItem.alignment === Text.AlignLeft
|
||||||
|
visible: headerItem.alignment !== Text.AlignLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: headerItem.text
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: headerItem.isActive ? Font.Bold : Font.Medium
|
||||||
|
color: headerItem.isActive ? Theme.primary : Theme.surfaceText
|
||||||
|
opacity: headerItem.isActive ? 1 : 0.8
|
||||||
|
}
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: headerItem.sortAscending ? "arrow_upward" : "arrow_downward"
|
||||||
|
size: Theme.fontSizeSmall
|
||||||
|
color: Theme.primary
|
||||||
|
visible: headerItem.isActive
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: headerItem.alignment !== Text.AlignLeft
|
||||||
|
visible: headerItem.alignment === Text.AlignLeft
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: headerMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: headerItem.clicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component ProcessItem: Rectangle {
|
||||||
|
id: processItemRoot
|
||||||
|
|
||||||
|
property var process: null
|
||||||
|
property bool isExpanded: false
|
||||||
|
property var contextMenu: null
|
||||||
|
readonly property bool hoveringExpanded: (isExpanded && processMouseArea.containsMouse) || copyMouseArea.containsMouse
|
||||||
|
|
||||||
|
signal toggleExpand
|
||||||
|
|
||||||
|
readonly property int processPid: process?.pid ?? 0
|
||||||
|
readonly property real processCpu: process?.cpu ?? 0
|
||||||
|
readonly property int processMemKB: process?.memoryKB ?? 0
|
||||||
|
readonly property string processCmd: process?.command ?? ""
|
||||||
|
readonly property string processFullCmd: process?.fullCommand ?? processCmd
|
||||||
|
|
||||||
|
height: isExpanded ? (44 + expandedRect.height + Theme.spacingXS) : 44
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06) : "transparent"
|
||||||
|
border.color: processMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"
|
||||||
|
border.width: 1
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: processMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: mouse => {
|
||||||
|
if (mouse.button === Qt.RightButton) {
|
||||||
|
if (processItemRoot.processPid > 0 && processItemRoot.contextMenu) {
|
||||||
|
processItemRoot.contextMenu.processData = processItemRoot.process;
|
||||||
|
const globalPos = processMouseArea.mapToGlobal(mouse.x, mouse.y);
|
||||||
|
const localPos = processItemRoot.contextMenu.parent ? processItemRoot.contextMenu.parent.mapFromGlobal(globalPos.x, globalPos.y) : globalPos;
|
||||||
|
processItemRoot.contextMenu.show(localPos.x, localPos.y);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
processItemRoot.toggleExpand();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: parent.width
|
||||||
|
height: 44
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.leftMargin: Theme.spacingS
|
||||||
|
anchors.rightMargin: Theme.spacingS
|
||||||
|
spacing: 0
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.minimumWidth: 200
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
Row {
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: DgopService.getProcessIcon(processItemRoot.processCmd)
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: {
|
||||||
|
if (processItemRoot.processCpu > 80)
|
||||||
|
return Theme.error;
|
||||||
|
if (processItemRoot.processCpu > 50)
|
||||||
|
return Theme.warning;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
opacity: 0.8
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: processItemRoot.processCmd
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceText
|
||||||
|
elide: Text.ElideRight
|
||||||
|
width: Math.min(implicitWidth, 280)
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 70
|
||||||
|
height: 24
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (processItemRoot.processCpu > 80)
|
||||||
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15);
|
||||||
|
if (processItemRoot.processCpu > 50)
|
||||||
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||||
|
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: DgopService.formatCpuUsage(processItemRoot.processCpu)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: {
|
||||||
|
if (processItemRoot.processCpu > 80)
|
||||||
|
return Theme.error;
|
||||||
|
if (processItemRoot.processCpu > 50)
|
||||||
|
return Theme.warning;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 70
|
||||||
|
height: 24
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
if (processItemRoot.processMemKB > 2 * 1024 * 1024)
|
||||||
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15);
|
||||||
|
if (processItemRoot.processMemKB > 1024 * 1024)
|
||||||
|
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.12);
|
||||||
|
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: DgopService.formatMemoryUsage(processItemRoot.processMemKB)
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: {
|
||||||
|
if (processItemRoot.processMemKB > 2 * 1024 * 1024)
|
||||||
|
return Theme.error;
|
||||||
|
if (processItemRoot.processMemKB > 1024 * 1024)
|
||||||
|
return Theme.warning;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 80
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: processItemRoot.processPid > 0 ? processItemRoot.processPid.toString() : ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Item {
|
||||||
|
Layout.preferredWidth: 40
|
||||||
|
height: parent.height
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: processItemRoot.isExpanded ? "expand_less" : "expand_more"
|
||||||
|
size: Theme.iconSize - 4
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: expandedRect
|
||||||
|
width: parent.width - Theme.spacingM * 2
|
||||||
|
height: processItemRoot.isExpanded ? (expandedContent.implicitHeight + Theme.spacingS * 2) : 0
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
radius: Theme.cornerRadius - 2
|
||||||
|
color: Qt.rgba(Theme.surfaceContainerHigh.r, Theme.surfaceContainerHigh.g, Theme.surfaceContainerHigh.b, 0.6)
|
||||||
|
clip: true
|
||||||
|
visible: processItemRoot.isExpanded
|
||||||
|
|
||||||
|
Behavior on height {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
easing.type: Theme.standardEasing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
id: expandedContent
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.margins: Theme.spacingS
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
width: parent.width
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: cmdLabel
|
||||||
|
text: I18n.tr("Full Command:", "process detail label")
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: cmdText
|
||||||
|
text: processItemRoot.processFullCmd
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceText
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
elide: Text.ElideMiddle
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: copyBtn
|
||||||
|
Layout.preferredWidth: 24
|
||||||
|
Layout.preferredHeight: 24
|
||||||
|
Layout.alignment: Qt.AlignVCenter
|
||||||
|
radius: Theme.cornerRadius - 2
|
||||||
|
color: copyMouseArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15) : "transparent"
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
name: "content_copy"
|
||||||
|
size: 14
|
||||||
|
color: copyMouseArea.containsMouse ? Theme.primary : Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: copyMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
Quickshell.execDetached(["dms", "cl", "copy", processItemRoot.processFullCmd]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingL
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "PPID:"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: (processItemRoot.process?.ppid ?? 0) > 0 ? processItemRoot.process.ppid.toString() : "--"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "Mem:"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: (processItemRoot.process?.memoryPercent ?? 0).toFixed(1) + "%"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,435 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import QtQuick.Controls
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
Component.onCompleted: {
|
|
||||||
DgopService.addRef(["cpu", "memory", "system"]);
|
|
||||||
}
|
|
||||||
Component.onDestruction: {
|
|
||||||
DgopService.removeRef(["cpu", "memory", "system"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingM * 2) / 3
|
|
||||||
height: 80
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (DgopService.sortBy === "cpu") {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16);
|
|
||||||
} else if (cpuCardMouseArea.containsMouse) {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
border.color: DgopService.sortBy === "cpu" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2)
|
|
||||||
border.width: DgopService.sortBy === "cpu" ? 2 : 1
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: cpuCardMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
DgopService.setSortBy("cpu");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Theme.spacingM
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("CPU")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: DgopService.sortBy === "cpu" ? Theme.primary : Theme.secondary
|
|
||||||
opacity: DgopService.sortBy === "cpu" ? 1 : 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.cpuUsage === undefined || DgopService.cpuUsage === null) {
|
|
||||||
return "--%";
|
|
||||||
}
|
|
||||||
return DgopService.cpuUsage.toFixed(1) + "%";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 1
|
|
||||||
height: 20
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.cpuTemperature === undefined || DgopService.cpuTemperature === null || DgopService.cpuTemperature <= 0) {
|
|
||||||
return "--°";
|
|
||||||
}
|
|
||||||
return Math.round(DgopService.cpuTemperature) + "°";
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: {
|
|
||||||
if (DgopService.cpuTemperature > 80) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
if (DgopService.cpuTemperature > 60) {
|
|
||||||
return Theme.warning;
|
|
||||||
}
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `${DgopService.cpuCores} cores`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: 0.7
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingM * 2) / 3
|
|
||||||
height: 80
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (DgopService.sortBy === "memory") {
|
|
||||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.16);
|
|
||||||
} else if (memoryCardMouseArea.containsMouse) {
|
|
||||||
return Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.12);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
border.color: DgopService.sortBy === "memory" ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4) : Qt.rgba(Theme.secondary.r, Theme.secondary.g, Theme.secondary.b, 0.2)
|
|
||||||
border.width: DgopService.sortBy === "memory" ? 2 : 1
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: memoryCardMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
onClicked: {
|
|
||||||
DgopService.setSortBy("memory");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Theme.spacingM
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Memory")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: DgopService.sortBy === "memory" ? Theme.primary : Theme.secondary
|
|
||||||
opacity: DgopService.sortBy === "memory" ? 1 : 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.formatSystemMemory(DgopService.usedMemoryKB)
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: 1
|
|
||||||
height: 20
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.3)
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: DgopService.totalSwapKB > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.totalSwapKB > 0 ? DgopService.formatSystemMemory(DgopService.usedSwapKB) : ""
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
visible: DgopService.totalSwapKB > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (DgopService.totalSwapKB > 0) {
|
|
||||||
return "of " + DgopService.formatSystemMemory(DgopService.totalMemoryKB) + " + swap";
|
|
||||||
}
|
|
||||||
return "of " + DgopService.formatSystemMemory(DgopService.totalMemoryKB);
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: 0.7
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on border.color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingM * 2) / 3
|
|
||||||
height: 80
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
if (gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1) {
|
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.16);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const vendor = gpu.vendor.toLowerCase();
|
|
||||||
if (vendor.includes("nvidia")) {
|
|
||||||
if (gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1) {
|
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.2);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.12);
|
|
||||||
}
|
|
||||||
} else if (vendor.includes("amd")) {
|
|
||||||
if (gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1) {
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.2);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12);
|
|
||||||
}
|
|
||||||
} else if (vendor.includes("intel")) {
|
|
||||||
if (gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1) {
|
|
||||||
return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.2);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.12);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1) {
|
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.16);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
border.color: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.2);
|
|
||||||
}
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const vendor = gpu.vendor.toLowerCase();
|
|
||||||
if (vendor.includes("nvidia")) {
|
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.3);
|
|
||||||
} else if (vendor.includes("amd")) {
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
|
|
||||||
} else if (vendor.includes("intel")) {
|
|
||||||
return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.3);
|
|
||||||
}
|
|
||||||
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.2);
|
|
||||||
}
|
|
||||||
border.width: 1
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: gpuCardMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
|
||||||
cursorShape: DgopService.availableGpus.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
onClicked: (mouse) => {
|
|
||||||
if (mouse.button === Qt.LeftButton) {
|
|
||||||
if (DgopService.availableGpus.length > 1) {
|
|
||||||
const nextIndex = (SessionData.selectedGpuIndex + 1) % DgopService.availableGpus.length;
|
|
||||||
SessionData.setSelectedGpuIndex(nextIndex);
|
|
||||||
}
|
|
||||||
} else if (mouse.button === Qt.RightButton) {
|
|
||||||
gpuContextMenu.popup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.leftMargin: Theme.spacingM
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("GPU")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.secondary
|
|
||||||
opacity: 0.8
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return "No GPU";
|
|
||||||
}
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
// Check if temperature monitoring is enabled for this GPU
|
|
||||||
const tempEnabled = SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.indexOf(gpu.pciId) !== -1;
|
|
||||||
const temp = gpu.temperature;
|
|
||||||
const hasTemp = tempEnabled && temp !== undefined && temp !== null && temp !== 0;
|
|
||||||
if (hasTemp) {
|
|
||||||
return Math.round(temp) + "°";
|
|
||||||
} else {
|
|
||||||
return gpu.vendor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const tempEnabled = SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.indexOf(gpu.pciId) !== -1;
|
|
||||||
const temp = gpu.temperature || 0;
|
|
||||||
if (tempEnabled && temp > 80) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
if (tempEnabled && temp > 60) {
|
|
||||||
return Theme.warning;
|
|
||||||
}
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return "No GPUs detected";
|
|
||||||
}
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const tempEnabled = SessionData.enabledGpuPciIds && SessionData.enabledGpuPciIds.indexOf(gpu.pciId) !== -1;
|
|
||||||
const temp = gpu.temperature;
|
|
||||||
const hasTemp = tempEnabled && temp !== undefined && temp !== null && temp !== 0;
|
|
||||||
if (hasTemp) {
|
|
||||||
return gpu.vendor + " " + gpu.displayName;
|
|
||||||
} else {
|
|
||||||
return gpu.displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
opacity: 0.7
|
|
||||||
width: parent.parent.width - Theme.spacingM * 2
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Menu {
|
|
||||||
id: gpuContextMenu
|
|
||||||
|
|
||||||
MenuItem {
|
|
||||||
text: I18n.tr("Enable GPU Temperature")
|
|
||||||
checkable: true
|
|
||||||
checked: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
if (!gpu.pciId) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return SessionData.enabledGpuPciIds ? SessionData.enabledGpuPciIds.indexOf(gpu.pciId) !== -1 : false;
|
|
||||||
}
|
|
||||||
onTriggered: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
if (!gpu.pciId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const enabledIds = SessionData.enabledGpuPciIds ? SessionData.enabledGpuPciIds.slice() : [];
|
|
||||||
const index = enabledIds.indexOf(gpu.pciId);
|
|
||||||
if (checked && index === -1) {
|
|
||||||
enabledIds.push(gpu.pciId);
|
|
||||||
DgopService.addGpuPciId(gpu.pciId);
|
|
||||||
} else if (!checked && index !== -1) {
|
|
||||||
enabledIds.splice(index, 1);
|
|
||||||
DgopService.removeGpuPciId(gpu.pciId);
|
|
||||||
}
|
|
||||||
SessionData.setEnabledGpuPciIds(enabledIds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,591 +0,0 @@
|
|||||||
import QtQuick
|
|
||||||
import qs.Common
|
|
||||||
import qs.Services
|
|
||||||
import qs.Widgets
|
|
||||||
|
|
||||||
DankFlickable {
|
|
||||||
anchors.fill: parent
|
|
||||||
contentHeight: systemColumn.implicitHeight
|
|
||||||
clip: true
|
|
||||||
Component.onCompleted: {
|
|
||||||
DgopService.addRef(["system", "diskmounts"]);
|
|
||||||
}
|
|
||||||
Component.onDestruction: {
|
|
||||||
DgopService.removeRef(["system", "diskmounts"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: systemColumn
|
|
||||||
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingM
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: systemInfoColumn.implicitHeight + 2 * Theme.spacingL
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.width: 0
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: systemInfoColumn
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.margins: Theme.spacingL
|
|
||||||
spacing: Theme.spacingL
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingL
|
|
||||||
|
|
||||||
SystemLogo {
|
|
||||||
width: 80
|
|
||||||
height: 80
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: parent.width - 80 - Theme.spacingL
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.hostname
|
|
||||||
font.pixelSize: Theme.fontSizeXLarge
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Light
|
|
||||||
color: Theme.surfaceText
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `${DgopService.distribution} • ${DgopService.architecture} • ${DgopService.kernelVersion}`
|
|
||||||
font.pixelSize: Theme.fontSizeMedium
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `${DgopService.uptime} • Boot: ${DgopService.bootTime}`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `Load: ${DgopService.loadAverage} • ${DgopService.processCount} processes, ${DgopService.threadCount} threads`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 1
|
|
||||||
color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingXL
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingXL) / 2
|
|
||||||
height: hardwareColumn.implicitHeight + Theme.spacingL
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.width: 1
|
|
||||||
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1)
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: hardwareColumn
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.margins: Theme.spacingM
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "memory"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("System")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.primary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.cpuModel
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
maximumLineCount: 1
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: DgopService.motherboard
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
wrapMode: Text.NoWrap
|
|
||||||
maximumLineCount: 1
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `BIOS ${DgopService.biosVersion}`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: `${DgopService.formatSystemMemory(DgopService.totalMemoryKB)} RAM`
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: (parent.width - Theme.spacingXL) / 2
|
|
||||||
height: gpuColumn.implicitHeight + Theme.spacingL
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: {
|
|
||||||
const baseColor = Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency);
|
|
||||||
const hoverColor = Qt.rgba(Theme.surfaceContainerHigh.r, Theme.surfaceContainerHigh.g, Theme.surfaceContainerHigh.b, Theme.popupTransparency * 1.5);
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1 ? hoverColor : baseColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const vendor = gpu.fullName.split(' ')[0].toLowerCase();
|
|
||||||
let tintColor;
|
|
||||||
if (vendor.includes("nvidia")) {
|
|
||||||
tintColor = Theme.success;
|
|
||||||
} else if (vendor.includes("amd")) {
|
|
||||||
tintColor = Theme.error;
|
|
||||||
} else if (vendor.includes("intel")) {
|
|
||||||
tintColor = Theme.info;
|
|
||||||
} else {
|
|
||||||
return gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1 ? hoverColor : baseColor;
|
|
||||||
}
|
|
||||||
if (gpuCardMouseArea.containsMouse && DgopService.availableGpus.length > 1) {
|
|
||||||
return Qt.rgba((hoverColor.r + tintColor.r * 0.1) / 1.1, (hoverColor.g + tintColor.g * 0.1) / 1.1, (hoverColor.b + tintColor.b * 0.1) / 1.1, 0.6);
|
|
||||||
} else {
|
|
||||||
return Qt.rgba((baseColor.r + tintColor.r * 0.08) / 1.08, (baseColor.g + tintColor.g * 0.08) / 1.08, (baseColor.b + tintColor.b * 0.08) / 1.08, 0.4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
border.width: 1
|
|
||||||
border.color: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const vendor = gpu.fullName.split(' ')[0].toLowerCase();
|
|
||||||
if (vendor.includes("nvidia")) {
|
|
||||||
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.3);
|
|
||||||
} else if (vendor.includes("amd")) {
|
|
||||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
|
|
||||||
} else if (vendor.includes("intel")) {
|
|
||||||
return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.3);
|
|
||||||
}
|
|
||||||
return Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: gpuCardMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
cursorShape: DgopService.availableGpus.length > 1 ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
||||||
onClicked: {
|
|
||||||
if (DgopService.availableGpus.length > 1) {
|
|
||||||
const nextIndex = (SessionData.selectedGpuIndex + 1) % DgopService.availableGpus.length;
|
|
||||||
SessionData.setSelectedGpuIndex(nextIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: gpuColumn
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.margins: Theme.spacingM
|
|
||||||
spacing: Theme.spacingXS
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "auto_awesome_mosaic"
|
|
||||||
size: Theme.iconSizeSmall
|
|
||||||
color: Theme.secondary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: "GPU"
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.secondary
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return "No GPUs detected";
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
return gpu.fullName;
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Medium
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
maximumLineCount: 1
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return "Device: N/A";
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
return `Device: ${gpu.pciId}`;
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
textFormat: Text.RichText
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return "Driver: N/A";
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
return `Driver: ${gpu.driver}`;
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return "Temp: --°";
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const temp = gpu.temperature;
|
|
||||||
return `Temp: ${(temp === undefined || temp === null || temp === 0) ? '--°' : `${Math.round(temp)}°C`}`;
|
|
||||||
}
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: {
|
|
||||||
if (!DgopService.availableGpus || DgopService.availableGpus.length === 0) {
|
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
const gpu = DgopService.availableGpus[Math.min(SessionData.selectedGpuIndex, DgopService.availableGpus.length - 1)];
|
|
||||||
const temp = gpu.temperature || 0;
|
|
||||||
if (temp > 80) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (temp > 60) {
|
|
||||||
return Theme.warning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7);
|
|
||||||
}
|
|
||||||
width: parent.width
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Behavior on color {
|
|
||||||
ColorAnimation {
|
|
||||||
duration: Theme.shortDuration
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: storageColumn.implicitHeight + 2 * Theme.spacingL
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
|
||||||
border.width: 0
|
|
||||||
|
|
||||||
Column {
|
|
||||||
id: storageColumn
|
|
||||||
|
|
||||||
anchors.left: parent.left
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.top: parent.top
|
|
||||||
anchors.margins: Theme.spacingL
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
DankIcon {
|
|
||||||
name: "storage"
|
|
||||||
size: Theme.iconSize
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Storage & Disks")
|
|
||||||
font.pixelSize: Theme.fontSizeLarge
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Column {
|
|
||||||
width: parent.width
|
|
||||||
spacing: 2
|
|
||||||
|
|
||||||
Row {
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Device")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.25
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Mount")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.2
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Size")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.15
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Used")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.15
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Available")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.15
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: I18n.tr("Use%")
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
font.weight: Font.Bold
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.1
|
|
||||||
elide: Text.ElideRight
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
id: diskMountRepeater
|
|
||||||
|
|
||||||
model: DgopService.diskMounts
|
|
||||||
|
|
||||||
Rectangle {
|
|
||||||
width: parent.width
|
|
||||||
height: 24
|
|
||||||
radius: Theme.cornerRadius
|
|
||||||
color: diskMouseArea.containsMouse ? Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.04) : "transparent"
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
id: diskMouseArea
|
|
||||||
|
|
||||||
anchors.fill: parent
|
|
||||||
hoverEnabled: true
|
|
||||||
}
|
|
||||||
|
|
||||||
Row {
|
|
||||||
anchors.fill: parent
|
|
||||||
spacing: Theme.spacingS
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.device
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.25
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.mount
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.2
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.size
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.15
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.used
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.15
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.avail
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: Theme.surfaceText
|
|
||||||
width: parent.width * 0.15
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
text: modelData.percent
|
|
||||||
font.pixelSize: Theme.fontSizeSmall
|
|
||||||
font.family: SettingsData.monoFontFamily
|
|
||||||
color: {
|
|
||||||
const percent = parseInt(modelData.percent);
|
|
||||||
if (percent > 90) {
|
|
||||||
return Theme.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (percent > 75) {
|
|
||||||
return Theme.warning;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Theme.surfaceText;
|
|
||||||
}
|
|
||||||
width: parent.width * 0.1
|
|
||||||
elide: Text.ElideRight
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
verticalAlignment: Text.AlignVCenter
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
386
quickshell/Modules/ProcessList/SystemView.qml
Normal file
386
quickshell/Modules/ProcessList/SystemView.qml
Normal file
@@ -0,0 +1,386 @@
|
|||||||
|
import QtQuick
|
||||||
|
import QtQuick.Layouts
|
||||||
|
import qs.Common
|
||||||
|
import qs.Services
|
||||||
|
import qs.Widgets
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
DgopService.addRef(["system", "cpu"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Component.onDestruction: {
|
||||||
|
DgopService.removeRef(["system", "cpu"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: systemInfoColumn.implicitHeight + Theme.spacingM * 2
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
id: systemInfoColumn
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "computer"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.primary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("System Information", "system info header in system monitor")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GridLayout {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
columns: 2
|
||||||
|
rowSpacing: Theme.spacingS
|
||||||
|
columnSpacing: Theme.spacingXL
|
||||||
|
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("Hostname", "system info label")
|
||||||
|
value: DgopService.hostname || "--"
|
||||||
|
}
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("Distribution", "system info label")
|
||||||
|
value: DgopService.distribution || "--"
|
||||||
|
}
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("Kernel", "system info label")
|
||||||
|
value: DgopService.kernelVersion || "--"
|
||||||
|
}
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("Architecture", "system info label")
|
||||||
|
value: DgopService.architecture || "--"
|
||||||
|
}
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("CPU")
|
||||||
|
value: DgopService.cpuModel || ("" + DgopService.cpuCores + " cores")
|
||||||
|
}
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("Uptime")
|
||||||
|
value: DgopService.uptime || "--"
|
||||||
|
}
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("Load Average", "system info label")
|
||||||
|
value: DgopService.loadAverage || "--"
|
||||||
|
}
|
||||||
|
InfoRow {
|
||||||
|
label: I18n.tr("Processes")
|
||||||
|
value: DgopService.processCount > 0 ? DgopService.processCount.toString() : "--"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "developer_board"
|
||||||
|
size: Theme.iconSize
|
||||||
|
color: Theme.secondary
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("GPU Monitoring", "gpu section header in system monitor")
|
||||||
|
font.pixelSize: Theme.fontSizeLarge
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.preferredHeight: 1
|
||||||
|
color: Theme.outlineLight
|
||||||
|
}
|
||||||
|
|
||||||
|
DankListView {
|
||||||
|
id: gpuListView
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.fillHeight: true
|
||||||
|
clip: true
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
model: DgopService.availableGpus
|
||||||
|
|
||||||
|
delegate: Rectangle {
|
||||||
|
required property var modelData
|
||||||
|
required property int index
|
||||||
|
|
||||||
|
width: gpuListView.width
|
||||||
|
height: 80
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: {
|
||||||
|
const vendor = (modelData?.vendor ?? "").toLowerCase();
|
||||||
|
if (vendor.includes("nvidia"))
|
||||||
|
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.08);
|
||||||
|
if (vendor.includes("amd"))
|
||||||
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.08);
|
||||||
|
if (vendor.includes("intel"))
|
||||||
|
return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.08);
|
||||||
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.08);
|
||||||
|
}
|
||||||
|
border.color: {
|
||||||
|
const vendor = (modelData?.vendor ?? "").toLowerCase();
|
||||||
|
if (vendor.includes("nvidia"))
|
||||||
|
return Qt.rgba(Theme.success.r, Theme.success.g, Theme.success.b, 0.2);
|
||||||
|
if (vendor.includes("amd"))
|
||||||
|
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.2);
|
||||||
|
if (vendor.includes("intel"))
|
||||||
|
return Qt.rgba(Theme.info.r, Theme.info.g, Theme.info.b, 0.2);
|
||||||
|
return Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.2);
|
||||||
|
}
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
readonly property bool tempEnabled: {
|
||||||
|
const pciId = modelData?.pciId ?? "";
|
||||||
|
if (!pciId)
|
||||||
|
return false;
|
||||||
|
return SessionData.enabledGpuPciIds ? SessionData.enabledGpuPciIds.indexOf(pciId) !== -1 : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: Theme.spacingM
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "developer_board"
|
||||||
|
size: Theme.iconSize + 4
|
||||||
|
color: {
|
||||||
|
const vendor = (modelData?.vendor ?? "").toLowerCase();
|
||||||
|
if (vendor.includes("nvidia"))
|
||||||
|
return Theme.success;
|
||||||
|
if (vendor.includes("amd"))
|
||||||
|
return Theme.error;
|
||||||
|
if (vendor.includes("intel"))
|
||||||
|
return Theme.info;
|
||||||
|
return Theme.surfaceVariantText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.displayName ?? I18n.tr("Unknown GPU", "fallback gpu name")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
font.weight: Font.Bold
|
||||||
|
color: Theme.surfaceText
|
||||||
|
}
|
||||||
|
|
||||||
|
Row {
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.vendor ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: "•"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: (modelData?.driver ?? "").length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.driver ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
visible: (modelData?.driver ?? "").length > 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: modelData?.pciId ?? ""
|
||||||
|
font.pixelSize: Theme.fontSizeSmall - 2
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
opacity: 0.7
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
width: 70
|
||||||
|
height: 32
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: parent.parent.tempEnabled ? Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3) : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.15)
|
||||||
|
border.color: tempMouseArea.containsMouse ? Theme.outline : "transparent"
|
||||||
|
border.width: 1
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: tempRow
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingXS
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "thermostat"
|
||||||
|
size: 16
|
||||||
|
color: {
|
||||||
|
if (!parent.parent.parent.parent.tempEnabled)
|
||||||
|
return Theme.surfaceVariantText;
|
||||||
|
const temp = modelData?.temperature ?? 0;
|
||||||
|
if (temp > 85)
|
||||||
|
return Theme.error;
|
||||||
|
if (temp > 70)
|
||||||
|
return Theme.warning;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
opacity: parent.parent.parent.parent.tempEnabled ? 1 : 0.5
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: {
|
||||||
|
if (!parent.parent.parent.parent.tempEnabled)
|
||||||
|
return I18n.tr("Off");
|
||||||
|
const temp = modelData?.temperature ?? 0;
|
||||||
|
return temp > 0 ? (temp.toFixed(0) + "°C") : "--";
|
||||||
|
}
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: parent.parent.parent.parent.tempEnabled ? SettingsData.monoFontFamily : ""
|
||||||
|
font.weight: parent.parent.parent.parent.tempEnabled ? Font.Bold : Font.Normal
|
||||||
|
color: {
|
||||||
|
if (!parent.parent.parent.parent.tempEnabled)
|
||||||
|
return Theme.surfaceVariantText;
|
||||||
|
const temp = modelData?.temperature ?? 0;
|
||||||
|
if (temp > 85)
|
||||||
|
return Theme.error;
|
||||||
|
if (temp > 70)
|
||||||
|
return Theme.warning;
|
||||||
|
return Theme.surfaceText;
|
||||||
|
}
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: tempMouseArea
|
||||||
|
anchors.fill: parent
|
||||||
|
hoverEnabled: true
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
onClicked: {
|
||||||
|
const pciId = modelData?.pciId;
|
||||||
|
if (!pciId)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const enabledIds = SessionData.enabledGpuPciIds ? SessionData.enabledGpuPciIds.slice() : [];
|
||||||
|
const idx = enabledIds.indexOf(pciId);
|
||||||
|
const wasEnabled = idx !== -1;
|
||||||
|
|
||||||
|
if (!wasEnabled) {
|
||||||
|
enabledIds.push(pciId);
|
||||||
|
DgopService.addGpuPciId(pciId);
|
||||||
|
} else {
|
||||||
|
enabledIds.splice(idx, 1);
|
||||||
|
DgopService.removeGpuPciId(pciId);
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionData.setEnabledGpuPciIds(enabledIds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: Theme.shortDuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: 300
|
||||||
|
height: 100
|
||||||
|
radius: Theme.cornerRadius
|
||||||
|
color: "transparent"
|
||||||
|
visible: DgopService.availableGpus.length === 0
|
||||||
|
|
||||||
|
Column {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
spacing: Theme.spacingM
|
||||||
|
|
||||||
|
DankIcon {
|
||||||
|
name: "developer_board_off"
|
||||||
|
size: 32
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: I18n.tr("No GPUs detected", "empty state in gpu list")
|
||||||
|
font.pixelSize: Theme.fontSizeMedium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
component InfoRow: RowLayout {
|
||||||
|
property string label: ""
|
||||||
|
property string value: ""
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
spacing: Theme.spacingS
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: label + ":"
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.weight: Font.Medium
|
||||||
|
color: Theme.surfaceVariantText
|
||||||
|
Layout.preferredWidth: 100
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
text: value
|
||||||
|
font.pixelSize: Theme.fontSizeSmall
|
||||||
|
font.family: SettingsData.monoFontFamily
|
||||||
|
color: Theme.surfaceText
|
||||||
|
Layout.fillWidth: true
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,7 @@ Singleton {
|
|||||||
property int processLimit: 20
|
property int processLimit: 20
|
||||||
property string processSort: "cpu"
|
property string processSort: "cpu"
|
||||||
property bool noCpu: false
|
property bool noCpu: false
|
||||||
|
property int dgopProcessPid: 0
|
||||||
|
|
||||||
// Cursor data for accurate CPU calculations
|
// Cursor data for accurate CPU calculations
|
||||||
property string cpuCursor: ""
|
property string cpuCursor: ""
|
||||||
@@ -59,6 +60,7 @@ Singleton {
|
|||||||
property var processes: []
|
property var processes: []
|
||||||
property var allProcesses: []
|
property var allProcesses: []
|
||||||
property string currentSort: "cpu"
|
property string currentSort: "cpu"
|
||||||
|
property bool sortAscending: false
|
||||||
property var availableGpus: []
|
property var availableGpus: []
|
||||||
|
|
||||||
property string kernelVersion: ""
|
property string kernelVersion: ""
|
||||||
@@ -93,10 +95,8 @@ Singleton {
|
|||||||
if (modules) {
|
if (modules) {
|
||||||
const modulesToAdd = Array.isArray(modules) ? modules : [modules];
|
const modulesToAdd = Array.isArray(modules) ? modules : [modules];
|
||||||
for (const module of modulesToAdd) {
|
for (const module of modulesToAdd) {
|
||||||
// Increment reference count for this module
|
|
||||||
const currentCount = moduleRefCounts[module] || 0;
|
const currentCount = moduleRefCounts[module] || 0;
|
||||||
moduleRefCounts[module] = currentCount + 1;
|
moduleRefCounts[module] = currentCount + 1;
|
||||||
console.log("Adding ref for module:", module, "count:", moduleRefCounts[module]);
|
|
||||||
|
|
||||||
// Add to enabled modules if not already there
|
// Add to enabled modules if not already there
|
||||||
if (enabledModules.indexOf(module) === -1) {
|
if (enabledModules.indexOf(module) === -1) {
|
||||||
@@ -126,17 +126,13 @@ Singleton {
|
|||||||
for (const module of modulesToRemove) {
|
for (const module of modulesToRemove) {
|
||||||
const currentCount = moduleRefCounts[module] || 0;
|
const currentCount = moduleRefCounts[module] || 0;
|
||||||
if (currentCount > 1) {
|
if (currentCount > 1) {
|
||||||
// Decrement reference count
|
|
||||||
moduleRefCounts[module] = currentCount - 1;
|
moduleRefCounts[module] = currentCount - 1;
|
||||||
console.log("Removing ref for module:", module, "count:", moduleRefCounts[module]);
|
|
||||||
} else if (currentCount === 1) {
|
} else if (currentCount === 1) {
|
||||||
// Remove completely when count reaches 0
|
|
||||||
delete moduleRefCounts[module];
|
delete moduleRefCounts[module];
|
||||||
const index = enabledModules.indexOf(module);
|
const index = enabledModules.indexOf(module);
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
enabledModules.splice(index, 1);
|
enabledModules.splice(index, 1);
|
||||||
modulesChanged = true;
|
modulesChanged = true;
|
||||||
console.log("Disabling module:", module, "(no more refs)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -171,17 +167,13 @@ Singleton {
|
|||||||
gpuPciIds = gpuPciIds.concat([pciId]);
|
gpuPciIds = gpuPciIds.concat([pciId]);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Adding GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId]);
|
|
||||||
// Force property change notification
|
|
||||||
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts);
|
gpuPciIdRefCounts = Object.assign({}, gpuPciIdRefCounts);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeGpuPciId(pciId) {
|
function removeGpuPciId(pciId) {
|
||||||
const currentCount = gpuPciIdRefCounts[pciId] || 0;
|
const currentCount = gpuPciIdRefCounts[pciId] || 0;
|
||||||
if (currentCount > 1) {
|
if (currentCount > 1) {
|
||||||
// Decrement reference count
|
|
||||||
gpuPciIdRefCounts[pciId] = currentCount - 1;
|
gpuPciIdRefCounts[pciId] = currentCount - 1;
|
||||||
console.log("Removing GPU PCI ID ref:", pciId, "count:", gpuPciIdRefCounts[pciId]);
|
|
||||||
} else if (currentCount === 1) {
|
} else if (currentCount === 1) {
|
||||||
// Remove completely when count reaches 0
|
// Remove completely when count reaches 0
|
||||||
delete gpuPciIdRefCounts[pciId];
|
delete gpuPciIdRefCounts[pciId];
|
||||||
@@ -203,8 +195,6 @@ Singleton {
|
|||||||
}
|
}
|
||||||
availableGpus = updatedGpus;
|
availableGpus = updatedGpus;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Removing GPU PCI ID completely:", pciId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Force property change notification
|
// Force property change notification
|
||||||
@@ -389,8 +379,12 @@ Singleton {
|
|||||||
if (data.processes && Array.isArray(data.processes)) {
|
if (data.processes && Array.isArray(data.processes)) {
|
||||||
const newProcesses = [];
|
const newProcesses = [];
|
||||||
processSampleCount++;
|
processSampleCount++;
|
||||||
|
const ourPid = dgopProcessPid;
|
||||||
|
|
||||||
for (const proc of data.processes) {
|
for (const proc of data.processes) {
|
||||||
|
if (ourPid > 0 && proc.pid === ourPid)
|
||||||
|
continue;
|
||||||
|
|
||||||
const cpuUsage = processSampleCount >= 2 ? (proc.cpu || 0) : 0;
|
const cpuUsage = processSampleCount >= 2 ? (proc.cpu || 0) : 0;
|
||||||
|
|
||||||
newProcesses.push({
|
newProcesses.push({
|
||||||
@@ -575,41 +569,49 @@ Singleton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function setSortBy(newSortBy) {
|
function setSortBy(newSortBy) {
|
||||||
if (newSortBy !== currentSort) {
|
if (newSortBy === currentSort) {
|
||||||
|
sortAscending = !sortAscending;
|
||||||
|
} else {
|
||||||
currentSort = newSortBy;
|
currentSort = newSortBy;
|
||||||
applySorting();
|
sortAscending = false;
|
||||||
}
|
}
|
||||||
|
applySorting();
|
||||||
}
|
}
|
||||||
|
|
||||||
function applySorting() {
|
function applySorting() {
|
||||||
if (!allProcesses || allProcesses.length === 0) {
|
if (!allProcesses || allProcesses.length === 0)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
|
const asc = sortAscending;
|
||||||
const sorted = allProcesses.slice();
|
const sorted = allProcesses.slice();
|
||||||
sorted.sort((a, b) => {
|
sorted.sort((a, b) => {
|
||||||
let valueA, valueB;
|
let valueA, valueB, result;
|
||||||
|
|
||||||
switch (currentSort) {
|
switch (currentSort) {
|
||||||
case "cpu":
|
case "cpu":
|
||||||
valueA = a.cpu || 0;
|
valueA = a.cpu || 0;
|
||||||
valueB = b.cpu || 0;
|
valueB = b.cpu || 0;
|
||||||
return valueB - valueA;
|
result = valueB - valueA;
|
||||||
|
break;
|
||||||
case "memory":
|
case "memory":
|
||||||
valueA = a.memoryKB || 0;
|
valueA = a.memoryKB || 0;
|
||||||
valueB = b.memoryKB || 0;
|
valueB = b.memoryKB || 0;
|
||||||
return valueB - valueA;
|
result = valueB - valueA;
|
||||||
|
break;
|
||||||
case "name":
|
case "name":
|
||||||
valueA = (a.command || "").toLowerCase();
|
valueA = (a.command || "").toLowerCase();
|
||||||
valueB = (b.command || "").toLowerCase();
|
valueB = (b.command || "").toLowerCase();
|
||||||
return valueA.localeCompare(valueB);
|
result = valueA.localeCompare(valueB);
|
||||||
|
break;
|
||||||
case "pid":
|
case "pid":
|
||||||
valueA = a.pid || 0;
|
valueA = a.pid || 0;
|
||||||
valueB = b.pid || 0;
|
valueB = b.pid || 0;
|
||||||
return valueA - valueB;
|
result = valueA - valueB;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
return asc ? -result : result;
|
||||||
});
|
});
|
||||||
|
|
||||||
processes = sorted.slice(0, processLimit);
|
processes = sorted.slice(0, processLimit);
|
||||||
@@ -628,10 +630,7 @@ Singleton {
|
|||||||
id: dgopProcess
|
id: dgopProcess
|
||||||
command: root.buildDgopCommand()
|
command: root.buildDgopCommand()
|
||||||
running: false
|
running: false
|
||||||
onCommandChanged:
|
onStarted: dgopProcessPid = processId ?? 0
|
||||||
|
|
||||||
//console.log("DgopService command:", JSON.stringify(command))
|
|
||||||
{}
|
|
||||||
onExited: exitCode => {
|
onExited: exitCode => {
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
console.warn("Dgop process failed with exit code:", exitCode);
|
console.warn("Dgop process failed with exit code:", exitCode);
|
||||||
|
|||||||
Reference in New Issue
Block a user