diff --git a/src/main.rs b/src/main.rs index 21a8128..7c6154f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -137,63 +137,76 @@ impl eframe::App for App { // The board is drawn using the egui Painter API (direct 2D drawing), // not egui widgets. This gives pixel-level control over placement. egui::CentralPanel::default().show(ctx, |ui| { - let available = ui.available_rect_before_wrap(); + let board_w = self.state.board.width; + let board_h = self.state.board.height; + let board_px = vec2(board_w as f32 * CELL_W, board_h as f32 * CELL_H); - // Extract board dimensions and player position before the immutable - // borrow scope so `board_origin` can use them without conflicting - // with the later mutable borrow for click-to-paint. - let (board_w, board_h) = (self.state.board.width, self.state.board.height); - let player = self.state.board.player; - let origin = board_origin(available, board_w, board_h, player); - - // Draw board and player. Immutable borrow of `board` ends after - // this block, freeing `self.state.board` for mutation below. - { - let board = &self.state.board; - let painter = ui.painter(); - - // Draw each board cell: filled background rect, then the character. - 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, - ); - } - - // In Edit mode, capture clicks on the viewport and paint cells. if self.mode == AppMode::Edit { - let response = ui.allocate_rect(available, Sense::click()); - if response.clicked() { - if let Some(pos) = response.interact_pointer_pos() { - // Convert pixel position to cell coordinates using the - // same origin computed for rendering above. - let rel = pos - origin; - let cx = (rel.x / CELL_W).floor() as i32; - let cy = (rel.y / CELL_H).floor() as i32; - // floor() keeps out-of-bounds negatives negative; caught here. - if cx >= 0 && cy >= 0 { - let cx = cx as usize; - let cy = cy as usize; - let board = &mut self.state.board; - if cx < board.width && cy < board.height { - let arch = self.editor.selected; - *board.get_mut(cx, cy) = (arch.default_glyph(), arch); + // Edit mode: ScrollArea lets the user scroll when the board + // 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, + ); + } + 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; + // floor() keeps out-of-bounds negatives negative; caught here. + if cx >= 0 && cy >= 0 { + let cx = cx as usize; + let cy = cy as usize; + let board = &mut self.state.board; + if cx < board.width && cy < board.height { + let arch = self.editor.selected; + *board.get_mut(cx, cy) = (arch.default_glyph(), arch); + } } } } + }); + } else { + // Play mode: player-centered viewport, no scroll bars. + 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, + ); } } });