Keep the menu Medium on every input, and shrink a long version to fit #1968

Merged
nullifyshot merged 2 commits from fix/menu-size-medium-version-fit into main 2026-11-10 23:20:15 +00:00
nullifyshot commented 2026-11-10 22:10:45 +00:00 (Migrated from github.com)

The Insert menu got bigger whenever a controller opened it, and the build version at the top left disappeared after the Audio tab was added. This keeps the menu one size, Medium by default, and makes a long version shrink to fit instead of vanishing.

What changed

Menu size: no more Auto.

  • Auto drew the menu Large (1.3× and wider margins) in fullscreen at 1080 lines and up whenever a controller opened it, and Medium otherwise. The menu changed size depending on which input you used.
  • The row is now Small, Medium and Large, with Medium as the default. The size no longer depends on the input.
  • An options file with menu_size=0 (the old Auto) loads as Medium. Without that step, the row's clamp would turn it into Small.
  • auto_pad, auto_large and the second prebake pass for "the other size Auto might pick" are removed. ui::unit no longer takes the fullscreen and controller arguments.

The banner's version shrinks to fit.

  • Since #1270 added the Audio tab, there's less room beside the tabs. A source build's version (Hookshot v2.0.11-821-g7b81752) no longer fit, so the code dropped it completely.
  • It still tries the full text first, then drops (source …), as before.
  • If it's still too wide, at Small and Medium it now shrinks to the largest half-point size that fits, with 10 pt as the floor.
  • Large is unchanged: full size or not at all.
  • The shrinking is a small general helper, ui::fit_px in vk_ui.h. It takes the text's own width at a given size, so it works for any version string and any text.

Measured with the game's own menu font at 1920×1080. The widths scale with the window height, so 1440p and 4K give the same sizes:

Menu size Hookshot v2.0.11-821-g7b81752-dirty Hookshot v2.0.12 (release)
Small 15 pt (fits as is) 15 pt
Medium about 11 pt (was hidden) 15 pt, as before
Large hidden, as before hidden, as before
  • readme.md: the options table and the Menu size note.

Tested

  • In game on Windows 11, RTX 5080, 1920×1080 fullscreen: the version shows at full size on Small and shrunk on Medium.
  • Not checked in game: switching between controller and keyboard with the menu open, and loading an older options file with menu_size=0. Both are covered by the test below.
  • tests/test_menu_size.py (new, game-free) covers:
    • the sizes, the labels and the old Auto value
    • fit_px on a stand-in font: text that fits as is, the largest step that fits, nothing below the floor, Large never shrinking, and rounding on either side of the first guess
  • python -I -B tests/run_all.py: 296 passed, 1 failed, 49 skipped. The failure is test_precompile_compute_guard, which needs the MSVC target's libraries on this PC and doesn't touch these files. tests/test_stick_dead_zone.py, which compiles menu_init's options loop, passes. That's why the old-Auto check sits outside that loop.

Screenshots

Small, Medium and Large at 1920×1080 are in this comment.

🤖 Generated with Claude Code

The Insert menu got bigger whenever a controller opened it, and the build version at the top left disappeared after the Audio tab was added. This keeps the menu one size, Medium by default, and makes a long version shrink to fit instead of vanishing. ### What changed **Menu size: no more Auto.** - Auto drew the menu Large (1.3× and wider margins) in fullscreen at 1080 lines and up whenever a controller opened it, and Medium otherwise. The menu changed size depending on which input you used. - The row is now Small, Medium and Large, with Medium as the default. The size no longer depends on the input. - An options file with `menu_size=0` (the old Auto) loads as Medium. Without that step, the row's clamp would turn it into Small. - `auto_pad`, `auto_large` and the second prebake pass for "the other size Auto might pick" are removed. `ui::unit` no longer takes the fullscreen and controller arguments. **The banner's version shrinks to fit.** - Since #1270 added the Audio tab, there's less room beside the tabs. A source build's version (`Hookshot v2.0.11-821-g7b81752`) no longer fit, so the code dropped it completely. - It still tries the full text first, then drops ` (source …)`, as before. - If it's still too wide, at Small and Medium it now shrinks to the largest half-point size that fits, with 10 pt as the floor. - Large is unchanged: full size or not at all. - The shrinking is a small general helper, `ui::fit_px` in `vk_ui.h`. It takes the text's own width at a given size, so it works for any version string and any text. Measured with the game's own menu font at 1920×1080. The widths scale with the window height, so 1440p and 4K give the same sizes: | Menu size | `Hookshot v2.0.11-821-g7b81752-dirty` | `Hookshot v2.0.12` (release) | |---|---|---| | Small | 15 pt (fits as is) | 15 pt | | Medium | about 11 pt (was hidden) | 15 pt, as before | | Large | hidden, as before | hidden, as before | - `readme.md`: the options table and the Menu size note. ### Tested - In game on Windows 11, RTX 5080, 1920×1080 fullscreen: the version shows at full size on Small and shrunk on Medium. - Not checked in game: switching between controller and keyboard with the menu open, and loading an older options file with `menu_size=0`. Both are covered by the test below. - `tests/test_menu_size.py` (new, game-free) covers: - the sizes, the labels and the old Auto value - `fit_px` on a stand-in font: text that fits as is, the largest step that fits, nothing below the floor, Large never shrinking, and rounding on either side of the first guess - `python -I -B tests/run_all.py`: 296 passed, 1 failed, 49 skipped. The failure is `test_precompile_compute_guard`, which needs the MSVC target's libraries on this PC and doesn't touch these files. `tests/test_stick_dead_zone.py`, which compiles `menu_init`'s options loop, passes. That's why the old-Auto check sits outside that loop. ### Screenshots Small, Medium and Large at 1920×1080 are in [this comment](https://git.magicbean.dev/MagicBeanDev/Hookshot/pulls/1968). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
nullifyshot commented 2026-11-10 22:15:17 +00:00 (Migrated from github.com)

Screenshots of the Gameplay tab at 1920×1080. They were taken from this branch's build in a headless run (HOS_HEADLESS=1 RX_VK_SHOT_MENU=1, keys from RX_VK_MENU_SCRIPT), on an RTX 5080. The version is a source build's, Hookshot v2.0.11-821-g7b81752-dirty.

Small: the version fits at its normal 15 pt.

Menu size Small

Medium (the default now): the same text, shrunk to fit beside the tabs. Before this PR it wasn't drawn at all at this size.

Menu size Medium

Large: unchanged. The version isn't drawn, because it doesn't fit at full size.

Menu size Large

Screenshots of the Gameplay tab at 1920×1080. They were taken from this branch's build in a headless run (`HOS_HEADLESS=1 RX_VK_SHOT_MENU=1`, keys from `RX_VK_MENU_SCRIPT`), on an RTX 5080. The version is a source build's, `Hookshot v2.0.11-821-g7b81752-dirty`. **Small:** the version fits at its normal 15 pt. ![Menu size Small](https://raw.githubusercontent.com/breakthetargets/Hookshot/a582df4b8c7760392b60e8e71f0611a5a024d526/menu-small.png) **Medium (the default now):** the same text, shrunk to fit beside the tabs. Before this PR it wasn't drawn at all at this size. ![Menu size Medium](https://raw.githubusercontent.com/breakthetargets/Hookshot/a582df4b8c7760392b60e8e71f0611a5a024d526/menu-medium.png) **Large:** unchanged. The version isn't drawn, because it doesn't fit at full size. ![Menu size Large](https://raw.githubusercontent.com/breakthetargets/Hookshot/a582df4b8c7760392b60e8e71f0611a5a024d526/menu-large.png)
hckrman101 (Migrated from github.com) approved these changes 2026-11-10 22:21:45 +00:00
hckrman101 (Migrated from github.com) left a comment

Nice cleanup: one size on every input is easier to reason about than Auto, and the version no longer vanishes. I read it through and checked the things below; no blockers.

  • Tests that read vk_menu.cpp: I searched every tests/*.py on main for what this removes or reshapes (auto_pad, auto_large, tx_menu_size, prebaked, prebake, ui::unit, the menu_size row). Nothing cuts those out. test_fps_overlay_order.py stubs prebake and prebake_needed itself, and test_menu_keys.py only declares its own pad_last. The tests that cut out menu_init (test_stick_dead_zone.py, test_menu_restart.py) cut the options loop or just look for restart_init();, and menu_auto sits outside that loop, so they are fine. The Windows llvm-mingw build is green. The "Python source checks" jobs were still pending when I looked, so I can't confirm the Linux/Windows run of the test suite.
  • API version: no bump needed. Options lives in vk_menu.h, not vk_api.h, and ui::unit has no caller outside vk_menu.cpp.
  • fit_px division and sign: room <= 0, w <= 0 and step <= 0 return 0 before the divide. min_px is 10 * s (or px at Large), always positive, and the result is either >= min_px or 0, so the scale can't go negative. Both loops end: the first is bounded by px, the second drops by step until f < min_px. At Large the first guess is below px, so it ends up 0 as intended (not drawn).
  • menu_size=0 migration: the row's clamp to 1 runs first and menu_auto then sets 2 after the loop, so an old Auto file loads as Medium. unit() also treats anything outside 1..3 as Medium.

Things to decide or look at:

  • Behaviour change for TV users: anyone who relied on Auto giving Large with a controller in fullscreen now gets Medium until they pick Large. The PR says so; that call is NotRexed's (there are no review comments or maintainer replies on this PR yet, only the author's screenshots comment, which matches the diff).
  • Test compiler (nit, probably worth fixing): tests/test_menu_size.py takes CXX from the environment and falls back to g++, with no RUN_ALL_ARGS. The neighbouring tests declare RUN_ALL_ARGS = ["--cxx", "{cxx}"] and take --cxx, so run_all.py finds the compiler and skips cleanly if there is none. On a machine without g++ (llvm-mingw, for one) this one fails instead of skipping.
  • Nit: the shrunk sizes (half-point steps below 15 pt) aren't in the prebake points list, so the first open at a given width may bake a few glyph sizes. The prebake dry run draws the banner once, so it probably covers them; I haven't measured it.

The PR body says test_precompile_compute_guard fails on the author's PC (needs MSVC libraries); I did not check that against main's CI.

Nice cleanup: one size on every input is easier to reason about than Auto, and the version no longer vanishes. I read it through and checked the things below; no blockers. - **Tests that read `vk_menu.cpp`:** I searched every `tests/*.py` on main for what this removes or reshapes (`auto_pad`, `auto_large`, `tx_menu_size`, `prebaked`, `prebake`, `ui::unit`, the `menu_size` row). Nothing cuts those out. `test_fps_overlay_order.py` stubs `prebake` and `prebake_needed` itself, and `test_menu_keys.py` only declares its own `pad_last`. The tests that cut out `menu_init` (`test_stick_dead_zone.py`, `test_menu_restart.py`) cut the options loop or just look for `restart_init();`, and `menu_auto` sits outside that loop, so they are fine. The Windows llvm-mingw build is green. The "Python source checks" jobs were still pending when I looked, so I can't confirm the Linux/Windows run of the test suite. - **API version:** no bump needed. `Options` lives in `vk_menu.h`, not `vk_api.h`, and `ui::unit` has no caller outside `vk_menu.cpp`. - **`fit_px` division and sign:** `room <= 0`, `w <= 0` and `step <= 0` return 0 before the divide. `min_px` is `10 * s` (or `px` at Large), always positive, and the result is either `>= min_px` or 0, so the scale can't go negative. Both loops end: the first is bounded by `px`, the second drops by `step` until `f < min_px`. At Large the first guess is below `px`, so it ends up 0 as intended (not drawn). - **`menu_size=0` migration:** the row's clamp to 1 runs first and `menu_auto` then sets 2 after the loop, so an old Auto file loads as Medium. `unit()` also treats anything outside 1..3 as Medium. Things to decide or look at: - **Behaviour change for TV users:** anyone who relied on Auto giving Large with a controller in fullscreen now gets Medium until they pick Large. The PR says so; that call is NotRexed's (there are no review comments or maintainer replies on this PR yet, only the author's screenshots comment, which matches the diff). - **Test compiler (nit, probably worth fixing):** `tests/test_menu_size.py` takes `CXX` from the environment and falls back to `g++`, with no `RUN_ALL_ARGS`. The neighbouring tests declare `RUN_ALL_ARGS = ["--cxx", "{cxx}"]` and take `--cxx`, so `run_all.py` finds the compiler and skips cleanly if there is none. On a machine without `g++` (llvm-mingw, for one) this one fails instead of skipping. - **Nit:** the shrunk sizes (half-point steps below 15 pt) aren't in the prebake `points` list, so the first open at a given width may bake a few glyph sizes. The prebake dry run draws the banner once, so it probably covers them; I haven't measured it. The PR body says `test_precompile_compute_guard` fails on the author's PC (needs MSVC libraries); I did not check that against main's CI.
@ -0,0 +1,103 @@
"""Menu size is Small, Medium or Large, Medium by default, whatever the input:
hckrman101 (Migrated from github.com) commented 2026-11-10 22:21:45 +00:00

Nit: other compile-based tests declare RUN_ALL_ARGS = ["--cxx", "{cxx}"] near the top and take --cxx (see test_menu_keys.py), so run_all.py finds a compiler or skips. Here a missing g++ is a failure.

Nit: other compile-based tests declare `RUN_ALL_ARGS = ["--cxx", "{cxx}"]` near the top and take `--cxx` (see `test_menu_keys.py`), so `run_all.py` finds a compiler or skips. Here a missing `g++` is a failure.
nullifyshot (Migrated from github.com) reviewed 2026-11-10 22:26:29 +00:00
@ -0,0 +1,103 @@
"""Menu size is Small, Medium or Large, Medium by default, whatever the input:
nullifyshot (Migrated from github.com) commented 2026-11-10 22:26:29 +00:00

Fixed in 9192909: RUN_ALL_ARGS = ["--cxx", "{cxx}"] and --cxx, as in test_menu_keys.py. With no C++ compiler on PATH, run_all.py now skips it ("not found here: cxx"). With llvm-mingw's clang++ it passes.

Fixed in 9192909: `RUN_ALL_ARGS = ["--cxx", "{cxx}"]` and `--cxx`, as in `test_menu_keys.py`. With no C++ compiler on PATH, `run_all.py` now skips it ("not found here: cxx"). With llvm-mingw's `clang++` it passes.
nullifyshot commented 2026-11-10 22:26:44 +00:00 (Migrated from github.com)

Thanks for the review, hckrman101.

  • Test compiler: fixed in 9192909 (see the reply on the test). The full suite on Windows with llvm-mingw gives 295 passed, 2 failed. The failures are test_precompile_compute_guard, which needs the MSVC target's libraries on this PC, and test_fpx_sticky. That one passed on the run before and passes alone (run_all.py test_fpx_sticky), so it looks flaky when 24 run at once here. Neither touches these files.
  • Glyph sizes: the banner is part of what prebake's dry run draws, and fit_px only depends on the window's width and height, the Menu size and the version string. So the shrunk size is baked before the first open, at the same unit and width that prebake_needed tracks. I haven't timed the first open separately.
  • TV users: agreed, that's NotRexed's call. Anyone who wants the old controller size picks Large once.
Thanks for the review, hckrman101. - **Test compiler:** fixed in 9192909 (see the reply on the test). The full suite on Windows with llvm-mingw gives 295 passed, 2 failed. The failures are `test_precompile_compute_guard`, which needs the MSVC target's libraries on this PC, and `test_fpx_sticky`. That one passed on the run before and passes alone (`run_all.py test_fpx_sticky`), so it looks flaky when 24 run at once here. Neither touches these files. - **Glyph sizes:** the banner is part of what prebake's dry run draws, and `fit_px` only depends on the window's width and height, the Menu size and the version string. So the shrunk size is baked before the first open, at the same unit and width that `prebake_needed` tracks. I haven't timed the first open separately. - **TV users:** agreed, that's NotRexed's call. Anyone who wants the old controller size picks Large once.
Commenting is not possible because the repository is archived.
No description provided.