1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-06 05:25:41 -05:00

Add systemd-inhibit widget

This commit is contained in:
bbedward
2025-08-15 21:52:44 -04:00
parent f67b90cfbe
commit af9607fdbe
6 changed files with 335 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
import QtQuick
import QtQuick.Effects
import Quickshell
import Quickshell.Wayland
import Quickshell.Widgets
import qs.Common
import qs.Services
import qs.Widgets
PanelWindow {
id: root
property var modelData
property bool inhibitorPopupVisible: false
function show() {
root.inhibitorPopupVisible = true
hideTimer.restart()
}
screen: modelData
visible: inhibitorPopupVisible
WlrLayershell.layer: WlrLayershell.Overlay
WlrLayershell.exclusiveZone: -1
WlrLayershell.keyboardFocus: WlrKeyboardFocus.None
color: "transparent"
anchors {
top: true
left: true
right: true
bottom: true
}
Timer {
id: hideTimer
interval: 2000
repeat: false
onTriggered: {
root.inhibitorPopupVisible = false
}
}
Connections {
function onInhibitorChanged() {
root.show()
}
target: IdleInhibitorService
}
Rectangle {
id: inhibitorPopup
width: Theme.iconSize + Theme.spacingS * 2
height: Theme.iconSize + Theme.spacingS * 2
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: parent.bottom
anchors.bottomMargin: Theme.spacingM
color: Theme.popupBackground()
radius: Theme.cornerRadius
border.color: Qt.rgba(Theme.outline.r, Theme.outline.g,
Theme.outline.b, 0.08)
border.width: 1
opacity: root.inhibitorPopupVisible ? 1 : 0
scale: root.inhibitorPopupVisible ? 1 : 0.9
layer.enabled: true
DankIcon {
id: inhibitorContent
anchors.centerIn: parent
name: IdleInhibitorService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
size: Theme.iconSize
color: IdleInhibitorService.idleInhibited ? Theme.primary : Theme.outline
}
layer.effect: MultiEffect {
shadowEnabled: true
shadowHorizontalOffset: 0
shadowVerticalOffset: 4
shadowBlur: 0.8
shadowColor: Qt.rgba(0, 0, 0, 0.3)
shadowOpacity: 0.3
}
transform: Translate {
y: root.inhibitorPopupVisible ? 0 : 20
}
Behavior on opacity {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on scale {
NumberAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
Behavior on transform {
PropertyAnimation {
duration: Theme.mediumDuration
easing.type: Theme.emphasizedEasing
}
}
}
mask: Region {
item: inhibitorPopup
}
}

View File

@@ -107,6 +107,12 @@ Item {
"description": "Battery level and power management",
"icon": "battery_std",
"enabled": true
}, {
"id": "idleInhibitor",
"text": "Idle Inhibitor",
"description": "Prevent screen timeout",
"icon": "motion_sensor_active",
"enabled": true
}, {
"id": "spacer",
"text": "Spacer",

View File

@@ -0,0 +1,73 @@
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
Rectangle {
id: root
property string section: "right"
property var popupTarget: null
property var parentScreen: null
width: 40
height: 30
radius: Theme.cornerRadius
color: {
const baseColor = mouseArea.containsMouse
? Theme.primaryPressed
: (IdleInhibitorService.idleInhibited ? Theme.primaryHover : Theme.secondaryHover)
return Qt.rgba(baseColor.r, baseColor.g, baseColor.b,
baseColor.a * Theme.widgetTransparency)
}
DankIcon {
anchors.centerIn: parent
name: IdleInhibitorService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
size: Theme.iconSize - 6
color: Theme.surfaceText
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
IdleInhibitorService.toggleIdleInhibit()
}
}
ToolTip {
visible: mouseArea.containsMouse
delay: 1000
text: IdleInhibitorService.idleInhibited
? "Screen timeout disabled\nClick to enable"
: "Screen timeout enabled\nClick to disable"
contentItem: Text {
text: parent.text || ""
font.family: Theme.fontFamily || "Sans"
font.pixelSize: Theme.fontSize - 2
color: Theme.surfaceText
}
background: Rectangle {
color: Theme.surfaceContainer
radius: Theme.cornerRadius
border.color: Theme.outline
border.width: 1
}
}
Behavior on color {
ColorAnimation {
duration: Theme.shortDuration
easing.type: Theme.standardEasing
}
}
}

View File

@@ -312,6 +312,8 @@ PanelWindow {
return true
case "controlCenterButton":
return true
case "idleInhibitor":
return true
case "spacer":
return true
case "separator":
@@ -355,6 +357,8 @@ PanelWindow {
return batteryComponent
case "controlCenterButton":
return controlCenterButtonComponent
case "idleInhibitor":
return idleInhibitorComponent
case "spacer":
return spacerComponent
case "separator":
@@ -953,6 +957,23 @@ PanelWindow {
}
}
Component {
id: idleInhibitorComponent
IdleInhibitor {
section: {
if (parent && parent.parent === leftSection)
return "left"
if (parent && parent.parent === rightSection)
return "right"
if (parent && parent.parent === centerSection)
return "center"
return "right"
}
parentScreen: root.screen
}
}
Component {
id: spacerComponent

View File

@@ -0,0 +1,110 @@
import QtQuick
import Quickshell
import Quickshell.Io
import qs.Common
pragma Singleton
pragma ComponentBehavior: Bound
Singleton {
id: root
property bool inhibitorAvailable: true
property bool idleInhibited: false
property string inhibitReason: "Keep system awake"
signal inhibitorChanged()
function enableIdleInhibit() {
if (idleInhibited) return;
idleInhibited = true;
inhibitorChanged();
}
function disableIdleInhibit() {
if (!idleInhibited) return;
idleInhibited = false;
inhibitorChanged();
}
function toggleIdleInhibit() {
if (idleInhibited) {
disableIdleInhibit();
} else {
enableIdleInhibit();
}
}
function setInhibitReason(reason) {
inhibitReason = reason;
if (idleInhibited) {
const wasActive = idleInhibited;
idleInhibited = false;
Qt.callLater(() => {
if (wasActive) idleInhibited = true;
});
}
}
Process {
id: idleInhibitProcess
command: {
if (!idleInhibited) {
return ["true"];
}
return [
"systemd-inhibit",
"--what=idle",
"--who=quickshell",
"--why=" + inhibitReason,
"--mode=block",
"sleep", "infinity"
];
}
running: idleInhibited
onExited: function(exitCode) {
if (idleInhibited && exitCode !== 0) {
console.warn("IdleInhibitorService: Inhibitor process crashed with exit code:", exitCode);
idleInhibited = false;
ToastService.showWarning("Idle inhibitor failed");
}
}
}
IpcHandler {
function toggle() : string {
root.toggleIdleInhibit();
return root.idleInhibited ? "Idle inhibit enabled" : "Idle inhibit disabled";
}
function enable() : string {
root.enableIdleInhibit();
return "Idle inhibit enabled";
}
function disable() : string {
root.disableIdleInhibit();
return "Idle inhibit disabled";
}
function status() : string {
return root.idleInhibited ? "Idle inhibit is enabled" : "Idle inhibit is disabled";
}
function reason(newReason: string) : string {
if (!newReason) {
return "Current reason: " + root.inhibitReason;
}
root.setInhibitReason(newReason);
return "Inhibit reason set to: " + newReason;
}
target: "inhibit"
}
}

View File

@@ -265,4 +265,12 @@ ShellRoot {
modelData: item
}
}
Variants {
model: Quickshell.screens
delegate: IdleInhibitorPopup {
modelData: item
}
}
}