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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c8fb5be42e
commit
8ca61d6b8b
+130
-38
@@ -2,13 +2,32 @@
|
||||
|
||||
# omarchy:summary=Launch the fastfetch TUI that gives information about the current system.
|
||||
|
||||
# The float rule only sets a starting size: the exact fit depends on the terminal
|
||||
# font and the user's About logo, so once the terminal grid has settled we measure
|
||||
# the rendered content and resize the window to hug it with even padding all around.
|
||||
# Bash defers WINCH traps while read blocks, so poll for size changes and re-render
|
||||
# fastfetch at the new width whenever the window is resized.
|
||||
# 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"
|
||||
@@ -16,74 +35,147 @@ hypr_dispatch() {
|
||||
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() {
|
||||
# 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.
|
||||
[[ -f $HOME/.config/fastfetch/config.jsonc ]] && return 0
|
||||
custom_fastfetch_config && return 0
|
||||
|
||||
local rows cols address width height
|
||||
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])"')"
|
||||
[[ -f $LOGO_FILE && -n ${address:-} ]] || return 1
|
||||
(( cols > 0 && rows > 0 && width > 0 && height > 0 )) || return 1
|
||||
|
||||
# Mirror the logo block in the fastfetch config: 2 columns of padding on the
|
||||
# left of the logo, 6 between the logo and the modules, 2 rows above the logo.
|
||||
# wc -L measures display columns, so double-width glyphs count correctly.
|
||||
local logo_w logo_h
|
||||
logo_w=$(wc -L <"$LOGO_FILE")
|
||||
logo_h=$(( $(wc -l <"$LOGO_FILE") + 2 ))
|
||||
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" | wc -L)
|
||||
module_w=$(printf '%s' "$modules" | display_columns)
|
||||
module_h=$(printf '%s' "$modules" | wc -l)
|
||||
|
||||
# Two columns of right padding to match the logo's left padding, and one row
|
||||
# for the cursor line so the trailing break stays visible as bottom padding.
|
||||
# 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 > module_h ? logo_h : module_h) + 1 ))
|
||||
local target_w=$(( (target_c * width + cols / 2) / cols ))
|
||||
local target_h=$(( (target_r * height + rows / 2) / rows ))
|
||||
local target_r=$(( (logo_h + 2 > module_h ? logo_h + 2 : module_h) + 1 ))
|
||||
|
||||
# Skip resizes within one terminal cell so the fit doesn't churn.
|
||||
(( (target_w - width) ** 2 <= (width / cols) ** 2 && (target_h - height) ** 2 <= (height / rows) ** 2 )) && return
|
||||
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 float rule before measuring cells.
|
||||
previous=""
|
||||
for _ in {1..20}; do
|
||||
current=$(stty size)
|
||||
[[ $current == $previous ]] && break
|
||||
previous=$current
|
||||
sleep 0.05
|
||||
done
|
||||
# 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
|
||||
if [[ $fitted == false ]] && fit_window; then
|
||||
fitted=true
|
||||
# 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.
|
||||
[[ $(stat -c %Y "$LOGO_FILE" 2>/dev/null) == $logo_stamp ]] || fitted=false
|
||||
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
|
||||
|
||||
@@ -20,8 +20,9 @@ o.window({
|
||||
}, { tag = "+floating-window" })
|
||||
|
||||
-- The About fastfetch layout needs more columns than the standard float provides.
|
||||
-- This size only positions the first paint: omarchy-launch-about then measures
|
||||
-- the rendered content and fits the window to it exactly.
|
||||
-- This size only covers the first launch: omarchy-launch-about measures the
|
||||
-- rendered content, remembers the size that hugs it, and applies that as its own
|
||||
-- rule before every later launch.
|
||||
o.window("org.omarchy.about", { float = true })
|
||||
o.window("org.omarchy.about", { center = true })
|
||||
o.window("org.omarchy.about", { size = { 920, 480 } })
|
||||
|
||||
Reference in New Issue
Block a user