From 60b04e6ef38c491576230e0a91a9ecbd09cd5dca Mon Sep 17 00:00:00 2001 From: Ross Andrews Date: Sun, 17 May 2026 18:35:21 -0500 Subject: [PATCH] Simplify main.rs: extract draw_board/pos_to_cell, deduplicate logic - Extract draw_board() to eliminate duplicated board+player render loop in Edit and Play branches - Extract pos_to_cell() to eliminate duplicated click-to-cell floor math in board and char grid handlers - Simplify glyph dedup to use Glyph: PartialEq directly instead of manual key tuples - Merge two identical clicked() handlers in archetype list into one || check - Remove WHAT-explaining comments Co-Authored-By: Claude Sonnet 4.6 --- src/main.rs | 97 +++++++++++++++++++---------------------------------- 1 file changed, 35 insertions(+), 62 deletions(-) diff --git a/src/main.rs b/src/main.rs index 52f32a5..0071b79 100644 --- a/src/main.rs +++ b/src/main.rs @@ -173,13 +173,9 @@ impl eframe::App for App { p.text(r.center(), Align2::CENTER_CENTER, glyph.ch, FontId::monospace(CELL_H), glyph.fg); } - if cell_resp.clicked() { - self.editor.selected = archetype; - self.editor.glyph = archetype.default_glyph(); - } - if ui.selectable_label(is_selected, archetype.name()) - .clicked() - { + let label_resp = + ui.selectable_label(is_selected, archetype.name()); + if cell_resp.clicked() || label_resp.clicked() { self.editor.selected = archetype; self.editor.glyph = archetype.default_glyph(); } @@ -227,29 +223,10 @@ impl eframe::App for App { // overflows the viewport, showing scroll bars as needed. egui::ScrollArea::new([true, true]).show(ui, |ui| { let (rect, response) = ui.allocate_exact_size(board_px, Sense::click()); - { - let board = &self.state.board; - let painter = ui.painter(); - for y in 0..board.height { - for x in 0..board.width { - let (glyph, _) = board.get(x, y); - draw_glyph(painter, rect.min, x, y, glyph); - } - } - let player_glyph = Glyph::player(); - draw_glyph( - painter, - rect.min, - board.player.x as usize, - board.player.y as usize, - &player_glyph, - ); - } + draw_board(ui.painter(), rect.min, &self.state.board); if response.clicked() { if let Some(pos) = response.interact_pointer_pos() { - let rel = pos - rect.min; - let cx = (rel.x / CELL_W).floor() as i32; - let cy = (rel.y / CELL_H).floor() as i32; + let (cx, cy) = pos_to_cell(rect.min, pos); // floor() keeps out-of-bounds negatives negative; caught here. if cx >= 0 && cy >= 0 { let cx = cx as usize; @@ -268,27 +245,7 @@ impl eframe::App for App { let available = ui.available_rect_before_wrap(); let player = self.state.board.player; let origin = board_origin(available, board_w, board_h, player); - { - let board = &self.state.board; - let painter = ui.painter(); - for y in 0..board.height { - for x in 0..board.width { - let (glyph, _) = board.get(x, y); - draw_glyph(painter, origin, x, y, glyph); - } - } - // Draw the player on top of the board. The player is not stored as - // a board cell, so it is rendered as a separate overlay. This will - // change once the player becomes a scripted object. - let player_glyph = Glyph::player(); - draw_glyph( - painter, - origin, - board.player.x as usize, - board.player.y as usize, - &player_glyph, - ); - } + draw_board(ui.painter(), origin, &self.state.board); } }); @@ -298,16 +255,13 @@ impl eframe::App for App { // Collect unique glyphs from the board before the window closure // borrows self mutably, to avoid a conflict with self.state.board. let board_glyphs: Vec = { - let mut seen: Vec<(u32, [u8; 4], [u8; 4])> = Vec::new(); - let mut out: Vec = Vec::new(); + let mut seen: Vec = Vec::new(); for (glyph, _) in &self.state.board.cells { - let key = (glyph.ch as u32, glyph.fg.to_array(), glyph.bg.to_array()); - if !seen.contains(&key) { - seen.push(key); - out.push(*glyph); + if !seen.contains(glyph) { + seen.push(*glyph); } } - out + seen }; // Use a local bool so the window's close button can write to it @@ -365,7 +319,6 @@ impl eframe::App for App { 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()); - // Copy colors and selected char before painter borrow. let fg = self.editor.glyph.fg; let bg = self.editor.glyph.bg; let cur_ch = self.editor.glyph.ch; @@ -388,17 +341,14 @@ impl eframe::App for App { } if grid_resp.clicked() { if let Some(pos) = grid_resp.interact_pointer_pos() { - let rel = pos - grid_rect.min; - let col = (rel.x / CELL_W).floor() as usize; - let row = (rel.y / CELL_H).floor() as usize; - let idx = row * CHAR_COLS + col; + let (col, row) = pos_to_cell(grid_rect.min, pos); + let idx = row as usize * CHAR_COLS + col as usize; if let Some(&ch) = chars.get(idx) { self.editor.glyph.ch = ch; } } } }); - // Window's close button sets picker_open to false. self.editor.glyph_picker_open = picker_open; } @@ -425,6 +375,29 @@ fn draw_glyph(painter: &egui::Painter, origin: Pos2, x: usize, y: usize, glyph: painter.text(center, Align2::CENTER_CENTER, glyph.ch, FontId::monospace(CELL_H), glyph.fg); } +/// Draws all board cells then the player overlay at `origin`. +fn draw_board(painter: &egui::Painter, origin: Pos2, board: &game::Board) { + for y in 0..board.height { + for x in 0..board.width { + let (glyph, _) = board.get(x, y); + draw_glyph(painter, origin, x, y, glyph); + } + } + // The player is not a board cell; it is rendered on top as an overlay. + // This separate draw will be removed once the player becomes a scripted object. + let player_glyph = Glyph::player(); + draw_glyph(painter, origin, board.player.x as usize, board.player.y as usize, &player_glyph); +} + +/// Converts a pixel position to cell coordinates relative to `origin`. +/// +/// Uses floor so positions above or left of `origin` produce negative values — +/// callers must guard `>= 0` before treating the result as a valid cell index. +fn pos_to_cell(origin: Pos2, pos: Pos2) -> (i32, i32) { + let rel = pos - origin; + ((rel.x / CELL_W).floor() as i32, (rel.y / CELL_H).floor() as i32) +} + /// Computes the pixel position of board cell (0, 0) within `available`. /// /// If the board fits along an axis, it is centered. If it overflows, the