egui changes

This commit is contained in:
2026-06-03 17:45:43 -05:00
parent dc5b903741
commit 353461dbfd
10 changed files with 630 additions and 375 deletions
+29 -27
View File
@@ -138,23 +138,26 @@ eframe requires `NativeOptions` (including window size) to be set before calling
`App` holds a `GameState`, an `AppMode` (`Play` | `Edit`), an `EditorState`, a `default_font: BitmapFont` (loaded from `assets/vga-font-8x16.png` at startup, falling back to `create_placeholder`), and a `board_font: Option<BitmapFont>` (loaded from `board.font` when a per-board font is set). `App::new` takes `&egui::Context` to upload textures before the first frame.
Font change detection: before calling `show_editor_panel`, `font_spec_before` is cloned; after the call, if `board.font` differs, `apply_font_spec` reloads `board_font`. The active font for the frame is `board_font.as_ref().unwrap_or(&default_font)`, extracted as a `let` binding before mutable borrows to satisfy the borrow checker.
Font change detection: before calling `show_editor_panel`, `font_spec_before` is cloned; after the call, if `board.font` differs, `apply_font_spec` reloads `board_font`. `App::active_font()` resolves `board_font.as_ref().unwrap_or(&default_font)`; at the editor-panel and glyph-picker sites the same expression is used inline instead so the font borrow stays disjoint from the `&mut editor`/`&mut board` borrows (a method call would borrow all of `self`).
The `update` method phases:
`update` is a thin sequence of phase methods, each owning one concern:
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: calls `editor::show_editor_panel` — declared before `CentralPanel` so egui allocates its space first
4. Draws the board and player via `render::draw_board` (see viewport sections below)
5. In Edit mode: calls `glyph_picker::show` if `editor.glyph_picker_open`
1. `handle_input` — table-driven arrow keys → `GameState::try_move` (**Play mode only**)
2. `menu_bar` File menu (Save/Save As via `save_to`/`save_as`, Exit) and the Play/Edit toggle
3. `try_show_script_editor` — if a script is open, fills the `CentralPanel` with the code editor and returns `true`, ending the frame early
4. Editor side panel (Edit mode) — `editor::show_editor_panel`, declared before `CentralPanel`; then the font-reload check
5. `show_board` — draws the viewport and returns the clicked cell; the click is dispatched to `handle_board_click` *after* it returns
6. `show_glyph_pickers` — the Palette and Objects glyph pickers; then the selected object's glyph is flushed back to `board.objects[i]`
**Click dispatch borrow split:** `show_board(&self)` only draws and returns `Option<(usize, usize)>` (validated cell coords). The mutating `handle_board_click(&mut self, cx, cy)` runs afterward, so the immutable font borrow held during drawing never conflicts with the `&mut self` handler.
**Viewport — Play mode:**
`render::board_origin(available, board_w, board_h, player)` 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.
`render::board_origin(available, board_w, board_h, player, font, zoom)` 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:**
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 passed to `render::draw_board`.
**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.
**Stamp action:** `*board.get_mut(cx, cy) = (self.editor.palette.glyph, self.editor.palette.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.
---
@@ -162,39 +165,38 @@ The board is wrapped in `egui::ScrollArea::new([true, true])`, which shows scrol
All pixel-level rendering knowledge lives here. No dependency on `EditorState` or `App`. Cell size is no longer a fixed constant — it comes from the active `BitmapFont`.
**Constants:** Window sizing (`DEFAULT_WINDOW_W = 840`, `DEFAULT_WINDOW_H = 524`, `MIN_WINDOW_W/H`) defined here. No `CELL_W`/`CELL_H` — those were removed when bitmap fonts were introduced.
**Constants:** Window sizing (`DEFAULT_WINDOW_W = 840`, `DEFAULT_WINDOW_H = 524`, `MIN_WINDOW_W/H`) defined here. No `CELL_W`/`CELL_H` — those were removed when bitmap fonts were introduced. Per-cell pixel size comes from `BitmapFont::cell_size(zoom)`.
**`paint_glyph(painter, rect, glyph, font)`** — fills `rect` with `glyph.bg`, then draws the tile image tinted by `glyph.fg`. Used by both `draw_glyph` and `glyph_picker`.
**`rgba8_to_color32(c)` / `color32_to_rgba8(c)`** — inverse conversions between the core `color::Rgba8` and egui's `Color32`, used at the glyph-picker boundary where colors cross between the two type systems.
**`draw_glyph(painter, origin, x, y, glyph, font)`** — computes the cell rect from `font.tile_w/tile_h`, delegates to `paint_glyph`.
**`paint_glyph(painter, rect, glyph, font)`** — fills `rect` with `glyph.bg`, then draws the tile image tinted by `glyph.fg`. Used by `draw_glyph`, `glyph_preview_button`, and `glyph_picker`.
**`draw_board(painter, origin, board, font)`** — iterates all cells calling `draw_glyph`, then draws the player as an overlay on top.
**`glyph_preview_button(ui, glyph, font) -> Response`** — allocates a one-cell swatch (unzoomed), paints `glyph`, and returns the click `Response`. Shared by the Palette and Objects editor tabs for their editable glyph swatches.
**`draw_object_overlays(painter, origin, board, font, selected)`** — called from Edit mode when the Objects tab is active. Draws a dim border on every object cell and a bright border on the selected one, making objects discoverable without obscuring the underlying tile.
**`draw_glyph(painter, origin, x, y, glyph, font, zoom)`** — computes the cell rect from `font.cell_size(zoom)`, delegates to `paint_glyph`.
**`draw_board(painter, origin, board, font, zoom)`** — iterates all cells calling `draw_glyph`, then draws objects and the player as overlays on top.
**`draw_object_overlays(painter, origin, board, font, selected, zoom)`** — called from Edit mode when the Objects tab is active. Draws a dim border on every object cell and a bright border on the selected one, making objects discoverable without obscuring the underlying tile.
**`pos_to_cell(origin, pos, tile_w, tile_h) -> (i32, i32)`** — converts a pixel position to cell coordinates using floor division. Returns negative values for clicks above/left of the origin; callers must guard `>= 0`. Mirrors the rendering math so clicks land on the correct cell.
**`board_origin(available, board_w, board_h, player, font) -> Pos2`** — computes the pixel origin for Play-mode rendering (centering or player-tracking with edge clamping).
**`board_origin(available, board_w, board_h, player, font, zoom) -> Pos2`** — computes the pixel origin for Play-mode rendering (centering or player-tracking with edge clamping).
---
### `editor.rs` — editor state and side panel
**`EditorTab`** (`Palette` | `Board` | `Objects` | `World`) — which tab is active in the side panel.
**`EditorTab`** (`Palette` | `Objects` | `Board` | `Scripts` | `World`) — which tab is active in the side panel.
**`EditorState`** — transient editor state:
- `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
**`EditorState`** — transient editor state, grouped by concern into sub-structs so each tab can take only the borrow it needs (the dispatch in `show_editor_panel` passes disjoint sub-struct borrows, e.g. `&mut editor.objects` and `&mut editor.scripts`):
- `tab: EditorTab` — which side-panel tab is active
- `font_dialog_open: bool` whether the font picker dialog is visible
- `font_dialog_state: FontDialogState` — in-progress edits for the font dialog
- `selected_object: Option<usize>` index into `board.objects` of the currently selected object
- `object_glyph_picker_open: bool` — whether the object glyph picker is open
- `object_editing_glyph: Glyph` — local copy of the selected object's glyph; written back to `board.objects[i].glyph` every frame
- `placing_object: bool` — when `true`, the next board click creates a new `ObjectDef` at that cell
- `palette: PaletteState``selected: Archetype` (class to stamp), `glyph: Glyph` (visual to stamp; resets to `archetype.default_glyph()` when the archetype changes), `picker_open: bool`
- `font_dialog: FontDialog``open: bool`, `state: FontDialogState`
- `objects: ObjectEdit``selected: Option<usize>` (index into `board.objects`), `picker_open: bool`, `editing_glyph: Glyph` (local copy written back to `board.objects[i].glyph` each frame), `placing: bool` (next board click creates a new `ObjectDef`)
- `scripts: ScriptEdit``editing: Option<String>` (script open in the code editor), `content: String` (working copy), `new_name: String` (new-script draft)
**`show_editor_panel(ctx, editor, board, active_font)`** — renders the resizable right-side panel (default 200 px). Palette tab: scrollable archetype list and glyph preview button. Board tab: current font path label + "Font…" button. Objects tab: "Add Object" button that enters placement mode; lists objects by coordinate with click-to-select; for the selected object shows glyph preview, **Passable** and **Opaque** checkboxes (writing directly to `board.objects[i]`), and a script combobox. World tab: placeholder.
**`show_editor_panel(ctx, editor, board, active_font)`** — renders the resizable right-side panel (default 200 px): a tab bar, then dispatch to one function per tab. `palette_tab`: scrollable archetype list and glyph preview button. `objects_tab`: "Add Object" placement toggle, click-to-select, and for the selected object a glyph preview, **Passable**/**Opaque** checkboxes (writing directly to `board.objects[i]`), and a script combobox. `board_tab`: font path label + "Font…" button + zoom slider. `scripts_tab`: create and edit named scripts. World tab: placeholder. The glyph preview swatches use `render::glyph_preview_button`.
---