From 12ebe77932f5ce4b2c5e6d54f56e8ef0a48ae237 Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Mon, 18 May 2026 22:01:21 -0500 Subject: [PATCH] Simplify: extract paint_glyph, drop unused param and redundant comments - Add paint_glyph(painter, rect, glyph) to render.rs; draw_glyph now delegates to it, eliminating the duplicated rect_filled+text pattern that appeared in four places across editor.rs and glyph_picker.rs - Remove unused _board param from show_editor_panel and its call site - Drop redundant section-header comments in glyph_picker.rs (the ui.label() calls immediately below already name each section) - Remove extracted fg/bg/cur_ch locals in the char grid loop now that paint_glyph accepts a Glyph directly - Fix CLAUDE.md update phase description (stale from pre-refactor) Co-Authored-By: Claude Sonnet 4.6 --- CLAUDE.md | 5 +++-- src/editor.rs | 28 ++++++---------------------- src/glyph_picker.rs | 30 +++++++++--------------------- src/main.rs | 2 +- src/render.rs | 18 +++++++++++------- 5 files changed, 30 insertions(+), 53 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 0e1d82c..5989dc0 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -38,8 +38,9 @@ The game is a single Rust binary using **eframe 0.33 / egui 0.33** for the GUI. `update` is structured in phases: - Input handling — arrow keys move the player (Play mode only) - `egui::TopBottomPanel::top` — menu bar (File → Exit, Play/Edit mode toggle) -- `egui::SidePanel::right` — archetype palette panel (Edit mode only; declared before CentralPanel) -- `egui::CentralPanel::default` — game viewport; rendered with `ui.painter()`; click-to-paint in Edit mode +- `editor::show_editor_panel` — archetype palette panel (Edit mode only; declared before CentralPanel) +- `egui::CentralPanel::default` — game viewport; rendered with `render::draw_board`; click-to-paint in Edit mode +- `glyph_picker::show` — floating picker dialog (Edit mode only, when open) ### Modules diff --git a/src/editor.rs b/src/editor.rs index 29eb6e9..fa90a2b 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1,8 +1,8 @@ use eframe::egui; -use egui::{Align2, FontId, Sense, vec2}; +use egui::{Sense, vec2}; -use crate::game::{Archetype, Board, Glyph, ALL_ARCHETYPES}; -use crate::render::{CELL_H, CELL_W, PALETTE_PANEL_W}; +use crate::game::{Archetype, Glyph, ALL_ARCHETYPES}; +use crate::render::{CELL_H, CELL_W, PALETTE_PANEL_W, paint_glyph}; /// Which tab is active in the editor side panel. #[derive(Copy, Clone, PartialEq, Eq)] @@ -43,12 +43,11 @@ impl EditorState { /// /// Shows tab bar (Palette / Board / World) and, on the Palette tab, the /// scrollable archetype list and current-glyph preview button. -pub(crate) fn show_editor_panel(ctx: &egui::Context, editor: &mut EditorState, _board: &Board) { +pub(crate) fn show_editor_panel(ctx: &egui::Context, editor: &mut EditorState) { egui::SidePanel::right("palette_panel") .resizable(true) .default_width(PALETTE_PANEL_W) .show(ctx, |ui| { - // Tab bar ui.horizontal(|ui| { ui.selectable_value(&mut editor.tab, EditorTab::Palette, "Palette"); ui.selectable_value(&mut editor.tab, EditorTab::Board, "Board"); @@ -72,12 +71,7 @@ pub(crate) fn show_editor_panel(ctx: &egui::Context, editor: &mut EditorState, _ ui.horizontal(|ui| { let (r, cell_resp) = ui.allocate_exact_size( vec2(CELL_W, CELL_H), Sense::click()); - { - let p = ui.painter(); - p.rect_filled(r, 0.0, glyph.bg); - p.text(r.center(), Align2::CENTER_CENTER, - glyph.ch, FontId::monospace(CELL_H), glyph.fg); - } + paint_glyph(ui.painter(), r, &glyph); let label_resp = ui.selectable_label(is_selected, archetype.name()); if cell_resp.clicked() || label_resp.clicked() { @@ -94,17 +88,7 @@ pub(crate) fn show_editor_panel(ctx: &egui::Context, editor: &mut EditorState, _ // Paint a preview cell showing the current glyph, sized like a board cell. let (cell_rect, glyph_btn) = ui.allocate_exact_size(vec2(CELL_W, CELL_H), Sense::click()); - { - let p = ui.painter(); - p.rect_filled(cell_rect, 0.0, editor.glyph.bg); - p.text( - cell_rect.center(), - Align2::CENTER_CENTER, - editor.glyph.ch, - FontId::monospace(CELL_H), - editor.glyph.fg, - ); - } + paint_glyph(ui.painter(), cell_rect, &editor.glyph); if glyph_btn.clicked() { editor.glyph_picker_open = true; } diff --git a/src/glyph_picker.rs b/src/glyph_picker.rs index 4a848c5..07fd07f 100644 --- a/src/glyph_picker.rs +++ b/src/glyph_picker.rs @@ -1,8 +1,8 @@ use eframe::egui; -use egui::{Align2, Color32, FontId, Rect, Sense, Stroke, vec2}; +use egui::{Color32, Rect, Sense, Stroke, vec2}; use crate::game::{Archetype, Glyph}; -use crate::render::{CELL_H, CELL_W, pos_to_cell}; +use crate::render::{CELL_H, CELL_W, paint_glyph, pos_to_cell}; /// Renders the floating glyph-picker dialog and updates `glyph` in place. /// @@ -32,21 +32,16 @@ pub(crate) fn show( .resizable(false) .open(open) .show(ctx, |ui| { - // ----- Board palette ----- ui.label("Board palette"); ui.horizontal_wrapped(|ui| { for &g in &board_glyphs { let (r, resp) = ui.allocate_exact_size(vec2(CELL_W, CELL_H), Sense::click()); - { - let p = ui.painter(); - p.rect_filled(r, 0.0, g.bg); - p.text(r.center(), Align2::CENTER_CENTER, g.ch, - FontId::monospace(CELL_H), g.fg); - if g == *glyph { - p.rect_stroke(r, 0.0, Stroke::new(1.0, Color32::WHITE), - egui::epaint::StrokeKind::Middle); - } + let p = ui.painter(); + paint_glyph(p, r, &g); + if g == *glyph { + p.rect_stroke(r, 0.0, Stroke::new(1.0, Color32::WHITE), + egui::epaint::StrokeKind::Middle); } if resp.clicked() { *glyph = g; @@ -56,7 +51,6 @@ pub(crate) fn show( ui.separator(); - // ----- Colors ----- ui.label("Colors"); ui.horizontal(|ui| { ui.label("FG"); @@ -69,7 +63,6 @@ pub(crate) fn show( ui.separator(); - // ----- Character grid ----- // 16 columns × 6 rows covering printable ASCII 0x20–0x7E. ui.label("Character"); const CHAR_COLS: usize = 16; @@ -79,9 +72,6 @@ pub(crate) fn show( let grid_size = vec2(CHAR_COLS as f32 * CELL_W, rows as f32 * CELL_H); let (grid_rect, grid_resp) = ui.allocate_exact_size(grid_size, Sense::click()); - let fg = glyph.fg; - let bg = glyph.bg; - let cur_ch = glyph.ch; { let p = ui.painter(); for (i, &ch) in chars.iter().enumerate() { @@ -90,10 +80,8 @@ pub(crate) fn show( let tl = grid_rect.min + vec2(col as f32 * CELL_W, row as f32 * CELL_H); let cell = Rect::from_min_size(tl, vec2(CELL_W, CELL_H)); - p.rect_filled(cell, 0.0, bg); - p.text(cell.center(), Align2::CENTER_CENTER, ch, - FontId::monospace(CELL_H), fg); - if ch == cur_ch { + paint_glyph(p, cell, &Glyph { ch, fg: glyph.fg, bg: glyph.bg }); + if ch == glyph.ch { p.rect_stroke(cell, 0.0, Stroke::new(1.0, Color32::WHITE), egui::epaint::StrokeKind::Middle); } diff --git a/src/main.rs b/src/main.rs index a2a35ce..9cddaec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -95,7 +95,7 @@ impl eframe::App for App { // --- Editor side panel --- // Must be declared before CentralPanel so egui allocates its space first. if self.mode == AppMode::Edit { - show_editor_panel(ctx, &mut self.editor, &self.state.board); + show_editor_panel(ctx, &mut self.editor); } // --- Board rendering --- diff --git a/src/render.rs b/src/render.rs index 2ce0621..a91571a 100644 --- a/src/render.rs +++ b/src/render.rs @@ -49,11 +49,17 @@ pub(crate) const MIN_WINDOW_W: f32 = CELL_W * 10.0 + 2.0 * PANEL_MARGIN; /// Minimum window height: enough room for 5 cells plus menu and margins. pub(crate) const MIN_WINDOW_H: f32 = CELL_H * 5.0 + MENU_H + 2.0 * PANEL_MARGIN; -/// Draws a single cell at grid position `(x, y)` using the given glyph. +/// Paints a glyph into an already-allocated `rect`. /// -/// Each cell is rendered as two layers: -/// 1. A filled rectangle in `glyph.bg` covering the full cell area. -/// 2. A monospace character (`glyph.ch`) in `glyph.fg`, centered in the cell. +/// Unlike [`draw_glyph`], this does not compute the rect from grid coordinates — +/// the caller owns the rect (typically from [`egui::Ui::allocate_exact_size`]) +/// and handles interaction. Use this when you need the click [`egui::Response`]. +pub(crate) fn paint_glyph(painter: &egui::Painter, rect: Rect, glyph: &Glyph) { + painter.rect_filled(rect, 0.0, glyph.bg); + painter.text(rect.center(), Align2::CENTER_CENTER, glyph.ch, FontId::monospace(CELL_H), glyph.fg); +} + +/// Draws a single cell at grid position `(x, y)` using the given glyph. /// /// `origin` is the pixel position of cell `(0, 0)` — the top-left corner of /// the board within the panel. Cell positions are computed from `origin` using @@ -61,9 +67,7 @@ pub(crate) const MIN_WINDOW_H: f32 = CELL_H * 5.0 + MENU_H + 2.0 * PANEL_MARGIN; pub(crate) fn draw_glyph(painter: &egui::Painter, origin: Pos2, x: usize, y: usize, glyph: &Glyph) { let tl = origin + vec2(x as f32 * CELL_W, y as f32 * CELL_H); let rect = Rect::from_min_size(tl, vec2(CELL_W, CELL_H)); - let center = rect.center(); - painter.rect_filled(rect, 0.0, glyph.bg); - painter.text(center, Align2::CENTER_CENTER, glyph.ch, FontId::monospace(CELL_H), glyph.fg); + paint_glyph(painter, rect, glyph); } /// Draws all board cells then the player overlay at `origin`.