repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
quickshell/nuguland/PowerPanel.qml
Rawpragma 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)
}
}
}
}
}