Files
omarchycn/bin/omarchy-launch-about
T
8ca61d6b8b Open the About window at the size it last settled on (#6770)
* Measure the About layout in UTF-8 so its window is not fitted too narrow

wc -L only counts display columns in a UTF-8 locale. A session that never
set one leaves it counting the box-drawing and Nerd Font glyphs the About
layout is built from as nothing, which measured the content 21 columns
narrower than it renders and fitted the window to clip it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* Open the About window at the size it last settled on

The window used to map at the float rule's starting size, paint, and only
then measure itself and resize, so every open flashed one window size and
reflowed into another. The size that hugs the content can only be measured
from inside the terminal, so remember it and apply it as a window rule
before the terminal is spawned: the window now maps at its final size and
never moves. A rebranded logo or a new font falls back to the float rule
for one launch, refits, and is remembered from then on.

The fit itself now moves the window by the cells it is off by, rather than
scaling it to the grid, which multiplied up the terminal's padding along
with them and left the fit a column or two short. It accepts a cell of
slack instead of chasing an exact grid, since a window lands where the
terminal's cell boundaries put it, not where it was asked to.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-13 11:32:20 +02:00

182 lines
6.7 KiB
Bash
Executable File

#!/bin/bash
# omarchy:summary=Launch the fastfetch TUI that gives information about the current system.
# The size that hugs the About content depends on the terminal font and the user's
# logo, so it can only be measured from inside the terminal. We remember the size
# that fit and apply it as a window rule before launching, so the window opens at
# it instead of resizing after the first paint. Bash defers WINCH traps while read
# blocks, so poll for size changes and re-render fastfetch whenever it is resized.
LOGO_FILE="$HOME/.config/omarchy/branding/about.txt"
FIT_FILE="$HOME/.local/state/omarchy/windows/about.fit"
# A user-level fastfetch config can relocate or restyle the logo in ways this
# measurement cannot see, so leave sizing to the float rule in that case.
custom_fastfetch_config() {
[[ -f $HOME/.config/fastfetch/config.jsonc ]]
}
# wc -L counts display columns only in a UTF-8 locale. A session that never set
# one counts every box-drawing and Nerd Font glyph in the About layout as
# nothing, which measures the content narrower than it renders.
display_columns() {
LC_ALL=C.UTF-8 wc -L
}
logo_dimensions() {
[[ -f $LOGO_FILE ]] || return 1
printf '%s %s' "$(display_columns <"$LOGO_FILE")" "$(wc -l <"$LOGO_FILE")"
}
hypr_dispatch() {
local lua="$1"
shift
hyprctl dispatch "$lua" >/dev/null 2>&1 || hyprctl dispatch "$@" >/dev/null
}
remember_fit() {
local directory tmp
directory=$(dirname "$FIT_FILE")
mkdir -p "$directory"
tmp=$(mktemp "$directory/.about.fit.XXXXXX")
printf '%s %s %s %s\n' "$1" "$2" "$3" "$4" >"$tmp"
mv "$tmp" "$FIT_FILE"
}
# Replaces the rule from the last launch so a remembered size never outlives the
# fit it came from. Called without one it only clears, leaving the float rule's
# starting size — where a Hyprland without the Lua API stays too.
apply_size_rule() {
local rule=""
(( $# == 2 )) && rule="omarchy_about_size_rule = hl.window_rule({ match = { class = \"org.omarchy.about\" }, size = { $1, $2 } })"
hyprctl eval "if omarchy_about_size_rule then omarchy_about_size_rule:set_enabled(false) end; omarchy_about_size_rule = nil; $rule" >/dev/null 2>&1
}
# Sized before the terminal is spawned, so the window maps at its final size.
presize_window() {
local logo_w logo_h fit_logo_w fit_logo_h fit_w fit_h
if ! custom_fastfetch_config && [[ -r $FIT_FILE ]]; then
read -r logo_w logo_h <<<"$(logo_dimensions)"
read -r fit_logo_w fit_logo_h fit_w fit_h <"$FIT_FILE"
# A different logo needs a different window, so leave that launch to the
# float rule and let the fit measure the new size.
if [[ -n ${logo_w:-} && $logo_w == "$fit_logo_w" && $logo_h == "$fit_logo_h" ]] &&
[[ $fit_w =~ ^[0-9]+$ && $fit_h =~ ^[0-9]+$ ]]; then
apply_size_rule "$fit_w" "$fit_h"
return
fi
fi
apply_size_rule
}
# Hyprland animates a resize and the terminal reflows to every step of it, so
# wait for the grid to hold still before measuring it.
settle_grid() {
local current previous="" held=0
for _ in {1..20}; do
current=$(stty size)
if [[ $current == $previous ]]; then
(( ++held == 3 )) && break
else
held=0
previous=$current
fi
sleep 0.05
done
}
fit_window() {
custom_fastfetch_config && return 0
local logo_w logo_h
read -r logo_w logo_h <<<"$(logo_dimensions)"
[[ -n ${logo_w:-} ]] || return 1
# The guard character keeps command substitution from eating the trailing
# break line, which provides the bottom padding row.
local modules module_w module_h
modules=$(fastfetch --logo none | sed 's/\x1b\[[0-9;?]*[a-zA-Z]//g'; printf X)
modules=${modules%X}
module_w=$(printf '%s' "$modules" | display_columns)
module_h=$(printf '%s' "$modules" | wc -l)
# Mirror the logo block in the fastfetch config: 2 columns of padding left of
# the logo, 6 between logo and modules, 2 rows above it. Then 2 columns of
# right padding to match, and a row for the cursor so the trailing break shows.
local target_c=$(( 2 + logo_w + 6 + module_w + 2 ))
local target_r=$(( (logo_h + 2 > module_h ? logo_h + 2 : module_h) + 1 ))
local nudges=0 rows cols address width height shift_w shift_h target_w target_h
while :; do
read -r rows cols <<<"$(stty size)"
read -r address width height <<<"$(hyprctl clients -j | jq -r '.[] | select(.class == "org.omarchy.about") | "\(.address) \(.size[0]) \(.size[1])"')"
[[ -n ${address:-} ]] || return 1
(( cols > 0 && rows > 0 && width > 0 && height > 0 )) || return 1
# A window has to land on the terminal's cell boundaries, so take a cell of
# slack over chasing an exact grid, and remember where it came to rest.
if (( cols >= target_c && cols <= target_c + 1 && rows >= target_r && rows <= target_r + 1 )); then
remember_fit "$logo_w" "$logo_h" "$width" "$height"
return 0
fi
# Two nudges is the budget, and each one is measured before the next is spent.
(( ++nudges <= 2 )) || return 1
# Move by the cells the window is off by, rather than scaling it to the grid,
# which would multiply up the terminal's padding along with them. Dividing a
# window that carries that padding still leaves the cell a touch generous, so
# round the move away from the grid that would clip.
shift_w=$(( (target_c - cols) * width ))
shift_h=$(( (target_r - rows) * height ))
target_w=$(( width + (shift_w >= 0 ? (shift_w + cols - 1) / cols : shift_w / cols) ))
target_h=$(( height + (shift_h >= 0 ? (shift_h + rows - 1) / rows : shift_h / rows) ))
hypr_dispatch "hl.dsp.window.resize({ window = \"address:$address\", x = $target_w, y = $target_h })" resizewindowpixel "exact $target_w $target_h,address:$address"
hypr_dispatch "hl.dsp.window.center({ window = \"address:$address\" })" centerwindow
settle_grid
done
return 1
}
if [[ ${1:-} == "--render" ]]; then
printf '\e[?25l'
# Give the compositor a moment to apply the window rules before measuring cells.
settle_grid
fitted=false
passes=0
while :; do
size=$(stty size)
logo_stamp=$(stat -c %Y "$LOGO_FILE" 2>/dev/null)
clear
fastfetch
# A second pass picks up a fit that could not measure the window the first
# time. Beyond that, a window that will not settle would be fitted again on
# every repaint.
if [[ $fitted == false ]] && (( ++passes <= 2 )); then
fit_window && fitted=true
fi
while [[ $(stty size) == $size && $(stat -c %Y "$LOGO_FILE" 2>/dev/null) == $logo_stamp ]]; do
read -t 0.5 -n 1 -s && exit
(( $? > 128 )) || exit
done
# A rebranded logo changes the content dimensions, so measure again.
if [[ $(stat -c %Y "$LOGO_FILE" 2>/dev/null) != $logo_stamp ]]; then
fitted=false
passes=0
fi
done
fi
presize_window
exec omarchy-launch-or-focus-tui --app-id=org.omarchy.about omarchy-launch-about --render