Files
security-game/docs/ui-copy.md
T
Gig Line Dev 7315166361
Full build / Import + headless tests (push) Successful in 36s
Full build / Full desktop build + optional release (push) Skipped
Guard destructive settings and radio actions
- Route reset campaign and tutorial replay through one reusable confirmation dialog; repeated presses cannot stack or emit twice.
- Keep emergency hold-to-send single-shot and cover the post-submit release.
- Add destructive-action regression checks, strings and documentation.
2026-11-02 16:29:26 +01:00

191 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# UI copy and button inventory
Status: dialogue, button-state, focus, modal-close, font/layout and destructive-action passes complete; end-to-end audit in progress for 0.9.3.
## Screen inventory
| Screen / panel | Owner | Primary actions | Notes |
| --- | --- | --- | --- |
| Start / venue menu | `start_menu.gd` | Continue shift, choose venue, Help, Settings, Quit | Campaign status and free-play venues share this screen. |
| Tutorial card | `tutorial_overlay.gd` | F1 skip | Non-blocking; no mouse capture. |
| HUD | `hud.gd` | Observe, target, inspect, wheel, radio, flashlight, body camera, door, pause | No interactive controls; must stay mouse-transparent. |
| Action wheel | `action_radial.gd` | Choose de-escalation/access/radio action | Confirm with Space/click; cancel with Esc/RMB. |
| Access inspection | `hud.gd` | A = admit, D = refuse | Real access evidence cards. |
| Wand screening | `hud.gd` | A = admit, D = refuse | Shows alert and carried items. |
| Radio / emergency | `radio_panel.gd` | Send request / hold Send emergency, Cancel | Normal click vs emergency hold. |
| Pause | `pause_overlay.gd` | Resume, restart, Settings, quit to menu | Esc resumes. |
| Settings | `settings_panel.gd` | Apply, Cancel, Replay tutorial, Reset campaign, rebind controls | Both reset and tutorial replay ask for confirmation. |
| Help | `help_overlay.gd` | Filter controls, Close | Policy card and current bindings. |
| Shift review | `shift_review.gd` | Continue campaign, timeline, return to menu, quit | Campaign next step is shown here. |
| Timeline | `event_timeline.gd` | Previous, Next, Close | Review evidence stepper. |
## Canonical terms
- **Admit / refuse** — access decisions. Do not alternate with “allow / deny” or “grant / reject” in player-facing controls.
- **Warning** — Level 2.
- **Blocking** — Level 3 positioning; do not call it “force”.
- **Assistance** — Level 4 radio call; **emergency** is Level 5.
- **Body camera** — always two words.
- **Shift review** — the end-of-shift screen; “review” alone is acceptable in prose.
- **Continue campaign** — advance to the next venue.
- **Return to menu** — leave the review to the venue menu (it does not start a new shift by itself).
- **Send request** — non-emergency assistance; **Send emergency** — emergency call.
- **Hollywood Boulevard — Day / Night** — the two scenery variants in the venue menu. Use an em dash and title case for the variant; do not call them “maps” in player-facing copy.
## Button labels and states
| Surface | Label | Behavior | State audit |
| --- | --- | --- | --- |
| Start menu | Continue shift — <venue> | Resume/advance campaign | Hidden when complete; venue-specific |
| Start menu | Venue rows | Start free-play/campaign venue | Hover/focus/pressed styled |
| Start menu | Help | Open Help overlay | Standard button |
| Start menu | Settings | Open Settings | Standard button |
| Start menu | Quit game | Exit application | Destructive; label clarifies scope |
| Tutorial | F1 skip tutorial | Skip lesson | Keyboard only, non-blocking |
| Action wheel | Stop/Look/Point/No/Ask/Inspect/Wand/Warn/Block/Reason/Recognize/Radio | Choose verb | Hover/selection highlight |
| Inspection | A = admit · D = refuse entry | Access decision | Keyboard hints, policy text |
| Wand | A = admit · D = refuse entry | Screening decision | Keyboard hints, item/alert text |
| Radio | Send request | Submit assistance | Normal click |
| Radio | Send emergency | Submit emergency | Hold-to-confirm; Cancel aborts |
| Radio | Cancel | Close form | Standard button |
| Pause | Resume | Resume shift | Default focus |
| Pause | Restart shift | Restart current shift | Standard button |
| Pause | Settings | Open Settings | Standard button |
| Pause | Quit to menu | Return to venue menu | Standard button |
| Settings | Apply settings | Persist settings | Standard button |
| Settings | Cancel | Discard edits | Standard button |
| Settings | Replay tutorial | Confirm, then clear tutorial flags and restart Hollywood | Confirmation dialog |
| Settings | Reset campaign | Confirm, then reset campaign progress | Confirmation dialog |
| Help | Close | Close overlay | Standard button |
| Review | Continue campaign | Start next venue | Hidden when campaign complete |
| Review | Body camera timeline | Open event timeline | Standard button |
| Review | Return to menu | Return to venue menu | Standard button |
| Review | Quit game | Exit application | Standard button |
| Timeline | Previous / Next / Close | Step/close evidence | Standard buttons |
## Button/label issues found
- HUD inspection and wand used “allow/deny” versus “admit/refuse”; both now use **A = admit · D = refuse entry**.
- Shift review’s old “Run another shift” actually returned to the menu; renamed **Return to menu**.
- Radio emergency action used all-caps while assistance used sentence case; both are now sentence case, with the warning in the panel title/hold hint.
- Normal radio send was previously wired only to the emergency hold handlers; fixed in 0.8.1 and covered by `test_radio_panel.gd`.
- “Quit” was ambiguous between menu and application exit; both application-exit buttons now read **Quit game**.
- Settings “Apply” was made verb+object as **Apply settings**.
## Dialogue audit
The NPC reason verb now uses the authored `reason` context instead of always
falling back to the general dialogue lines. Every shared archetype has at least
one role-specific reason answer, so asking a staff member, guest, delivery
worker or performer no longer produces an unexplained silence. Deliberately
silent profiles can still return an empty context line.
| Surface | Finding | Result |
| --- | --- | --- |
| Shared NPC reasons | `request_reason()` ignored `context_lines["reason"]`; staff, guests, delivery, performers, intoxicated people, crisis and threat profiles had no answer. | Fixed; all 12 shared archetypes now answer in character. |
| Door staff | Only The Joke Shop authored door lines. | Hollywood has wristband lines; the Studio has ticket/credential/guest-list lines; the inspection log selects the venue context. |
| Radio | “Call cancelled” could read as if a call had been sent; the description placeholder was ungrammatical. | “Call not sent — hold the button to confirm.” and “Describe the person and their behavior.” |
| Tutorial / Help | The wheel verb is **Ask**, but help and the HUD key strip called it “polite direction”. | Both now say **Ask**; the tutorial tells the player to choose Ask after closing the panel. |
| Review / campaign | Continue/return/quit labels and campaign-next copy checked. | No ambiguity found; no change. |
| Repetition | Shared tourist/confused/delivery lines recur across venues by design; role-specific reason lines prevent every archetype sounding alike. | Accepted for the shared-archetype model. |
| Localization length | Longest authored NPC/door line is 56 characters; longest UI string is `hud.controls` at 172 characters. | Dialogue fits the HUD log; long-string/font-scale layout is covered below. |
## Button state audit
All `Button` controls now use `src/game/ui/ui_styles.gd`, which defines explicit
**normal**, **hover**, **pressed**, **focus** and **disabled** styles plus the
matching font colours. This removes the engine-theme fallback that previously
appeared when a custom dark button was disabled or focused by keyboard.
| Surface | States / behavior checked | Result |
| --- | --- | --- |
| Start menu | Venue rows, Continue, Help, Settings, Quit; focus clamped to the list; Continue hidden when the campaign is complete. | All buttons styled; hidden state is intentional, not a disabled state. |
| Tutorial | F1 skip is a keyboard shortcut, not a button; its hint is non-interactive. | Keyboard-only by design; no hover/focus state to style. |
| Pause | Resume, Restart, Settings, Quit to menu; Resume receives focus on open. | All four states styled; focus is distinct from hover. |
| Settings | Replay tutorial, Reset campaign, key-binding rows, Apply settings, Cancel; rebinding shows “Press…” while listening. | All action buttons styled; reset-confirmation dialog OK/Cancel styled; fields keep the engine control theme. |
| Help | Close; Esc still closes. | Styled; no disabled state is used. |
| Shift review | Continue campaign, Body camera timeline, Return to menu, Quit game; Continue hidden when the campaign is complete. | All styled; hidden state is intentional. |
| Timeline | Previous/Next/Close; Previous is disabled at the first event, Next at the last, and both when empty. | Disabled state verified in `test_event_timeline.gd`; Close stays enabled. |
| Radio | Send request, Send emergency, Cancel. Emergency hold uses press/release, so the action button stays enabled while holding. | All styled; disabling it during the hold would break confirmation. |
| Toggles | CheckButton/OptionButton controls keep the engine theme. | Intentional: the shared style targets action buttons, not checkbox/radio affordances. |
## Keyboard and gamepad focus
`UiFocus` builds an explicit focus ring for each modal, skips hidden and
disabled controls, and makes Up/Down, Tab/Shift+Tab and the D-pad/stick wrap.
`GuardController._ensure_gamepad_bindings()` adds A to `ui_accept` and B to
`ui_cancel`; the built-in directional UI actions already carry D-pad/stick
events.
| Surface | Default focus | Cancellation / trap check |
| --- | --- | --- |
| Start menu | First visible target: Continue when present, otherwise the first venue. | A/Enter/Space activates the focused row; B/Esc quits; Continue, venue rows and footer actions share one wrapping ring. |
| Pause | Resume. | B/Esc resumes; the four actions wrap. |
| Settings | First slider. | B/Esc cancels; focus ring covers sliders, toggles, difficulty, rebinds, Apply and Cancel; sliders keep Left/Right for value adjustment. |
| Help | Search field. | B/Esc closes; Search and Close share a ring. |
| Shift review | Continue when available, otherwise the timeline. | B/Esc returns to the menu; all four actions are in the ring and hidden Continue is skipped. |
| Timeline | First enabled action: Next when Previous is disabled, Close when empty. | B/Esc closes; Previous/Next leave the ring when disabled so focus cannot land on a dead button. |
| Radio | Send action. | B/Esc cancels; form fields, action and Cancel are linked; A confirms the focused action. |
| Tutorial | None; F1 is the documented skip shortcut. | Keyboard-only by design, no focus trap because the card is mouse-transparent and non-modal. |
## Modal close and pointer recapture
`GameManager._close_top_modal()` is now the single close path for the top
interactive layer. It handles the radial, inspection, wand, radio, timeline,
Help, Settings, Pause and review before Esc/right-click falls back to target
clearing or pause. `_refresh_freeze()` recaptures the mouse when the shift
resumes; `_show_start_menu()` keeps the pointer visible at the menu.
| Surface | Close routes | Result |
| --- | --- | --- |
| Pause | Resume, Esc, right-click | Closes via one path; guard unfreezes and mouse recaptures. |
| Settings | Apply settings, Cancel, Esc, right-click | From the venue menu, both apply and cancel restore the menu. From a shift, cancel returns to the game with the guard unfrozen; apply returns to pause. |
| Help | Close, Esc, right-click | Closes only Help; it no longer opens Pause behind the overlay. Returns to whatever was underneath. |
| Shift review | Return to menu, Esc, right-click | Returns to the venue menu with the pointer visible. |
| Timeline | Close, Esc, right-click | Returns to the review and keeps the pointer visible. |
| Radio | Cancel, Esc, right-click | Closes the form, clears the action hint and recaptures the mouse. |
| Inspection / wand | Esc, right-click | Closes the panel, unfreezes the guard and recaptures the mouse. |
| Action wheel | Esc, right-click | Cancels without a log entry and recaptures the mouse. |
| Start menu | Esc quits the application | Root menu; right-click is intentionally inert. |
| Tutorial | F1 skip | Non-modal card; never blocks the pointer or movement. |
The session smoke now covers menu → Settings → Cancel → menu, Help closing on
right-click, and Settings cancel from pause leaving the guard unfrozen.
## Destructive actions
Campaign reset and tutorial replay both open a `ConfirmationDialog`; pressing
the button again while it is open focuses the existing dialog instead of
stacking a second one. The confirmation is dismissed and the signal is emitted
at most once on OK; Cancel closes without emitting. The emergency radio remains
a press-and-hold action: a normal click does nothing for an emergency, a short
hold cancels, a full 1.6 s hold submits once, and the release after a completed
hold cannot submit again.
Regression coverage:
- `test_session_shell.gd` checks reset reuse/one-shot and replay confirmation/cancel.
- `test_radio_panel.gd` checks normal click, short hold, full hold and post-submit release.
## Font-scale and long-string layout
The HUD scale is tested at its **0.75×** and **1.75×** bounds. HUD top/time labels
clip instead of crossing into each other, and the target chip now measures the
scaled target text: normal long names widen the chip, while an extreme synthetic
name is capped at 420 px and ellipsized. Action-button labels use `clip_text` and
ellipsis in `UiStyles`, so a 1.5–2× pseudo-string cannot push a panel beyond the
viewport. The menu shell intentionally keeps its own fixed typography; the HUD
scale setting only changes HUD text.
Regression coverage lives in `test_accessibility.gd` (target-chip width/cap) and
`test_ui_buttons.gd` (long action-label minimum width). The pseudo-locale
translator workflow remains a 0.10.0 task; this pass uses deterministic long
strings and the maximum supported HUD scale.
## Remaining audit work
- Player-facing labels are now routed through `content/strings.en.json`; numeric
values, empty-state clears and debug-only text remain in code by design.
- Focus/hover/pressed/disabled states and keyboard/gamepad rings are recorded above.
- Long strings and 0.75×/1.75× HUD scale are covered above.
- Capture every dialog/button state and run `screen_designs.py compare` plus the pinned judge.