Add --fit OSD option and use it for system shutdown/reboot/logout
This commit is contained in:
+6
-3
@@ -1,8 +1,8 @@
|
||||
#!/bin/bash
|
||||
|
||||
# omarchy:summary=Show the Omarchy Quickshell on-screen display
|
||||
# omarchy:args=[-i|--icon <icon>] [-m|--message <text>] [-p|--progress <0-100>] [-d|--duration <ms>]
|
||||
# omarchy:examples=omarchy osd -i brightness -p 50 | omarchy osd -m "Hello"
|
||||
# omarchy:args=[-i|--icon <icon>] [-m|--message <text>] [-p|--progress <0-100>] [-d|--duration <ms>] [-f|--fit]
|
||||
# omarchy:examples=omarchy osd -i brightness -p 50 | omarchy osd -m "Hello" | omarchy osd -m "Done" --fit
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
@@ -12,6 +12,7 @@ progress=""
|
||||
progress_text=""
|
||||
max="100"
|
||||
duration=""
|
||||
fit=0
|
||||
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
@@ -19,6 +20,7 @@ while (($#)); do
|
||||
-m|--message) message="${2:-}"; shift 2 ;;
|
||||
-p|--progress) progress="${2:-}"; shift 2 ;;
|
||||
-d|--duration) duration="${2:-}"; shift 2 ;;
|
||||
-f|--fit) fit=1; shift ;;
|
||||
-h|--help) omarchy osd --help; exit 0 ;;
|
||||
*) echo "Unknown OSD option: $1" >&2; exit 1 ;;
|
||||
esac
|
||||
@@ -35,6 +37,7 @@ payload=$(jq -cn \
|
||||
--arg progressText "$progress_text" \
|
||||
--arg max "$max" \
|
||||
--arg duration "$duration" \
|
||||
'{icon:$icon,message:$message,value:$value,progressText:$progressText,max:$max,duration:$duration}')
|
||||
--argjson fit "$fit" \
|
||||
'{icon:$icon,message:$message,value:$value,progressText:$progressText,max:$max,duration:$duration,fit:$fit}')
|
||||
|
||||
omarchy-shell -q osd show "$payload"
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
nohup bash -c "sleep 2 && uwsm stop" >/dev/null 2>&1 &
|
||||
|
||||
omarchy-osd -i logout -m "Logging out…" -d 5000 --fit
|
||||
|
||||
# Now close all windows
|
||||
omarchy-hyprland-window-close-all
|
||||
sleep 1 # Allow apps like Chrome to shutdown correctly
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# scope cannot terminate it before it runs.
|
||||
systemd-run --user --collect --quiet --on-active="2s" --timer-property=AccuracySec=100ms systemctl reboot --no-wall || exit 1
|
||||
|
||||
omarchy-osd -i reboot -m "Rebooting…" -d 5000
|
||||
omarchy-osd -i reboot -m "Rebooting…" -d 5000 --fit
|
||||
|
||||
omarchy-state clear re*-required
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# scope cannot terminate it before it runs.
|
||||
systemd-run --user --collect --quiet --on-active="2s" --timer-property=AccuracySec=100ms systemctl poweroff --no-wall || exit 1
|
||||
|
||||
omarchy-osd -i shutdown -m "Shutting down…" -d 5000
|
||||
omarchy-osd -i shutdown -m "Shutting down…" -d 5000 --fit
|
||||
|
||||
omarchy-state clear re*-required
|
||||
|
||||
|
||||
@@ -17,18 +17,24 @@ Item {
|
||||
property int maxValue: 100
|
||||
property bool hasProgress: true
|
||||
property int duration: 1200
|
||||
property bool fit: false
|
||||
readonly property int cardWidth: Style.space(269)
|
||||
readonly property int mediaCardWidth: Math.round(cardWidth * 1.5)
|
||||
readonly property int messageWidth: Style.space(190)
|
||||
readonly property int mediaMessageWidth: messageWidth + mediaCardWidth - cardWidth
|
||||
readonly property bool mediaOsd: iconKey.indexOf("media") === 0 || iconKey.indexOf("player") === 0
|
||||
readonly property bool textOnlyOsd: root.fit && !root.hasProgress && !root.mediaOsd
|
||||
readonly property int fitMessageWidth: root.message === "" ? 0 : Math.round(messageMetrics.boundingRect.width) + Style.space(4)
|
||||
readonly property int fitCardWidth: root.message === "" || root.fitMessageWidth === 0
|
||||
? card.borderLeft + Style.space(16) + Style.space(28) + Style.space(16) + card.borderRight
|
||||
: card.borderLeft + Style.space(16) + Style.space(28) + Style.space(16) + root.fitMessageWidth + Style.space(16) + card.borderRight
|
||||
|
||||
function iconFor(name, percent) {
|
||||
return OsdModel.iconFor(name, percent)
|
||||
}
|
||||
|
||||
function show(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration) {
|
||||
var next = OsdModel.stateForShow(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration)
|
||||
function show(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration, rawFit) {
|
||||
var next = OsdModel.stateForShow(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration, rawFit)
|
||||
iconKey = next.iconKey
|
||||
maxValue = next.maxValue
|
||||
hasProgress = next.hasProgress
|
||||
@@ -36,6 +42,7 @@ Item {
|
||||
message = next.message
|
||||
icon = next.icon
|
||||
duration = next.duration
|
||||
fit = next.fit
|
||||
opened = true
|
||||
if (duration > 0) hideTimer.restart()
|
||||
else hideTimer.stop()
|
||||
@@ -44,7 +51,7 @@ Item {
|
||||
function open(payloadJson) {
|
||||
try {
|
||||
var p = JSON.parse(payloadJson || "{}")
|
||||
show(p.icon || "", p.message || "", p.value === undefined ? "" : String(p.value), p.max === undefined ? "100" : String(p.max), p.progressText || "", p.duration === undefined ? "1200" : String(p.duration))
|
||||
show(p.icon || "", p.message || "", p.value === undefined ? "" : String(p.value), p.max === undefined ? "100" : String(p.max), p.progressText || "", p.duration === undefined ? "1200" : String(p.duration), p.fit === undefined ? false : p.fit)
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -56,6 +63,14 @@ Item {
|
||||
onTriggered: root.opened = false
|
||||
}
|
||||
|
||||
TextMetrics {
|
||||
id: messageMetrics
|
||||
font.family: Style.font.family
|
||||
font.bold: true
|
||||
font.pixelSize: Style.font.title
|
||||
text: root.message
|
||||
}
|
||||
|
||||
IpcHandler {
|
||||
target: "osd"
|
||||
function show(payloadJson: string): string {
|
||||
@@ -82,7 +97,7 @@ Item {
|
||||
|
||||
BorderSurface {
|
||||
id: card
|
||||
width: root.mediaOsd ? root.mediaCardWidth : root.cardWidth
|
||||
width: root.textOnlyOsd ? root.fitCardWidth : (root.mediaOsd ? root.mediaCardWidth : root.cardWidth)
|
||||
height: Math.max(Style.space(68), Style.font.displayLarge + Style.spacing.panelGap)
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
@@ -121,16 +136,16 @@ Item {
|
||||
}
|
||||
}
|
||||
Text {
|
||||
width: root.hasProgress ? Style.space(41) : (root.mediaOsd ? root.mediaMessageWidth : root.messageWidth)
|
||||
width: root.textOnlyOsd ? root.fitMessageWidth : (root.hasProgress ? Style.space(41) : (root.mediaOsd ? root.mediaMessageWidth : root.messageWidth))
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
text: root.message
|
||||
font.family: Style.font.family
|
||||
font.bold: true
|
||||
font.pixelSize: Style.font.title
|
||||
color: Color.popups.text
|
||||
elide: Text.ElideRight
|
||||
elide: root.textOnlyOsd ? Text.ElideNone : Text.ElideRight
|
||||
maximumLineCount: 1
|
||||
clip: true
|
||||
clip: !root.textOnlyOsd
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ function iconFor(name, percent) {
|
||||
if (n === "touch" || n === "touchscreen") return ""
|
||||
if (n === "reboot" || n === "restart") return ""
|
||||
if (n === "shutdown" || n === "power" || n === "poweroff") return ""
|
||||
if (n === "logout" || n === "sign-out" || n === "leave") return ""
|
||||
if (n === "media" || n === "player") return ""
|
||||
if (n === "media-source" || n === "player-source") return ""
|
||||
if (n === "media-play" || n === "player-play") return ""
|
||||
@@ -29,13 +30,14 @@ function iconFor(name, percent) {
|
||||
return ""
|
||||
}
|
||||
|
||||
function stateForShow(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration) {
|
||||
function stateForShow(iconName, rawMessage, rawValue, rawMax, rawProgressText, rawDuration, rawFit) {
|
||||
var maxValue = Math.max(1, parseInt(rawMax || "100", 10))
|
||||
var parsedValue = parseInt(rawValue || "0", 10)
|
||||
var hasProgress = rawValue !== "" && !isNaN(parsedValue) && rawMessage === ""
|
||||
var value = hasProgress ? clamp(parsedValue, 0, maxValue) : 0
|
||||
var percent = hasProgress ? Math.round(value * 100 / maxValue) : -1
|
||||
var parsedDuration = parseInt(rawDuration || "1200", 10)
|
||||
var fit = rawFit === true || rawFit === 1 || rawFit === "1" || rawFit === "true"
|
||||
|
||||
return {
|
||||
iconKey: String(iconName || "").toLowerCase(),
|
||||
@@ -44,7 +46,8 @@ function stateForShow(iconName, rawMessage, rawValue, rawMax, rawProgressText, r
|
||||
value: value,
|
||||
message: String(rawMessage || (hasProgress ? (rawProgressText || percent + "%") : "")),
|
||||
icon: iconFor(iconName, percent),
|
||||
duration: isNaN(parsedDuration) ? 1200 : Math.max(0, parsedDuration)
|
||||
duration: isNaN(parsedDuration) ? 1200 : Math.max(0, parsedDuration),
|
||||
fit: fit
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ const osd = requireFromRoot('shell/plugins/osd/OsdModel.js')
|
||||
|
||||
assertEqual(osd.iconFor('', 0), osd.iconFor('muted', 50), 'osd falls back to muted icon at zero percent')
|
||||
assertEqual(osd.iconFor('volume-high', 1), osd.iconFor('', 100), 'osd maps high volume aliases')
|
||||
assertEqual(osd.iconFor('logout', 50), '', 'osd maps logout icon')
|
||||
assertEqual(osd.iconFor('custom-symbol', 50), 'custom-symbol', 'osd preserves unknown explicit icons')
|
||||
|
||||
assertDeepEqual(
|
||||
@@ -20,7 +21,8 @@ assertDeepEqual(
|
||||
value: 75,
|
||||
message: '75%',
|
||||
icon: osd.iconFor('volume', 75),
|
||||
duration: 800
|
||||
duration: 800,
|
||||
fit: false
|
||||
},
|
||||
'osd builds progress state'
|
||||
)
|
||||
@@ -34,8 +36,24 @@ assertDeepEqual(
|
||||
value: 0,
|
||||
message: 'Paused',
|
||||
icon: osd.iconFor('media-pause', -1),
|
||||
duration: 1200
|
||||
duration: 1200,
|
||||
fit: false
|
||||
},
|
||||
'osd builds message state'
|
||||
)
|
||||
|
||||
assertDeepEqual(
|
||||
osd.stateForShow('shutdown', 'Shutting down…', '', '100', '', '5000', '1'),
|
||||
{
|
||||
iconKey: 'shutdown',
|
||||
maxValue: 100,
|
||||
hasProgress: false,
|
||||
value: 0,
|
||||
message: 'Shutting down…',
|
||||
icon: osd.iconFor('shutdown', -1),
|
||||
duration: 5000,
|
||||
fit: true
|
||||
},
|
||||
'osd parses fit flag'
|
||||
)
|
||||
JS
|
||||
|
||||
Reference in New Issue
Block a user