From 48f77e1691e94ecb80e3962bf255fd386327df75 Mon Sep 17 00:00:00 2001 From: bbedward Date: Sun, 23 Nov 2025 12:16:03 -0500 Subject: [PATCH] processlist: convert to floating window --- core/internal/config/embedded/hyprland.conf | 4 +- core/internal/config/embedded/niri.kdl | 9 +- quickshell/Modals/ProcessListModal.qml | 483 ++++++++++---------- 3 files changed, 251 insertions(+), 245 deletions(-) diff --git a/core/internal/config/embedded/hyprland.conf b/core/internal/config/embedded/hyprland.conf index 082c9289..afe78eb1 100644 --- a/core/internal/config/embedded/hyprland.conf +++ b/core/internal/config/embedded/hyprland.conf @@ -126,9 +126,7 @@ windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$ windowrulev2 = float, class:^(zoom)$ # DMS windows floating by default -windowrulev2 = float, class:^(org.quickshell)$, title:^(Settings)$ -windowrulev2 = float, class:^(org.quickshell)$, title:^(File)$ - +windowrulev2 = float, class:^(org.quickshell)$ windowrulev2 = opacity 0.9 0.9, floating:0, focus:0 layerrule = noanim, ^(quickshell)$ diff --git a/core/internal/config/embedded/niri.kdl b/core/internal/config/embedded/niri.kdl index 6ce37b88..d435eeae 100644 --- a/core/internal/config/embedded/niri.kdl +++ b/core/internal/config/embedded/niri.kdl @@ -218,16 +218,9 @@ window-rule { geometry-corner-radius 12 clip-to-geometry true } -// Open dms settings as floating by default +// Open dms windows as floating by default window-rule { match app-id=r#"org.quickshell$"# - match title="Settings" - open-floating true -} -// dms file browser -window-rule { - match app-id=r#"org.quickshell$"# - match title=r#"File$"# open-floating true } binds { diff --git a/quickshell/Modals/ProcessListModal.qml b/quickshell/Modals/ProcessListModal.qml index 1d3c61a3..b92d09e1 100644 --- a/quickshell/Modals/ProcessListModal.qml +++ b/quickshell/Modals/ProcessListModal.qml @@ -1,30 +1,32 @@ import QtQuick import QtQuick.Layouts +import Quickshell import qs.Common -import qs.Modals.Common import qs.Modules.ProcessList import qs.Services import qs.Widgets -DankModal { +FloatingWindow { id: processListModal - layerNamespace: "dms:process-list-modal" - property int currentTab: 0 property var tabNames: ["Processes", "Performance", "System"] + property bool shouldHaveFocus: visible + property alias shouldBeVisible: processListModal.visible + + signal closingModal function show() { if (!DgopService.dgopAvailable) { console.warn("ProcessListModal: dgop is not available"); return; } - open(); + visible = true; UserInfoService.getUptime(); } function hide() { - close(); + visible = false; if (processContextMenu.visible) { processContextMenu.close(); } @@ -35,20 +37,26 @@ DankModal { console.warn("ProcessListModal: dgop is not available"); return; } - if (shouldBeVisible) { - hide(); - } else { - show(); - } + visible = !visible; } - modalWidth: 900 - modalHeight: 680 - backgroundColor: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) - cornerRadius: Theme.cornerRadius - enableShadow: true - onBackgroundClicked: () => { - return hide(); + objectName: "processListModal" + title: "System Monitor" + implicitWidth: 900 + implicitHeight: 680 + color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + visible: false + + onVisibleChanged: { + if (!visible) { + closingModal(); + } else { + Qt.callLater(() => { + if (contentFocusScope) { + contentFocusScope.forceActiveFocus(); + } + }); + } } Component { @@ -75,248 +83,255 @@ DankModal { id: processContextMenu } - content: Component { - Item { - anchors.fill: parent - focus: true - Keys.onPressed: event => { - if (event.key === Qt.Key_Escape) { - processListModal.hide(); - event.accepted = true; - } else if (event.key === Qt.Key_1) { - currentTab = 0; - event.accepted = true; - } else if (event.key === Qt.Key_2) { - currentTab = 1; - event.accepted = true; - } else if (event.key === Qt.Key_3) { - currentTab = 2; - event.accepted = true; - } + FocusScope { + id: contentFocusScope + + anchors.fill: parent + focus: true + + Keys.onPressed: event => { + if (event.key === Qt.Key_Escape) { + hide(); + event.accepted = true; + return; } - // Show error message when dgop is not available - Rectangle { + switch (event.key) { + case Qt.Key_1: + currentTab = 0; + event.accepted = true; + return; + case Qt.Key_2: + currentTab = 1; + event.accepted = true; + return; + case Qt.Key_3: + currentTab = 2; + event.accepted = true; + return; + } + } + + Rectangle { + anchors.centerIn: parent + width: 400 + height: 200 + radius: Theme.cornerRadius + color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1) + border.color: Theme.error + border.width: 2 + visible: !DgopService.dgopAvailable + + Column { anchors.centerIn: parent - width: 400 - height: 200 + spacing: Theme.spacingL + + DankIcon { + name: "error" + size: 48 + color: Theme.error + anchors.horizontalCenter: parent.horizontalCenter + } + + StyledText { + text: I18n.tr("System Monitor Unavailable") + font.pixelSize: Theme.fontSizeLarge + font.weight: Font.Bold + color: Theme.error + anchors.horizontalCenter: parent.horizontalCenter + } + + StyledText { + text: I18n.tr("The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature.") + font.pixelSize: Theme.fontSizeMedium + color: Theme.surfaceText + anchors.horizontalCenter: parent.horizontalCenter + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + } + } + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: Theme.spacingL + spacing: Theme.spacingL + visible: DgopService.dgopAvailable + + RowLayout { + Layout.fillWidth: true + height: 40 + + StyledText { + text: I18n.tr("System Monitor") + font.pixelSize: Theme.fontSizeLarge + 4 + font.weight: Font.Bold + color: Theme.surfaceText + Layout.alignment: Qt.AlignVCenter + } + + Item { + Layout.fillWidth: true + } + + DankActionButton { + circular: false + iconName: "close" + iconSize: Theme.iconSize - 4 + iconColor: Theme.surfaceText + onClicked: () => { + processListModal.hide(); + } + Layout.alignment: Qt.AlignVCenter + } + } + + Rectangle { + Layout.fillWidth: true + height: 52 + color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) radius: Theme.cornerRadius - color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1) - border.color: Theme.error - border.width: 2 - visible: !DgopService.dgopAvailable + border.color: Theme.outlineLight + border.width: 1 - Column { - anchors.centerIn: parent - spacing: Theme.spacingL + Row { + anchors.fill: parent + anchors.margins: 4 + spacing: 2 - DankIcon { - name: "error" - size: 48 - color: Theme.error - anchors.horizontalCenter: parent.horizontalCenter - } + Repeater { + model: tabNames - StyledText { - text: I18n.tr("System Monitor Unavailable") - font.pixelSize: Theme.fontSizeLarge - font.weight: Font.Bold - color: Theme.error - anchors.horizontalCenter: parent.horizontalCenter - } + Rectangle { + width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length + height: 44 + radius: Theme.cornerRadius + color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent") + border.color: currentTab === index ? Theme.primary : "transparent" + border.width: currentTab === index ? 1 : 0 - StyledText { - text: I18n.tr("The 'dgop' tool is required for system monitoring.\nPlease install dgop to use this feature.") - font.pixelSize: Theme.fontSizeMedium - color: Theme.surfaceText - anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap + Row { + anchors.centerIn: parent + spacing: Theme.spacingXS + + DankIcon { + name: { + const tabIcons = ["list_alt", "analytics", "settings"]; + return tabIcons[index] || "tab"; + } + size: Theme.iconSize - 2 + color: currentTab === index ? Theme.primary : Theme.surfaceText + opacity: currentTab === index ? 1 : 0.7 + anchors.verticalCenter: parent.verticalCenter + + Behavior on color { + ColorAnimation { + duration: Theme.shortDuration + } + } + } + + StyledText { + text: modelData + font.pixelSize: Theme.fontSizeLarge + font.weight: Font.Medium + color: currentTab === index ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + anchors.verticalCenterOffset: -1 + + Behavior on color { + ColorAnimation { + duration: Theme.shortDuration + } + } + } + } + + MouseArea { + id: tabMouseArea + + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: () => { + currentTab = index; + } + } + + Behavior on color { + ColorAnimation { + duration: Theme.shortDuration + } + } + + Behavior on border.color { + ColorAnimation { + duration: Theme.shortDuration + } + } + } } } } - ColumnLayout { - anchors.fill: parent - anchors.margins: Theme.spacingL - spacing: Theme.spacingL - visible: DgopService.dgopAvailable + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + radius: Theme.cornerRadius + color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) + border.color: Theme.outlineLight + border.width: 1 - RowLayout { - Layout.fillWidth: true - height: 40 + Loader { + id: processesTab - StyledText { - text: I18n.tr("System Monitor") - font.pixelSize: Theme.fontSizeLarge + 4 - font.weight: Font.Bold - color: Theme.surfaceText - Layout.alignment: Qt.AlignVCenter - } + anchors.fill: parent + anchors.margins: Theme.spacingS + active: processListModal.visible && currentTab === 0 + visible: currentTab === 0 + opacity: currentTab === 0 ? 1 : 0 + sourceComponent: processesTabComponent - Item { - Layout.fillWidth: true - } - - DankActionButton { - circular: false - iconName: "close" - iconSize: Theme.iconSize - 4 - iconColor: Theme.surfaceText - onClicked: () => { - return processListModal.hide(); - } - Layout.alignment: Qt.AlignVCenter - } - } - - Rectangle { - Layout.fillWidth: true - height: 52 - color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) - radius: Theme.cornerRadius - border.color: Theme.outlineLight - border.width: 1 - - Row { - anchors.fill: parent - anchors.margins: 4 - spacing: 2 - - Repeater { - model: tabNames - - Rectangle { - width: (parent.width - (tabNames.length - 1) * 2) / tabNames.length - height: 44 - radius: Theme.cornerRadius - color: currentTab === index ? Theme.primaryPressed : (tabMouseArea.containsMouse ? Theme.primaryHoverLight : "transparent") - border.color: currentTab === index ? Theme.primary : "transparent" - border.width: currentTab === index ? 1 : 0 - - Row { - anchors.centerIn: parent - spacing: Theme.spacingXS - - DankIcon { - name: { - const tabIcons = ["list_alt", "analytics", "settings"]; - return tabIcons[index] || "tab"; - } - size: Theme.iconSize - 2 - color: currentTab === index ? Theme.primary : Theme.surfaceText - opacity: currentTab === index ? 1 : 0.7 - anchors.verticalCenter: parent.verticalCenter - - Behavior on color { - ColorAnimation { - duration: Theme.shortDuration - } - } - } - - StyledText { - text: modelData - font.pixelSize: Theme.fontSizeLarge - font.weight: Font.Medium - color: currentTab === index ? Theme.primary : Theme.surfaceText - anchors.verticalCenter: parent.verticalCenter - anchors.verticalCenterOffset: -1 - - Behavior on color { - ColorAnimation { - duration: Theme.shortDuration - } - } - } - } - - MouseArea { - id: tabMouseArea - - anchors.fill: parent - hoverEnabled: true - cursorShape: Qt.PointingHandCursor - onClicked: () => { - currentTab = index; - } - } - - Behavior on color { - ColorAnimation { - duration: Theme.shortDuration - } - } - - Behavior on border.color { - ColorAnimation { - duration: Theme.shortDuration - } - } - } + Behavior on opacity { + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing } } } - Rectangle { - Layout.fillWidth: true - Layout.fillHeight: true - radius: Theme.cornerRadius - color: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency) - border.color: Theme.outlineLight - border.width: 1 + Loader { + id: performanceTab - Loader { - id: processesTab + anchors.fill: parent + anchors.margins: Theme.spacingS + active: processListModal.visible && currentTab === 1 + visible: currentTab === 1 + opacity: currentTab === 1 ? 1 : 0 + sourceComponent: performanceTabComponent - anchors.fill: parent - anchors.margins: Theme.spacingS - active: processListModal.shouldBeVisible && currentTab === 0 - visible: currentTab === 0 - opacity: currentTab === 0 ? 1 : 0 - sourceComponent: processesTabComponent - - Behavior on opacity { - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } + Behavior on opacity { + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing } } + } - Loader { - id: performanceTab + Loader { + id: systemTab - anchors.fill: parent - anchors.margins: Theme.spacingS - active: processListModal.shouldBeVisible && currentTab === 1 - visible: currentTab === 1 - opacity: currentTab === 1 ? 1 : 0 - sourceComponent: performanceTabComponent + anchors.fill: parent + anchors.margins: Theme.spacingS + active: processListModal.visible && currentTab === 2 + visible: currentTab === 2 + opacity: currentTab === 2 ? 1 : 0 + sourceComponent: systemTabComponent - Behavior on opacity { - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } - } - } - - Loader { - id: systemTab - - anchors.fill: parent - anchors.margins: Theme.spacingS - active: processListModal.shouldBeVisible && currentTab === 2 - visible: currentTab === 2 - opacity: currentTab === 2 ? 1 : 0 - sourceComponent: systemTabComponent - - Behavior on opacity { - NumberAnimation { - duration: Theme.mediumDuration - easing.type: Theme.emphasizedEasing - } + Behavior on opacity { + NumberAnimation { + duration: Theme.mediumDuration + easing.type: Theme.emphasizedEasing } } }