# UI System

## Scene structure

```
RootUI (Control, fullscreen, always visible)
├── DebugMenu
├── GameMenu
│   ├── GameMenuPartyTab
│   └── GameMenuItemsTab
├── ChatBoxContainer
│   └── InteractIndicator
├── ModalBackdrop          ← shared backdrop; repositions dynamically
├── PauseMenu
│   ├── PauseMain
│   └── PauseSettings
├── QuitConfirmDialog
└── MainMenuConfirmDialog
```

Child order matters — later siblings render on top. `ModalBackdrop` shifts its own position in the tree at runtime to sit just below whichever modal overlay is currently topmost (see [ModalBackdrop](#modalbackdrop)).

`RootUI` is a permanent child of `RootScene` and registers itself with the `UI` singleton on `_enter_tree`. Access its children through the `UI` singleton.

## UI singleton

`UI` (autoload) is the global access point.

| Accessor | Type | Notes |
|---|---|---|
| `UI.DEBUG_MENU` | `DebugMenu` | Dev scene-jump overlay |
| `UI.GAME_MENU` | `GameMenu` | JRPG-style in-game menu |
| `UI.PAUSE_MENU` | `PauseMenu` | Pause overlay; also pauses the scene tree |
| `UI.QUIT_DIALOG` | `QuitConfirmDialog` | "Quit to desktop?" confirm; call `.open()` to show |
| `UI.MAIN_MENU_DIALOG` | `ConfirmDialog` | "Return to main menu?" confirm; call `.open()` to show |
| `UI.BACKDROP` | `ModalBackdrop` | Shared semi-transparent backdrop; managed automatically |
| `UI.dialogueActive` | `bool` | `true` for the entire duration of a `DialogueAction` |
| `UI.activeConversation` | `bool` | `true` only during a `CONVERSATION`-mode dialogue |
| `UI.chatBoxContainer` | `Control` | Parent node for world-space dialogue textboxes |

`dialogueActive` is set by `DialogueManager` signals — it is broader than any single textbox being visible. Movement blocks on `dialogueActive` and on `UI.GAME_MENU.isOpen()`.

## ClosableMenu

Base class for any togglable panel. Extends `Control`; the `isOpen:bool` export drives `visible`.

```gdscript
menu.open()    # shows, emits opened
menu.close()   # hides, emits closed
menu.toggle()
```

Signals: `opened`, `closed`.

All new menus that need standard show/hide behaviour should extend `ClosableMenu`.

## ConfirmDialog

Reusable "Yes / No" confirmation overlay at `res://ui/component/ConfirmDialog.gd`. Extends `ClosableMenu`.

```gdscript
dialog.open()                        # shows, focuses No (safe default), emits opened
dialog.confirmed                     # signal — fires after Yes is pressed, before close
```

**Focus locking:** `focus_neighbor_top/bottom` on both buttons is wired so controller navigation cannot escape the dialog to elements behind it.

**`ui_cancel`** closes the dialog the same as pressing No.

**Subclassing:**

```gdscript
class_name MyConfirmDialog extends ConfirmDialog

func _ready() -> void:
  super._ready()
  confirmed.connect(func(): do_the_thing())
```

`QuitConfirmDialog` uses this pattern — it extends `ConfirmDialog` and connects `confirmed → get_tree().quit()` in its own `_ready()`.

**Adding a new confirm dialog:**
1. Create a `.tscn` with `ConfirmDialog.gd` as script; set label text in the scene
2. Add `btnYes`/`btnNo` node path exports pointing to your two buttons
3. Add the instance to `RootUI.tscn` after `PauseMenu` (so it renders on top)
4. Register it with the backdrop in `RootUI._ready()`: `modalBackdrop.register(myDialog)`
5. Expose via `RootUI.gd` export + `UISingleton.gd` accessor if other systems need it
6. Connect `myDialog.confirmed` wherever the action should fire

## ModalBackdrop

`res://ui/component/ModalBackdrop.gd` — a single fullscreen semi-transparent `ColorRect` shared across all modal overlays.

**How it works:** each registered overlay's `opened`/`closed` signals are connected. When any overlay opens, the backdrop becomes visible and `move_child()`s itself in the scene tree to sit immediately before the highest-index open overlay. When all overlays close, it hides.

**Result:** only one backdrop is ever visible, and it always sits between the game world (or lower overlays) and the frontmost open overlay — even when overlays are stacked (e.g. `QuitConfirmDialog` over `PauseMenu`).

**Registering a new overlay:**

```gdscript
# In RootUI._ready() — overlay must be a direct child of RootUI and have opened/closed signals
modalBackdrop.register(myOverlay)
```

`PauseMenu`, `QuitConfirmDialog`, and `MainMenuConfirmDialog` are all registered.

## AdvancedRichText

`RichTextLabel` subclass (`@tool`) used inside world-space dialogue textboxes. Handles:

- Smart word-wrap (`TextServer.AUTOWRAP_WORD_SMART`)
- Pagination via `maxLines` / `startLine` exports
- Inline input icons: `[input action=interact]text[/input]` → replaced with the icon image from `res://ui/input/{action}.tres`
- Optional auto-translation via Godot's `tr()`

Supported icon actions: `interact`, `pause`, `debug`, `up`, `down`, `left`, `right`.

## Game menu

JRPG-style in-game menu at `res://ui/gamemenu/`. Open with the `menu` input (**Tab** on keyboard, **Y** on controller). Blocks player movement while open via `EntityMovement._canMove()`.

**Structure:**
- Left sidebar: `ItemList` tab selector (Party, Items)
- Right panel: active tab content

| Tab | Content |
|---|---|
| Party | One card per `PartyMember` — name, status, HP/MP, ATK/DEF/SPD/MAG/LCK |
| Items | One row per `ItemStack` in `PARTY.BACKPACK` — item name + quantity |

Both tabs are populated dynamically on open; call `refresh()` on the active tab directly if data changes while the menu is already open.

**Key methods on `GameMenu`:**

| Method | Effect |
|---|---|
| `open()` | Shows menu, refreshes active tab, grabs sidebar focus |
| `close()` | Hides menu |
| `isOpen() -> bool` | Visibility state |

`ui_cancel` or `menu` closes the menu. The `menu` input opens it only when `UI.dialogueActive` is false and the textbox is closed.

To add a new tab: add a value to `GameMenu.Tab`, create a tab scene/script under `ui/gamemenu/`, instance it in `GameMenu.tscn` as a sibling of the other tabs, add an `@export` for it in `GameMenu.gd`, and add the `match` branch in `_selectTab()`.

## Pause menu

`PauseMenu` wraps `PauseMain` (button list) and `PauseSettings` (settings tabs). Opening it calls `get_tree().paused = true`; closing restores it.

| Method | Effect |
|---|---|
| `PauseMenu.open()` | Pauses tree, shows container, opens PauseMain, emits `opened` |
| `PauseMenu.close()` | Unpauses tree, hides everything, emits `closed` |
| `PauseMenu.isOpen() -> bool` | Visibility state |

**`ui_cancel` behaviour inside PauseMenu:**
- If `QuitConfirmDialog` or `MainMenuConfirmDialog` is open → ignored (the dialog handles it)
- If `PauseSettings` is open → closes settings, reopens PauseMain
- Otherwise → closes PauseMenu

**PauseMain buttons:**

| Button | Behaviour |
|---|---|
| Resume | Closes PauseMenu |
| Settings | Opens PauseSettings |
| Main Menu | Opens `MainMenuConfirmDialog`; on confirm → `SCENE.setScene(INITIAL)` |
| Quit Game | Opens `QuitConfirmDialog`; on confirm → `get_tree().quit()` |

When either confirm dialog cancels, focus returns to the button that opened it.

**Cannot open on main menu:** `Pause.gd` checks `SCENE.currentScene == INITIAL` and skips opening when already closed.

## Main menu

`res://ui/mainmenu/MainMenu.tscn`. Buttons: **New Game**, **Settings**, **Quit Game**.

- New Game → `SCENE.setScene(OVERWORLD)` + `OVERWORLD.mapChange(...)`
- Settings → opens the `MainMenuSettings` overlay
- Quit Game → opens `UI.QUIT_DIALOG`; on cancel, focus returns to the Quit button

## Settings menu

`SettingsMenu` is a shared tabbed component instanced in both `MainMenuSettings` and `PauseSettings`. Three tabs: **Gameplay**, **Sound**, **Graphics** — currently contain placeholder labels only.

## Debug menu

`DebugMenu` (toggle via `debug` input — **F1**) provides four scene-jump buttons:

| Button | Action |
|---|---|
| Overworld | `SCENE.setScene(OVERWORLD)` |
| Battle | `SCENE.setScene(BATTLE)` |
| Cooking | `SCENE.setScene(COOKING)` |
| Initial | `SCENE.setScene(INITIAL)` |

Access via `UI.DEBUG_MENU`. Starts hidden.

## Theme & assets

- Global theme: `res://ui/UI Theme.tres` — applied to all UI nodes
- Input icons: `res://ui/input/{action}.tres` — one file per action name
- Font: configured globally in project settings

## Adding a new menu

1. Create a scene whose root extends `ClosableMenu` (or `Control` if open/close isn't needed)
2. Add it as a child of `RootUI.tscn` — position after `PauseMenu` if it should render above it
3. Export a typed reference on `RootUI.gd` and wire it in the Inspector
4. Expose via a getter on `UISingleton.gd` if other systems need access (follow the `PAUSE_MENU` / `GAME_MENU` pattern)
5. If it needs a backdrop, register it: `modalBackdrop.register(myMenu)` in `RootUI._ready()`
