Update CLAUDE.md and ARCHITECTURE.md for viewport scrolling, glyph chooser, and sidebar redesign

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-17 17:59:14 -05:00
parent d5d67e988b
commit 5d97f67a64
2 changed files with 30 additions and 15 deletions
+22 -9
View File
@@ -82,25 +82,38 @@ Currently a thin wrapper around `Board` that provides game-logic methods (`try_m
**`pub fn load(path: &str) -> Result<Board, ...>`** — reads a file, deserializes, converts. Called from `main()` before the window is created.
**Why loading happens before window creation:**
The window minimum size is derived from `board.width` and `board.height`. eframe requires `NativeOptions` (including window size) to be set before calling `run_native`. So the board must be loaded first.
eframe requires `NativeOptions` (including window size) to be set before calling `run_native`. Loading the board first means its dimensions are available if they're ever needed for sizing logic (currently the window uses fixed defaults, but the board must exist before `App::new` is called).
---
### `main.rs` — app + rendering
`App` holds a `GameState`, an `AppMode` (`Play` | `Edit`), and an `EditorState` (`selected: Archetype`). The `update` method:
`App` holds a `GameState`, an `AppMode` (`Play` | `Edit`), and an `EditorState`. `EditorState` carries:
- `selected: Archetype` — the archetype class to stamp
- `glyph: Glyph` — the visual to stamp (independent of the archetype's default; resets to `archetype.default_glyph()` when the archetype selection changes)
- `glyph_picker_open: bool` — whether the glyph picker dialog is visible
- `tab: EditorTab` (`Palette` | `Board` | `World`) — which side-panel tab is active
The `update` method:
1. Reads arrow key input and calls `GameState::try_move`**Play mode only**
2. Draws the menu bar with a File menu and a Play/Edit mode toggle
3. In Edit mode: draws a right-side palette panel listing `ALL_ARCHETYPES` as selectable rows — declared before `CentralPanel` so egui allocates its space first
4. Draws the board: for each cell, a filled background rect then a centered monospace character
5. Draws the player on top using `Glyph::player()` (hardcoded `@` in cyan — the player is not a board cell)
6. In Edit mode: allocates the full viewport as a click target; on click, converts pixel position to cell coordinates and stamps the cell with `(selected.default_glyph(), selected)`
3. In Edit mode: draws a resizable right-side panel (default 200 px) with a Palette/Board/World tab bar — declared before `CentralPanel` so egui allocates its space first
4. Draws the board and player (see viewport sections below)
5. In Edit mode: renders the glyph picker `egui::Window` if open — board-palette of unique glyphs, fg/bg `color_edit_button_srgba`, and a 16×6 ASCII character grid
**Cell rendering constants:** `CELL_W = 14.0`, `CELL_H = 20.0` pixels. The board is centered in the CentralPanel when the window is larger than the minimum size. Window min-size is computed for Play mode; the Edit mode palette panel (`PALETTE_PANEL_W = 120.0` px) may clip the board at minimum size.
**Viewport — Play mode (`board_origin`):**
The `board_origin(available, board_w, board_h, player)` function computes the pixel position of cell (0,0). If the board fits along an axis, it is centered. If it overflows, the viewport is centered on the player and clamped so no empty space appears at the board edges. No scroll bars.
**Viewport — Edit mode (`ScrollArea`):**
The board is wrapped in `egui::ScrollArea::new([true, true])` which shows scroll bars when the board overflows the viewport. Content is allocated at exact board size; `rect.min` from `allocate_exact_size` serves as the origin.
**Cell rendering constants:** `CELL_W = 14.0`, `CELL_H = 20.0` pixels. Window uses fixed default size (`DEFAULT_WINDOW_W = 840`, `DEFAULT_WINDOW_H = 524`) and a small fixed minimum — map size no longer drives window dimensions.
**Click-to-cell math:** `rel = click_pos - origin; cell_x = (rel.x / CELL_W).floor() as i32`. Negative `rel` values stay negative and are caught by a `>= 0` guard. This mirrors the rendering math exactly so clicks land on the correct cell.
**Stamp action:** `*board.get_mut(cx, cy) = (self.editor.glyph, self.editor.selected)` — the custom glyph and selected archetype are written together. The glyph can differ from the archetype's `default_glyph()` if the user has customized it.
---
## Map file format
@@ -166,10 +179,10 @@ A direct mapping from palette character → `(Glyph, Archetype)` means the map f
**Multi-board world** — right now the engine loads a single `maps/start.toml`. Future: a world file or directory of boards, lazy-loaded as the player moves through portals.
**Editor glyph chooser** — the editor can paint cells with archetypes (default glyphs), but there's no way yet to choose a custom character or color for a cell. The next editor feature is buttons that open char/color picker dialogs.
**File open dialog** — the editor has no way to load a different map file from the UI. This requires a native file dialog (e.g. `rfd`). WASM support for file dialogs is an open question.
**Map save** — the editor can paint cells but has no way to write changes back to a `.toml` file. Saving will require serializing the `Board` back to `MapFile` format and writing it to disk.
---
## Future considerations
+8 -6
View File
@@ -51,14 +51,16 @@ The game is a single Rust binary using **eframe 0.33 / egui 0.33** for the GUI.
- `pub fn load(path: &str) -> Result<Board, Box<dyn std::error::Error>>` — reads and converts a `.toml` map file
**`src/main.rs`** — app entry point:
- `AppMode` enum (`Play` | `Edit`) — gates arrow-key input; toggles the palette panel
- `EditorState` — holds `selected: Archetype` (the archetype to paint on click)
- Loads `maps/start.toml` before creating the window (so board dimensions drive window size)
- Window min-size = `board.width * CELL_W + 2*PANEL_MARGIN` × `board.height * CELL_H + MENU_H + 2*PANEL_MARGIN` (sized for Play mode; Edit mode panel may clip at minimum size)
- Board is centered in the CentralPanel when the window is larger than minimum
- `AppMode` enum (`Play` | `Edit`) — gates arrow-key input; toggles the side panel and viewport mode
- `EditorTab` enum (`Palette` | `Board` | `World`) — which tab is active in the side panel
- `EditorState` — holds `selected: Archetype`, `glyph: Glyph` (the glyph to stamp, independent of archetype default), `glyph_picker_open: bool`, and `tab: EditorTab`. Selecting a new archetype resets `glyph` to that archetype's default.
- Window uses fixed default size (`DEFAULT_WINDOW_W = 840`, `DEFAULT_WINDOW_H = 524`) independent of map dimensions; `board_origin()` handles centering or player-centered scrolling at runtime
- `draw_glyph(painter, origin, x, y, glyph)` draws one cell: filled rect (bg) + monospace char (fg)
- `board_origin(available, board_w, board_h, player)` — if the board fits in the viewport, centers it; if it overflows, centers on the player and clamps to board edges (no empty space shown)
- Player is rendered on top of the board using `Glyph::player()`
- Click-to-paint converts `mouse_pos - origin` to cell coordinates using the same math as rendering
- **Play mode**: arrow keys move player; viewport scrolls to keep player centered when map > window
- **Edit mode**: `ScrollArea::both()` wraps the board so scroll bars appear when map > window; click-to-paint stamps `(self.editor.glyph, self.editor.selected)` at the clicked cell; side panel (200 px, resizable) shows Palette/Board/World tabs
- **Glyph picker dialog** (`egui::Window`): opened from the Palette tab; shows a board-palette of unique glyphs, fg/bg `color_edit_button_srgba` pickers, and a 16×6 grid of printable ASCII chars (0x200x7E)
### Map file format (`maps/*.toml`)