elenapan/config/eww/scripts/manage
2024-12-14 01:41:23 +02:00

284 lines
8.5 KiB
Bash
Executable file

#!/usr/bin/env bash
# This script:
# - Changes the visibility of an eww widget, by also setting its state variable
# and accounting for a delay to allow the hiding animation to complete.
# Related: https://github.com/elkowar/eww/issues/196
# - Manages pre-show, post-show, pre-hide and post-hide commands.
# - Automatically enables or disables the respective sway binding mode when
# needed.
# - Automatically opens the dismiss-overlay widget when needed which can be used
# to achieve "close when clicking outside the widget" functionality.
# - Before showing the selected widget, it automatically hides all other widgets
# if needed.
#
# The script assumes that:
# - The widget state variable is in the format ${widget_name}-visible
# (e.g. variable `sidebar-visible` refers to the visibility state of the
# `sidebar` widget)
# Value: true/false
# - Windows that need overlays in secondary monitors declare an `is-secondary`
# argument.
#
# Usage:
# manage (show|hide|toggle|hide_all_except) widget_name
eww="$HOME/.config/eww/scripts"
# Fullscreen widgets will be shown in the primary monitor, and if needed,
# overlays will be shown in the following secondary monitors.
# You can find OUTPUT_NAME with `swaymsg -t get_outputs`
secondary_monitors=(
"HEADLESS-3"
"HDMI-A-1"
)
# Overlay widgets are spawned in all monitors (using the `is-secondary` argument
# to customize their appearance or functionality depending on monitor type)
declare -A overlay_widgets
overlay_widgets[powermenu]=true
# How long to wait for the revealer animation to complete. This should be
# match the revealer delay in the respective *.yuck files.
declare -A revealer_delay
revealer_delay[sidebar]=0.3
revealer_delay[powermenu]=0.3
revealer_delay[split-indicator]=0.3
revealer_delay[mode-indicator]=0.3
revealer_delay[microphone-indicator]=0.2
revealer_delay[networks]=0.3
revealer_delay[alarms]=0.3
# Only one of these should be visible at a time
mutually_exclusive_widgets=(
"sidebar"
"networks"
"alarms"
"powermenu"
)
# These widgets capture keys while they are visible (through sway binding
# modes). The convention currently is that for every such widget there exists a
# sway binding mode with the same name that starts with an underscore.
declare -A mode_widget
mode_widget[sidebar]="_sidebar"
mode_widget[networks]="_networks"
mode_widget[alarms]="_alarms"
mode_widget[powermenu]="_powermenu"
# Clicking out of these widgets should close them
dismiss_when_clicking_outside_widgets=(
"sidebar"
"networks"
"alarms"
)
declare -A pre_show_cmds
# shellcheck disable=2016
pre_show_cmds[networks]='[ "$(eww get networks-ping-loading)" = "true" ] || eww update networks-ping-result=ping'
declare -A post_show_cmds
post_show_cmds[networks]="($eww/networks.sh update_networks &)"
declare -A pre_hide_cmds
declare -A post_hide_cmds
post_hide_cmds[powermenu]='eww update powermenu-button-selected= screen-lock-input='
post_hide_cmds[alarms]='eww update alarms-state=select-type'
# ------------------------------------------------------------
cmd="$1"
widget_name="$2"
active_windows="$(eww active-windows 2>/dev/null)"
active_monitors="$(swaymsg -t get_outputs | jq -r '.[] | .name')"
# Helpers
_is_secondary_monitor() {
mon="$1"
grep -q "^${mon}$" <<< "$(printf '%s\n' "${secondary_monitors[@]}")"
}
_open() {
widget_name="$1"
if [[ -v overlay_widgets["$widget_name"] ]]; then
extra_args="--arg ${widget_name}:is-secondary=false"
for mon in ${active_monitors}; do
if _is_secondary_monitor "$mon"; then
extra_args+=" ${widget_name}:${widget_name}-${mon} --arg ${widget_name}-${mon}:screen=${mon} --arg ${widget_name}-${mon}:is-secondary=true"
fi
done
fi
# shellcheck disable=2086
eww open-many "$widget_name" $extra_args >/dev/null
}
_close() {
widget_name="$1"
if [[ -v overlay_widgets["$widget_name"] ]]; then
for mon in ${active_monitors}; do
if _is_secondary_monitor "$mon"; then
extra_args+=" ${widget_name}-${mon}"
fi
done
fi
# shellcheck disable=2086
eww close "$widget_name" $extra_args >/dev/null
}
_mode_enable() {
widget_name="$1"
if [[ -v mode_widget["$widget_name"] ]]; then
swaymsg -q mode "${mode_widget[$widget_name]}"
fi
}
_mode_disable() {
widget_name="$1"
if [[ -v mode_widget["$widget_name"] ]]; then
swaymsg -q mode default
fi
}
_pre_show() {
widget_name="$1"
if [[ -v pre_show_cmds["$widget_name"] ]]; then
sh <<< "${pre_show_cmds[$widget_name]}"
fi
if [[ ${mutually_exclusive_widgets[*]} =~ $widget_name ]]; then
hide_all_except "$widget_name"
fi
}
_post_show() {
widget_name="$1"
if [[ -v post_show_cmds["$widget_name"] ]]; then
sh <<< "${post_show_cmds[$widget_name]}"
fi
if [[ ${dismiss_when_clicking_outside_widgets[*]} =~ $widget_name ]]; then
secondary_overlays=''
# Show an overlay for all active secondary monitors
for mon in ${active_monitors}; do
if _is_secondary_monitor "$mon"; then
secondary_overlays+=" dismiss-overlay:dismiss-overlay-${mon} --arg dismiss-overlay-${mon}:screen=${mon}"
fi
done
# shellcheck disable=2086
eww open-many \
dismiss-overlay:dismiss-overlay \
$secondary_overlays \
--arg widget-to-dismiss="${widget_name}" \
>/dev/null;
fi
}
_pre_hide() {
widget_name="$1"
if [[ -v pre_hide_cmds["$widget_name"] ]]; then
sh <<< "${pre_hide_cmds[$widget_name]}"
fi
if [[ ${dismiss_when_clicking_outside_widgets[*]} =~ $widget_name ]]; then
secondary_overlays=''
# Hide the overlay for all active secondary monitors
for mon in ${active_monitors}; do
if _is_secondary_monitor "$mon"; then
secondary_overlays+=" dismiss-overlay-${mon}"
fi
done
# shellcheck disable=2086
eww close \
dismiss-overlay \
$secondary_overlays \
>/dev/null;
fi
}
_post_hide() {
widget_name="$1"
if [[ -v post_hide_cmds["$widget_name"] ]]; then
sh <<< "${post_hide_cmds[$widget_name]}"
fi
}
_show() {
widget_name="$1"
_pre_show "$widget_name"
_mode_enable "$widget_name"
_open "$widget_name"
eww update "${widget_name}"-visible=true
_post_show "$widget_name"
}
_hide() {
widget_name="$1"
delay="${revealer_delay[$widget_name]:-0}" # default delay is 0
_pre_hide "$widget_name"
_mode_disable "$widget_name"
eww update "${widget_name}"-visible=false
sleep "$delay"
_close "$widget_name"
_post_hide "$widget_name"
}
# Available commands
toggle() {
widget_name="$1"
if [ "$(eww get "${widget_name}-visible")" = "false" ]; then
_show "$widget_name"
else
_hide "$widget_name"
fi
}
show() {
if [ "$(eww get "${widget_name}-visible")" = "false" ]; then
_show "$widget_name"
fi
}
hide() {
if [ "$(eww get "${widget_name}-visible")" = "true" ]; then
_hide "$widget_name"
fi
}
hide_all_except() {
widget_except="$1"
for widget in "${mutually_exclusive_widgets[@]}"; do
if [[ ! "$widget" == "$widget_except" ]] && grep -e "^${widget}:" >/dev/null <<< "$active_windows"; then
delay="${revealer_delay[$widget]:-0}" # default delay is 0
# We do not call the `hide` helper because we do not necessarily
# want to mess with the sway modes.
# The widget to be activated will set its own mode through the
# `show` function if needed.
# If:
# - Widget to be hidden uses a mode AND
# - Widget to stay active does not use any mode
# -> We need to disable the mode.
# We need to do this outside the subshell running in the background
# to avoid a race condition causing the mode that was enabled in
# `show` to be disabled right after.
if [[ -v mode_widget["$widget"] && ! -v mode_widget["$widget_except"] ]]; then
swaymsg -q mode default
fi
# Run in the background to avoid delaying the next command while
# waiting for the animation
(
_pre_hide "$widget"
eww update "${widget}-visible"=false
sleep "$delay"
_close "$widget"
_post_hide "$widget"
)&
fi
done
}
# Run selected cmd
$cmd "$widget_name"