Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

quickshell/nuguland/PowerPanel.qml

Raw
pragma ComponentBehavior: Bound
import QtQuick
import "Util.js" as Util

Flickable {
    id: root

    required property Theme theme
    required property Power powerService
    readonly property var panelState: powerService ? Util.parseJson(powerService.stateJson(), {
        "battery": {},
        "externalDevices": []
    }) : ({
            "battery": {},
            "externalDevices": []
        })
    readonly property var battery: panelState.battery || ({})
    readonly property var devices: panelState.externalDevices || []
    readonly property var sessionActions: panelState.sessionActions || ["lock", "suspend", "monitor-off", "monitor-on", "logout", "reboot", "poweroff"]
    readonly property bool hasMainBattery: !!(battery && battery.present)
    readonly property var safeActionNames: ["lock", "suspend", "monitor-off"]
    readonly property var destructiveActionNames: ["logout", "reboot", "poweroff"]
    readonly property var safeActions: actionSubset(safeActionNames)
    readonly property var destructiveActions: actionSubset(destructiveActionNames)

    function runSession(action) {
        if (powerService)
            powerService.sessionAction(action);
    }

    function restoreActionFocus(action) {
        const index = safeActions.concat(destructiveActions).indexOf(action);
        const button = actionRepeater.itemAt(index);
        if (button)
            button.forceActiveFocus(Qt.TabFocusReason);
    }

    function confirm(action) {
        if (powerService)
            powerService.confirmAction(action);
        restoreActionFocus(action);
    }

    function cancel() {
        const action = panelState.pendingConfirmation;
        if (powerService)
            powerService.cancelAction();
        restoreActionFocus(action);
    }

    function roleColor(role) {
        if (!theme)
            return "white";
        if (role === "warning")
            return theme.warning;
        if (role === "error")
            return theme.error;
        if (role === "ui.minor")
            return theme.ui.minor;
        return theme.ui.accent;
    }

    function actionSubset(names) {
        const rows = [];
        for (let i = 0; i < names.length; i++) {
            if (sessionActions.indexOf(names[i]) >= 0)
                rows.push(names[i]);
        }
        return rows;
    }

    function actionIcon(action) {
        if (action === "lock")
            return "\uF023";
        if (action === "suspend")
            return "\uF186";
        if (action === "monitor-off" || action === "monitor-on")
            return "\uF108";
        if (action === "logout")
            return "\uF08B";
        if (action === "reboot")
            return "\uF01E";
        return "\uF011";
    }

    function actionLabel(action) {
        if (action === "suspend")
            return "sleep";
        if (action === "monitor-off")
            return "display off";
        if (action === "poweroff")
            return "power off";
        return action;
    }

    function actionTooltip(action) {
        if (action === "lock")
            return "lock your session";
        if (action === "suspend")
            return "suspend the computer";
        if (action === "monitor-off")
            return "turn the display off";
        if (action === "logout")
            return "end your session";
        if (action === "reboot")
            return "restart the computer";
        return "shut down the computer";
    }

    function batteryMeta() {
        const parts = [];
        if (battery.timeEstimate && battery.timeEstimate !== "unknown")
            parts.push(battery.timeEstimate);
        if (battery.health >= 0)
            parts.push(`health ${battery.health}%`);
        return parts.join(" · ");
    }

    width: parent ? parent.width : 0
    contentWidth: width
    contentHeight: powerCol.height
    clip: true
    interactive: contentHeight > height
    boundsBehavior: Flickable.StopAtBounds
    flickableDirection: Flickable.VerticalFlick

    Column {
        id: powerCol

        width: root.width
        spacing: 12

        Item {
            objectName: "batteryStatus"
            width: parent.width
            height: 48
            visible: root.hasMainBattery

            Rectangle {
                id: batteryIcon

                anchors.left: parent.left
                anchors.verticalCenter: parent.verticalCenter
                width: 34
                height: 20
                radius: 4
                color: "transparent"
                border.width: 1
                border.color: root.theme.ui.minor
                Accessible.ignored: true

                Rectangle {
                    x: 4
                    y: 4
                    width: 26 * Math.max(0, Math.min(1, Number(root.battery.percent || 0) / 100))
                    height: 12
                    radius: 1
                    color: root.roleColor(root.battery.role)
                }

                Rectangle {
                    x: 34
                    y: 6
                    width: 3
                    height: 8
                    radius: 1
                    color: root.theme.ui.minor
                }
            }

            Column {
                anchors.left: batteryIcon.right
                anchors.leftMargin: 16
                anchors.right: parent.right
                anchors.verticalCenter: parent.verticalCenter
                spacing: 2

                Row {
                    spacing: 10

                    NuguText {
                        id: batteryPercent
                        theme: root.theme
                        variant: "title"
                        font.pixelSize: 18
                        text: `${root.battery.percent || 0}%`
                    }

                    NuguText {
                        anchors.baseline: batteryPercent.baseline
                        theme: root.theme
                        variant: "meta"
                        text: root.battery.state || "unknown"
                        color: root.theme.ui.minor
                    }
                }

                NuguText {
                    theme: root.theme
                    variant: "meta"
                    width: parent.width
                    visible: root.batteryMeta().length > 0
                    text: root.batteryMeta()
                    color: root.theme.ui.minor
                    elide: Text.ElideRight
                }
            }
        }

        Flow {
            id: deviceFlow

            objectName: "externalDeviceBatteryChips"
            width: parent.width
            height: visible ? implicitHeight : 0
            spacing: 8
            visible: root.devices.length > 0

            Repeater {
                model: root.devices

                delegate: PopupCard {
                    id: deviceChip

                    required property var modelData

                    theme: root.theme
                    padding: 0
                    width: Math.floor((root.width - 8) / 2)
                    height: 32

                    NuguText {
                        anchors.left: parent.left
                        anchors.leftMargin: 9
                        anchors.right: devicePercent.left
                        anchors.rightMargin: 8
                        anchors.verticalCenter: parent.verticalCenter
                        theme: root.theme
                        variant: "meta"
                        text: `${deviceChip.modelData.name}`
                        color: root.theme.ui.minor
                        elide: Text.ElideRight
                    }

                    NuguText {
                        id: devicePercent

                        anchors.right: parent.right
                        anchors.rightMargin: 9
                        anchors.verticalCenter: parent.verticalCenter
                        theme: root.theme
                        variant: "meta"
                        text: `${deviceChip.modelData.percent}%`
                        color: root.theme.content.normal
                    }
                }
            }
        }

        Grid {
            id: actionGrid

            objectName: "powerActionGrid"
            width: parent.width
            columns: 3
            spacing: 8

            Repeater {
                id: actionRepeater
                model: root.safeActions.concat(root.destructiveActions)

                delegate: PopupButton {
                    id: actionButton

                    required property string modelData
                    readonly property bool risky: root.destructiveActionNames.indexOf(modelData) >= 0
                    readonly property color labelColor: risky ? (hovered ? root.theme.error : root.theme.ui.normal) : root.theme.content.normal

                    theme: root.theme
                    width: Math.floor((root.width - actionGrid.spacing * 2) / 3)
                    height: 66
                    text: root.actionLabel(modelData)
                    showText: false
                    tooltip: root.panelState.pendingConfirmation ? "" : root.actionTooltip(modelData)
                    role: risky ? "ghost" : "normal"
                    onControlFocusedChanged: if (controlFocused)
                        Util.revealIn(this, root)
                    onClicked: root.runSession(modelData)

                    Column {
                        anchors.centerIn: parent
                        spacing: 6

                        Text {
                            anchors.horizontalCenter: parent.horizontalCenter
                            font.family: "Symbols Nerd Font Mono"
                            font.pixelSize: 20
                            color: actionButton.labelColor
                            text: root.actionIcon(actionButton.modelData)
                            Accessible.ignored: true
                        }

                        NuguText {
                            anchors.horizontalCenter: parent.horizontalCenter
                            theme: root.theme
                            variant: "meta"
                            color: actionButton.labelColor
                            text: actionButton.text
                            Accessible.ignored: true
                        }
                    }
                }
            }
        }

        Item {
            objectName: "powerConfirmation"
            width: parent.width
            height: 104
            visible: !!root.panelState.pendingConfirmation

            Rectangle {
                width: parent.width
                height: 1
                color: root.theme.withAlpha(root.theme.ui.minor, 0.2)
            }

            Column {
                y: 13
                width: parent.width
                spacing: 2

                NuguText {
                    theme: root.theme
                    variant: "title"
                    text: `${root.actionLabel(root.panelState.pendingConfirmation || "")}?`
                }

                NuguText {
                    theme: root.theme
                    variant: "meta"
                    text: "unsaved work may be lost"
                    color: root.theme.ui.minor
                }
            }

            Row {
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                spacing: 8

                PopupButton {
                    theme: root.theme
                    text: "cancel"
                    tooltip: "keep this session running"
                    onControlFocusedChanged: if (controlFocused)
                        Util.revealIn(this, root)
                    onClicked: root.cancel()
                }

                PopupButton {
                    theme: root.theme
                    text: root.actionLabel(root.panelState.pendingConfirmation || "")
                    accessibleName: `confirm ${text}`
                    tooltip: root.actionTooltip(root.panelState.pendingConfirmation)
                    role: "destructive"
                    primary: true
                    onControlFocusedChanged: if (controlFocused)
                        Util.revealIn(this, root)
                    onClicked: root.confirm(root.panelState.pendingConfirmation)
                }
            }
        }
    }
}