WIP: feat(menu): a Display tab for the screen, HDR and overlay rows #2178

Draft
pmwork-personal wants to merge 13 commits from pmwork-personal/feat/menu-display-tab into main
pmwork-personal commented 2026-11-11 06:09:09 +00:00 (Migrated from github.com)

The Graphics tab holds both how the picture is made and how it reaches the screen. This gives the screen's rows a tab of
their own, so Graphics is about the picture's quality and cost alone. It is step 2 of the menu organization in #2177
(sections on the Graphics tab), and needs it first.

What changed

  • A Display tab, second in the strip:
    • Screen: Fullscreen, V-Sync, Window scaling filter, Ultrawide, Ultrawide menus (from Graphics)
    • HDR: HDR, HDR paper white, HDR peak brightness, HDR contrast (#2342) (from Graphics; the levels still only with
      HDR on). test_hdr_encode.py finds HDR contrast on Display.
    • Overlays: FPS counter, FPS counter position (#2181), Menu size (from Gameplay)
  • Graphics keeps the Quality preset, Resolution and upscaling, Frame generation and latency, Detail, and GPU and memory.
  • Moves only: every row's definition is unchanged apart from its tab, so options files carry over and #1864's tables
    (keyed by the English text) still find every row. New strings: "Display", "Overlays".
  • The tab strip fits the window. The menu is scaled by the window's height alone, so the strip is as wide in points
    everywhere: six tabs at Large were wider than a 16:10 screen (on the Steam Deck's 1280x800 the Q and E keys fell past
    its edges) and a 4:3 one (already true there with five). New tab_strip_fit: where the titles and their gaps are wider
    than the room between the safe margins and the tab keys, the gaps narrow first, down to 16 points, then the titles
    shrink in proportion; where they fit, nothing changes (every 16:9 screen at Small and Medium, the Deck at Medium).
  • enum MenuTab: TAB_DISPLAY = 1, the tabs after it one up; the Motion and Controls pages follow TAB_COUNT as before.
    scrolled_to starts every slot at -1 (the Controls page's was left at 0 since the Audio tab), with a static_assert so
    the list grows with the slots.
  • The restart line shows on Display too (Ultrawide takes a restart).
  • Main's tuning bar (#2378) names a row's tab from the same list (tab_titles, now global): it says Display for these rows.
  • The control socket (#926): bridge_fill's tab names have display in its place, so option_get and option_set tell the
    moved rows' tab as display (they were graphics or gameplay). A script that looks for those rows by tab needs the new
    name; by key (fps_counter, vsync, ...) nothing changes.
  • readme.md: the Display tab's rows under Display in the option table; the sections sentence and the restart note name
    it; the tab list names all six (it still said four, Graphics, Gameplay, Controls and Debug).
  • Tests: test_vsync_row.py finds V-Sync on Display; test_menu_sections.py checks Display's sections and that it is the
    strip's second tab (the shape row by its value, so it holds whether it is named Ultrawide or Aspect ratio); new
    test_tab_strip_fit.py runs the fit over 16:9, 16:10, the Deck, 4:3 and a narrow window at each menu size.

With #2119 (the Aspect ratio row, was Ultrawide): the two touch the same row and its readme line. Whichever lands second
takes the other's: the row reads Aspect ratio, with #2119's help, on Display ▸ Screen. Checked with a merge of the two
branches: those two places conflict, nothing else.

Testing

  • tests/run_all.py on Windows 11 (llvm-mingw clang, 8 at once) after merging main at bd699925 (with #2118's Frame generation by row, #2342's HDR contrast, and #1859's
    VRAM budget in GPU and memory): 383 passed, 31 skipped, 1 failed: test_vram_budget_row.py, which fails the same on
    untouched main on this PC. A build of the branch compiles (the tab names' static_assert included).
  • The menu's own checks on a local merge with #2119 too: test_menu_sections.py, test_tab_strip_fit.py,
    test_vsync_row.py, and #2119's test_menu_restart.py, test_stick_dead_zone.py, test_audio_output_menu.py pass.
  • A build of this branch (RTX 5090), headless with scripted menu keys: the Display tab at 1920x1080 at Small, Medium and
    Large, and at Large on 1280x800 (the Deck's size), 1920x1200 and 1440x1080. The tab opens on Fullscreen (past the
    Screen heading); Up from it wraps to Menu size; the strip and its Q and E keys stay inside the screen at every size.
    The build label at the banner's left: a release's ("Hookshot v2.2.0-rc.1") shows at Small and Medium; at Large it is
    left out, as it already is with five tabs (it shows at its size or not at all). A source build's longer label
    ("v2.1.0-rc.3-45-g…-dirty") shows at Small only.

Not tested: a pad and the mouse in a window (the tab keys and clicks are unchanged code), AMD and Intel, Linux, a real
Steam Deck (its size was tested, not the device).

Screenshots

The Display tab and Graphics after it, 1920x1080, Medium:

Display tab: Screen, HDR and Overlays

Graphics tab: Quality preset, Resolution and upscaling, Frame generation and latency

The strip fitted at Large on the Steam Deck's 1280x800 (the gaps narrower) and on 1440x1080 (the titles a little smaller too):

Display tab at Large, 1280x800

Display tab at Large, 1440x1080

Preview, not this PR: the Display tab with #2119 merged in as well (a local merge of the two branches), the shape row
as Aspect ratio at its new default:

Display tab with #2119: Aspect ratio, Monitor's shape

All from headless runs of builds of this branch (HOS_HEADLESS=1, RX_VK_SHOT_MENU=1, the keys from
RX_VK_MENU_SCRIPT), RTX 5090, built with HOOKSHOT_VERSION=v2.2.0-rc.1 for a release's build label.

🤖 Generated with Claude Code

The Graphics tab holds both how the picture is made and how it reaches the screen. This gives the screen's rows a tab of their own, so Graphics is about the picture's quality and cost alone. It is step 2 of the menu organization in #2177 (sections on the Graphics tab), and needs it first. **What changed** - **A Display tab**, second in the strip: - **Screen:** Fullscreen, V-Sync, Window scaling filter, Ultrawide, Ultrawide menus (from Graphics) - **HDR:** HDR, HDR paper white, HDR peak brightness, HDR contrast (#2342) (from Graphics; the levels still only with HDR on). `test_hdr_encode.py` finds HDR contrast on Display. - **Overlays:** FPS counter, FPS counter position (#2181), Menu size (from Gameplay) - **Graphics** keeps the Quality preset, Resolution and upscaling, Frame generation and latency, Detail, and GPU and memory. - Moves only: every row's definition is unchanged apart from its tab, so options files carry over and #1864's tables (keyed by the English text) still find every row. New strings: "Display", "Overlays". - **The tab strip fits the window.** The menu is scaled by the window's height alone, so the strip is as wide in points everywhere: six tabs at Large were wider than a 16:10 screen (on the Steam Deck's 1280x800 the Q and E keys fell past its edges) and a 4:3 one (already true there with five). New `tab_strip_fit`: where the titles and their gaps are wider than the room between the safe margins and the tab keys, the gaps narrow first, down to 16 points, then the titles shrink in proportion; where they fit, nothing changes (every 16:9 screen at Small and Medium, the Deck at Medium). - `enum MenuTab`: `TAB_DISPLAY = 1`, the tabs after it one up; the Motion and Controls pages follow `TAB_COUNT` as before. `scrolled_to` starts every slot at -1 (the Controls page's was left at 0 since the Audio tab), with a `static_assert` so the list grows with the slots. - The restart line shows on Display too (Ultrawide takes a restart). - Main's tuning bar (#2378) names a row's tab from the same list (`tab_titles`, now global): it says Display for these rows. - The control socket (#926): `bridge_fill`'s tab names have `display` in its place, so `option_get` and `option_set` tell the moved rows' tab as `display` (they were `graphics` or `gameplay`). A script that looks for those rows by tab needs the new name; by key (`fps_counter`, `vsync`, ...) nothing changes. - `readme.md`: the Display tab's rows under Display in the option table; the sections sentence and the restart note name it; the tab list names all six (it still said four, Graphics, Gameplay, Controls and Debug). - Tests: `test_vsync_row.py` finds V-Sync on Display; `test_menu_sections.py` checks Display's sections and that it is the strip's second tab (the shape row by its value, so it holds whether it is named Ultrawide or Aspect ratio); new `test_tab_strip_fit.py` runs the fit over 16:9, 16:10, the Deck, 4:3 and a narrow window at each menu size. **With #2119** (the Aspect ratio row, was Ultrawide): the two touch the same row and its readme line. Whichever lands second takes the other's: the row reads Aspect ratio, with #2119's help, on Display ▸ Screen. Checked with a merge of the two branches: those two places conflict, nothing else. **Testing** - `tests/run_all.py` on Windows 11 (llvm-mingw clang, 8 at once) after merging main at bd699925 (with #2118's Frame generation by row, #2342's HDR contrast, and #1859's VRAM budget in GPU and memory): 383 passed, 31 skipped, 1 failed: `test_vram_budget_row.py`, which fails the same on untouched main on this PC. A build of the branch compiles (the tab names' `static_assert` included). - The menu's own checks on a local merge with #2119 too: `test_menu_sections.py`, `test_tab_strip_fit.py`, `test_vsync_row.py`, and #2119's `test_menu_restart.py`, `test_stick_dead_zone.py`, `test_audio_output_menu.py` pass. - A build of this branch (RTX 5090), headless with scripted menu keys: the Display tab at 1920x1080 at Small, Medium and Large, and at Large on 1280x800 (the Deck's size), 1920x1200 and 1440x1080. The tab opens on Fullscreen (past the Screen heading); Up from it wraps to Menu size; the strip and its Q and E keys stay inside the screen at every size. The build label at the banner's left: a release's ("Hookshot v2.2.0-rc.1") shows at Small and Medium; at Large it is left out, as it already is with five tabs (it shows at its size or not at all). A source build's longer label ("v2.1.0-rc.3-45-g…-dirty") shows at Small only. **Not tested**: a pad and the mouse in a window (the tab keys and clicks are unchanged code), AMD and Intel, Linux, a real Steam Deck (its size was tested, not the device). **Screenshots** The Display tab and Graphics after it, 1920x1080, Medium: ![Display tab: Screen, HDR and Overlays](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-display/display-tab-medium.webp) ![Graphics tab: Quality preset, Resolution and upscaling, Frame generation and latency](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-display/graphics-tab-medium.webp) The strip fitted at Large on the Steam Deck's 1280x800 (the gaps narrower) and on 1440x1080 (the titles a little smaller too): ![Display tab at Large, 1280x800](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-display/display-tab-1280x800-large.webp) ![Display tab at Large, 1440x1080](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-display/display-tab-1440x1080-large.webp) **Preview, not this PR:** the Display tab with #2119 merged in as well (a local merge of the two branches), the shape row as Aspect ratio at its new default: ![Display tab with #2119: Aspect ratio, Monitor's shape](https://raw.githubusercontent.com/pmwork-personal/Hookshot-pr/pr-screenshots/menu-display/display-tab-with-2119-preview.webp) _All from headless runs of builds of this branch (`HOS_HEADLESS=1`, `RX_VK_SHOT_MENU=1`, the keys from `RX_VK_MENU_SCRIPT`), RTX 5090, built with `HOOKSHOT_VERSION=v2.2.0-rc.1` for a release's build label._ 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Commenting is not possible because the repository is archived.
No description provided.