feat(menu): add compact TV-style tuning bar for live setting tweaks #2378
No reviewers
Labels
No labels
accessibility
autorelease: pending
autorelease: tagged
bug
dependencies
documentation
duplicate
enhancement
github_actions
good first issue
help wanted
invalid
question
testing wanted
wontfix
No milestone
No assignees
1 participant
Due date
No due date set.
Dependencies
No dependencies set
Reference
MagicBeanDev/Hookshot!2378
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/menu-tuning-bar"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why is this PR needed?
The full settings menu covers ~80% of the screen, making it difficult to gauge real-time visual changes (sharpness, resolution scaling, upscaler presets) during live gameplay. This PR introduces a compact TV-style tuning bar at the bottom of the screen (~10% height) so settings can be adjusted with over 90% of the game scene visible. It can be invoked from the normal settings menu or as a special keybind directly from the game.
What changes?
Compact Tuning Bar (draw_tune_bar): Renders a sleek bottom card showing the current tab, active setting row, value editor, and navigation hints without full-screen dimming.
Two Entry / Exit Paths:
Option A (In-menu toggle): Pressing the configured modifier key (Right Shift by default) or controller X/Square switches smoothly between the full menu and the tuning bar.
Option B (Direct game shortcut): Holding the configured key while pressing Insert or F1 jumps directly from the game into the compact tuning bar; Esc / B closes straight back to the game.
Controls Configuration (Alt menu shortcut): Adds an entry under the Controls tab to choose between Right Shift (default) and Right Alt, leaving left-hand gaming keys (WASD, sprint/roll) completely free from interference.
Deduplicated Rendering (row_value_cell): Sliders, toggles, choices, and subpage links share a single unified cell renderer between the full menu and tuning bar, preventing double maintenance when upstream adds new options.
Dynamic Prompts: Menu prompt bar shows [RShift] Tune or [RAlt] Tune (and controller West button dot) dynamically.
New Test: Added tests/test_menu_tune.py covering Option A/B triggers, prompt glyphs, and non-dimmed drawing.
Thanks, a bar you can tune with while still seeing the picture is a nice idea, and pulling the value cell out into
row_value_cellkeeps the two views from drifting. The screenshots in the thread help. The settings key is saved by row name (alt_menu_shortcut=in hookshot_options.ini), so no option id is used and nothing collides with main or the open menu PRs (#2177, #2304, #2197, #2119 touch other hunks). When the menu is closed the new code only drains the menu's own key queue, so game input isn't touched.A few things to fix before it goes in (details inline):
!press[12]doesn't catch it and the menu flips into (or out of) tune mode before the tab changes.WM_KEYDOWNin host/display.c, but a plain Alt press arrives asWM_SYSKEYDOWN, which isn't recorded. Sotake_key_press(VK_RMENU/VK_MENU)never fires there. Holding it with Insert still works (that path usesGetAsyncKeyState). Linux is fine, because display_sdl.c maps both keys.tune_modeis set, not only for the bar. Open a page from the bar (Controls, Teleport, Inventory) or get the restart question, and the full-screen page draws straight over the undimmed game.Nits:
pad(HOS_PAD_NORTH)toggles the bar too, and the glyph only lights the West dot.row_can_tuneandmenu_is_tuning()are never used.tab_titlegetsTAB_MOTION).CI hasn't run yet: the workflows are waiting for approval (action_required). The static checks I looked at that cut code out of vk_menu.cpp (test_menu_keys' key-poll block, test_audio_volume's
tab_titlesregex, the fps tests) still match the new layout, but this needs a real run. Since this adds a new UI mode and a new keybind, whether it belongs in the menu is NotRexed's call.Small regression: before, a Debug tool that both changes and confirms (cheats 0, 1, 4, 6, 7, 9: "Toggle", "Set", "Apply", "Switch") showed Change and its confirm prompt. This branch shows only Change. Adding
if (selr.cheat >= 0) if (const char* says = cheat_confirm(selr.cheat)) prompt_list[prompts_n++] = {ui::kConfirm, says};here would keep it (there's room in the 7).This skips the dim for every page while
tune_modeis set. From the bar, Enter on Controls opens page 1, Teleport and Inventory open pages 3 and 4, and closing with a restart-needed change gives page 5. All of those then draw full-screen over the undimmed game.if (!(tune_mode && page == 0))would keep the dim for them.On Windows,
take_key_pressonly sees keys noted fromWM_KEYDOWN(host/display.c). A plain Alt press comes asWM_SYSKEYDOWN, so with "Right Alt" chosen this never returns true and the in-menu toggle does nothing. NotingVK_MENUin theWM_SYSKEYDOWNcase would fix that, or the tune key could be edge-detected fromGetAsyncKeyStatelike the Linux path. (Releasing a lone Alt also sendsSC_KEYMENUto the window, which is worth a quick check in the game.)With Right Shift as the key, Shift+Tab (previous tab) toggles tune mode: the Shift press lands a frame or more before Tab, so
press[12]is still false whentune_pressfires. You could fire on the release of the key when nothing else was pressed while it was held, or skip the toggle while Tab is down or was pressed during the hold. Also,HOS_PAD_NORTHtoggles too, but the description and the glyph only mention X/Square (West).https://github.com/user-attachments/assets/72170716-1cad-41bb-a9fd-01dfd8272aa7
change to release fire
noted, and switched to right ctrl just to avoid the entire issue
got it - adjusted to check for page zero to add the dim on secondary screens executed through the compact menu
I interpreted "prompt" as "hint" here , I think that is what we are looking for. I have added this back in
Hey - made the changes you requested - sidestepped right alt and chose right ctrl. Otherwise took your suggestions and pulled head back into so its ready to merge should you like the work
oh and went with "Compact menu"
Thanks for the quick turnaround, checked the new head against my last review.
Fixed:
GetAsyncKeyState(VK_RCONTROL)works on both Windows and the Linux key table (display_sdl.c fillsVK_RCONTROL), so the in-menu toggle works on both.!(tune_mode && page == 0)keeps the dim for Controls, Teleport, Inventory and the restart question.row_can_tuneis gone, the bar says "Motion" on the Motion page, and "Compact menu key" is a clear row name.New, small:
row_value_cellhas a branch the old full-menu code didn't: Debug tools with no value but a confirm (Apply x2, Open list, Switch age, Open) now show that word in their value cell instead of the ▸ arrow, in the full menu too (inline). Fine if intended, but it changes the Debug tab's look outside the bar, so worth a line in the description.!r.value && r.cheat < 0), where the full menu shows the row's name / "Confirm".CI: the build and test workflows (Windows builder, game-free source regressions, Linux AppImage) are waiting for a maintainer to approve the run, so nothing beyond the title checks has run yet. No conflicts with main.
The bugs I raised are fixed. Whether this UI mode belongs in the menu is still NotRexed's call, so I'm leaving the decision with them.
@ -2000,3 +1957,3 @@} else {const std::string v = cheat_value(r.cheat);choice_cell(c, va, vb, v.empty() ? buttons[r.cheat] : v, selected, cheat_chooses(r.cheat), cheat_chooses(r.cheat), r.cheat == 9);choice_cell(c, va, vb, v.empty() ? cheat_button_text(r.cheat) : v, selected, cheat_chooses(r.cheat), cheat_chooses(r.cheat), r.cheat == 9);This branch is new compared with the old inline code: before, a Debug tool with an empty value fell through to
!r.valueand drew the ▸ arrow; now cheats 2, 3, 8, 9 and 10 show "Apply" / "Open list" / "Switch" / "Open" as their value in the full menu as well. Fine if that's the intent, just flagging that it changes the full Debug tab too.Thanks for the feedback an kind words. Here is what I have for you
1 - restart/quit - fixed so that on first selection the prompt says the action (enter or quit) and on enter you get "confirm", just like the F1 menu
2 - updated the test to use real code so it actually is a test
3 - unified the button labels under a single cheat_button_text() method so both the full menu and compact bar pull from the same table
let me know if there is anyhting else
Thanks for the fast follow-up, checked 7aed804b against
c44fbb62(the head was rebased onto main, so I compared the patches).Fixed since my last review:
menu_tick.cheat_button_text()is now the one table both the full menu androw_value_cellread, and the full Debug tab draws the same words as before this PR (Mark, Return, Choose map, Switch, Edit items). That also settles my earlier note about the changed Debug look, so nothing needs adding to the description.Still open (small):
tests/run_all.py. It compiles a fixture, but has noRUN_ALL_ARGS = ["--cxx", "{cxx}"](see test_menu_keys.py), so it always usesg++, which the llvm-mingw Windows runner may not have. Please add the line; it already takes--cxx.Not blocking: the settings key is saved by row name (no option id), so no collision. The three build and test workflows are still waiting for a maintainer to approve the run (action_required), so none of the source checks have run on this head. I read the other tests that cut code from vk_menu.cpp (test_menu_keys, test_audio_volume's
tab_titles) against the new layout and they still match, but a real run is needed.Whether this UI mode and keybind belong in the menu is still NotRexed's call, so I am leaving the decision with them.
@ -0,0 +19,4 @@RUN_ALL_ARGS = ["--cxx", "{cxx}"]ROOT = Path(__file__).resolve().parents[1]Add
RUN_ALL_ARGS = ["--cxx", "{cxx}"]here (as test_menu_keys.py does) so run_all passes the CI's compiler instead of the defaultg++.Fixing this myself^
I pushed one small commit on top:
tests/test_menu_tune.pynow declaresRUN_ALL_ARGS = ["--cxx", "{cxx}"], so run_all passes the platform compiler instead of falling back to g++ (it passes locally). That was the last open point; everything from the earlier rounds is fixed. Looks good to me, thanks for iterating on this.CI still needs a maintainer to approve the workflow runs before it goes in.
github-sync referenced this pull request2026-11-12 06:22:54 +00:00