Add scroll bars to Edit mode when board overflows viewport
Edit mode now wraps the board in a ScrollArea so horizontal/vertical scroll bars appear when the board is larger than the window. Play mode keeps the existing player-centered viewport with no scroll bars. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+65
-52
@@ -137,63 +137,76 @@ impl eframe::App for App {
|
|||||||
// The board is drawn using the egui Painter API (direct 2D drawing),
|
// The board is drawn using the egui Painter API (direct 2D drawing),
|
||||||
// not egui widgets. This gives pixel-level control over placement.
|
// not egui widgets. This gives pixel-level control over placement.
|
||||||
egui::CentralPanel::default().show(ctx, |ui| {
|
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 {
|
if self.mode == AppMode::Edit {
|
||||||
let response = ui.allocate_rect(available, Sense::click());
|
// Edit mode: ScrollArea lets the user scroll when the board
|
||||||
if response.clicked() {
|
// overflows the viewport, showing scroll bars as needed.
|
||||||
if let Some(pos) = response.interact_pointer_pos() {
|
egui::ScrollArea::new([true, true]).show(ui, |ui| {
|
||||||
// Convert pixel position to cell coordinates using the
|
let (rect, response) = ui.allocate_exact_size(board_px, Sense::click());
|
||||||
// same origin computed for rendering above.
|
{
|
||||||
let rel = pos - origin;
|
let board = &self.state.board;
|
||||||
let cx = (rel.x / CELL_W).floor() as i32;
|
let painter = ui.painter();
|
||||||
let cy = (rel.y / CELL_H).floor() as i32;
|
for y in 0..board.height {
|
||||||
// floor() keeps out-of-bounds negatives negative; caught here.
|
for x in 0..board.width {
|
||||||
if cx >= 0 && cy >= 0 {
|
let (glyph, _) = board.get(x, y);
|
||||||
let cx = cx as usize;
|
draw_glyph(painter, rect.min, x, y, glyph);
|
||||||
let cy = cy as usize;
|
}
|
||||||
let board = &mut self.state.board;
|
}
|
||||||
if cx < board.width && cy < board.height {
|
let player_glyph = Glyph::player();
|
||||||
let arch = self.editor.selected;
|
draw_glyph(
|
||||||
*board.get_mut(cx, cy) = (arch.default_glyph(), arch);
|
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,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user