diff options
| author | Nippy <nippy@rp1.hu> | 2026-05-08 19:50:49 +0200 |
|---|---|---|
| committer | Nippy <nippy@rp1.hu> | 2026-05-08 19:50:49 +0200 |
| commit | 3462bcc46ecf74f576ea4397f16492c16bd75b10 (patch) | |
| tree | c5ab7aef4498647e057bda8d49c11e5f9dda74c9 /raveos-hyprland-theme/theme-data/dms/Modules/ProcessList/ProcessContextMenu.qml | |
| parent | 56616f693b4f263cbf0d47da43b67424efa6022d (diff) | |
| download | RaveOS-PKGBUILD-3462bcc46ecf74f576ea4397f16492c16bd75b10.tar.gz RaveOS-PKGBUILD-3462bcc46ecf74f576ea4397f16492c16bd75b10.zip | |
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/dms/Modules/ProcessList/ProcessContextMenu.qml')
| -rw-r--r-- | raveos-hyprland-theme/theme-data/dms/Modules/ProcessList/ProcessContextMenu.qml | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/dms/Modules/ProcessList/ProcessContextMenu.qml b/raveos-hyprland-theme/theme-data/dms/Modules/ProcessList/ProcessContextMenu.qml new file mode 100644 index 0000000..7c48b5a --- /dev/null +++ b/raveos-hyprland-theme/theme-data/dms/Modules/ProcessList/ProcessContextMenu.qml @@ -0,0 +1,341 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import qs.Common +import qs.Services +import qs.Widgets + +Popup { + id: processContextMenu + + property var processData: null + property int selectedIndex: -1 + property bool keyboardNavigation: false + property var parentFocusItem: null + + signal menuClosed + signal processKilled + + readonly property var menuItems: [ + { + text: I18n.tr("Copy PID"), + icon: "tag", + action: copyPid, + enabled: true + }, + { + text: I18n.tr("Copy Name"), + icon: "content_copy", + action: copyName, + enabled: true + }, + { + text: I18n.tr("Copy Full Command"), + icon: "code", + action: copyFullCommand, + enabled: true + }, + { + type: "separator" + }, + { + text: I18n.tr("Kill Process"), + icon: "close", + action: killProcess, + enabled: true, + dangerous: true + }, + { + text: I18n.tr("Force Kill (SIGKILL)"), + icon: "dangerous", + action: forceKillProcess, + enabled: processData && processData.pid > 1000, + dangerous: true + } + ] + + readonly property int visibleItemCount: { + let count = 0; + for (let i = 0; i < menuItems.length; i++) { + if (menuItems[i].type !== "separator") + count++; + } + return count; + } + + function show(x, y, fromKeyboard) { + let finalX = x; + let finalY = y; + + if (processContextMenu.parent) { + const parentWidth = processContextMenu.parent.width; + const parentHeight = processContextMenu.parent.height; + const menuWidth = processContextMenu.width; + const menuHeight = processContextMenu.height; + + if (finalX + menuWidth > parentWidth) + finalX = Math.max(0, parentWidth - menuWidth); + if (finalY + menuHeight > parentHeight) + finalY = Math.max(0, parentHeight - menuHeight); + } + + processContextMenu.x = finalX; + processContextMenu.y = finalY; + keyboardNavigation = fromKeyboard || false; + selectedIndex = fromKeyboard ? 0 : -1; + open(); + } + + function selectNext() { + if (visibleItemCount === 0) + return; + let current = selectedIndex; + let next = current; + do { + next = (next + 1) % menuItems.length; + } while (menuItems[next].type === "separator" && next !== current) + selectedIndex = next; + } + + function selectPrevious() { + if (visibleItemCount === 0) + return; + let current = selectedIndex; + let prev = current; + do { + prev = (prev - 1 + menuItems.length) % menuItems.length; + } while (menuItems[prev].type === "separator" && prev !== current) + selectedIndex = prev; + } + + function activateSelected() { + if (selectedIndex < 0 || selectedIndex >= menuItems.length) + return; + const item = menuItems[selectedIndex]; + if (item.type === "separator" || !item.enabled) + return; + item.action(); + } + + function copyPid() { + if (processData) + Quickshell.execDetached(["dms", "cl", "copy", processData.pid.toString()]); + close(); + } + + function copyName() { + if (processData) { + const name = processData.command || ""; + Quickshell.execDetached(["dms", "cl", "copy", name]); + } + close(); + } + + function copyFullCommand() { + if (processData) { + const fullCmd = processData.fullCommand || processData.command || ""; + Quickshell.execDetached(["dms", "cl", "copy", fullCmd]); + } + close(); + } + + function killProcess() { + if (processData) + Quickshell.execDetached(["kill", processData.pid.toString()]); + processKilled(); + close(); + } + + function forceKillProcess() { + if (processData) + Quickshell.execDetached(["kill", "-9", processData.pid.toString()]); + processKilled(); + close(); + } + + width: 200 + height: menuColumn.implicitHeight + Theme.spacingS * 2 + padding: 0 + modal: false + closePolicy: Popup.CloseOnEscape + + onClosed: { + closePolicy = Popup.CloseOnEscape; + keyboardNavigation = false; + selectedIndex = -1; + menuClosed(); + if (parentFocusItem) + Qt.callLater(() => parentFocusItem.forceActiveFocus()); + } + + onOpened: { + outsideClickTimer.start(); + if (keyboardNavigation) + Qt.callLater(() => keyboardHandler.forceActiveFocus()); + } + + Timer { + id: outsideClickTimer + interval: 100 + onTriggered: processContextMenu.closePolicy = Popup.CloseOnEscape | Popup.CloseOnPressOutside + } + + background: Rectangle { + color: "transparent" + } + + contentItem: Rectangle { + color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + radius: Theme.cornerRadius + border.color: BlurService.enabled ? BlurService.borderColor : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) + border.width: BlurService.enabled ? BlurService.borderWidth : 1 + + Item { + id: keyboardHandler + anchors.fill: parent + focus: keyboardNavigation + + Keys.onPressed: event => { + switch (event.key) { + case Qt.Key_Down: + case Qt.Key_J: + keyboardNavigation = true; + selectNext(); + event.accepted = true; + return; + case Qt.Key_Up: + case Qt.Key_K: + keyboardNavigation = true; + selectPrevious(); + event.accepted = true; + return; + case Qt.Key_Return: + case Qt.Key_Enter: + case Qt.Key_Space: + activateSelected(); + event.accepted = true; + return; + case Qt.Key_Escape: + case Qt.Key_Left: + case Qt.Key_H: + close(); + event.accepted = true; + return; + } + } + } + + Column { + id: menuColumn + anchors.fill: parent + anchors.margins: Theme.spacingS + spacing: 1 + + Repeater { + model: menuItems + + Item { + width: parent.width + height: modelData.type === "separator" ? 5 : 32 + visible: modelData.type !== "separator" || index > 0 + + property int itemVisibleIndex: { + let count = 0; + for (let i = 0; i < index; i++) { + if (menuItems[i].type !== "separator") + count++; + } + return count; + } + + Rectangle { + visible: modelData.type === "separator" + width: parent.width - Theme.spacingS * 2 + height: 1 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.15) + } + + Rectangle { + id: menuItem + visible: modelData.type !== "separator" + width: parent.width + height: 32 + radius: Theme.cornerRadius + color: { + if (!modelData.enabled) + return "transparent"; + const isSelected = keyboardNavigation && selectedIndex === index; + if (modelData.dangerous) { + if (isSelected) + return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.2); + return menuItemArea.containsMouse ? Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.12) : "transparent"; + } + if (isSelected) + return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.2); + return menuItemArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent"; + } + opacity: modelData.enabled ? 1 : 0.5 + + Row { + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: modelData.icon || "" + size: 16 + color: { + if (!modelData.enabled) + return Theme.surfaceVariantText; + const isSelected = keyboardNavigation && selectedIndex === index; + if (modelData.dangerous && (menuItemArea.containsMouse || isSelected)) + return Theme.error; + return Theme.surfaceText; + } + anchors.verticalCenter: parent.verticalCenter + } + + StyledText { + text: modelData.text || "" + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Normal + color: { + if (!modelData.enabled) + return Theme.surfaceVariantText; + const isSelected = keyboardNavigation && selectedIndex === index; + if (modelData.dangerous && (menuItemArea.containsMouse || isSelected)) + return Theme.error; + return Theme.surfaceText; + } + anchors.verticalCenter: parent.verticalCenter + } + } + + DankRipple { + id: menuItemRipple + rippleColor: modelData.dangerous ? Theme.error : Theme.surfaceText + cornerRadius: menuItem.radius + } + + MouseArea { + id: menuItemArea + anchors.fill: parent + hoverEnabled: true + cursorShape: modelData.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor + enabled: modelData.enabled ?? false + onEntered: { + keyboardNavigation = false; + selectedIndex = index; + } + onPressed: mouse => menuItemRipple.trigger(mouse.x, mouse.y) + onClicked: modelData.action() + } + } + } + } + } + } +} |