scrolls
This commit is contained in:
+22
-6
@@ -53,6 +53,8 @@ struct Ui {
|
||||
scroll: u16,
|
||||
/// Vertical scroll offset within an active scroll overlay.
|
||||
scroll_offset: u16,
|
||||
/// Maximum valid value for `scroll_offset`; updated each frame by the renderer.
|
||||
scroll_max: u16,
|
||||
/// Animation state for the scroll overlay; `None` when no overlay is active.
|
||||
scroll_anim: Option<ScrollAnimState>,
|
||||
}
|
||||
@@ -64,11 +66,20 @@ impl Default for Ui {
|
||||
log_height: 10,
|
||||
scroll: 0,
|
||||
scroll_offset: 0,
|
||||
scroll_max: 0,
|
||||
scroll_anim: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Ui {
|
||||
/// Scrolls the overlay by `delta` lines, clamped to the valid range.
|
||||
fn scroll_lines(&mut self, delta: i32) {
|
||||
self.scroll_offset = (self.scroll_offset as i32 + delta)
|
||||
.clamp(0, self.scroll_max as i32) as u16;
|
||||
}
|
||||
}
|
||||
|
||||
/// Starts the scroll-close animation with the optional player choice.
|
||||
/// Uses the current opening progress if the overlay was still animating in.
|
||||
fn begin_close(ui: &mut Ui, choice: Option<String>) {
|
||||
@@ -197,8 +208,8 @@ fn run(
|
||||
// Esc dismisses the scroll only when there are no choices;
|
||||
// with choices present the player must select one.
|
||||
KeyCode::Esc if !has_choices => begin_close(ui, None),
|
||||
KeyCode::Up => ui.scroll_offset = ui.scroll_offset.saturating_sub(1),
|
||||
KeyCode::Down => ui.scroll_offset = ui.scroll_offset.saturating_add(1),
|
||||
KeyCode::Up => ui.scroll_lines(-1),
|
||||
KeyCode::Down => ui.scroll_lines(1),
|
||||
KeyCode::Char(c) => {
|
||||
if let Some(ch) = resolve_choice(&scroll.lines, c) {
|
||||
begin_close(ui, Some(ch));
|
||||
@@ -233,8 +244,8 @@ fn run(
|
||||
&& !matches!(ui.scroll_anim, Some(ScrollAnimState::Closing { .. }));
|
||||
if scroll_active {
|
||||
match m.kind {
|
||||
MouseEventKind::ScrollUp => ui.scroll_offset = ui.scroll_offset.saturating_sub(1),
|
||||
MouseEventKind::ScrollDown => ui.scroll_offset = ui.scroll_offset.saturating_add(1),
|
||||
MouseEventKind::ScrollUp => ui.scroll_lines(-1),
|
||||
MouseEventKind::ScrollDown => ui.scroll_lines(1),
|
||||
_ => {}
|
||||
}
|
||||
} else if ui.log_open {
|
||||
@@ -278,6 +289,7 @@ fn run(
|
||||
game.close_scroll(ch.as_deref());
|
||||
ui.scroll_anim = None;
|
||||
ui.scroll_offset = 0;
|
||||
ui.scroll_max = 0;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@@ -306,7 +318,11 @@ fn scroll_log(ui: &mut Ui, game: &GameState, delta: i32) {
|
||||
/// panel across the bottom. When the panel is closed the latest log message is
|
||||
/// previewed in the board's bottom-left border after a `[l]og:` label.
|
||||
/// A scroll overlay is drawn on top of everything when active.
|
||||
fn draw(frame: &mut Frame, game: &GameState, ui: &Ui) {
|
||||
///
|
||||
/// TODO: `draw` mutates `ui.scroll_max` as a side-effect of rendering. This is a wart —
|
||||
/// ideally render functions are pure and all state lives in the game loop. The max scroll
|
||||
/// should be computed independently of drawing and stored before the draw call.
|
||||
fn draw(frame: &mut Frame, game: &GameState, ui: &mut Ui) {
|
||||
// Borrow the board for the duration of the frame (no script runs during draw).
|
||||
let board = game.board();
|
||||
let board = &*board;
|
||||
@@ -360,7 +376,7 @@ fn draw(frame: &mut Frame, game: &GameState, ui: &Ui) {
|
||||
};
|
||||
// Capture area before the mutable buffer borrow to satisfy the borrow checker.
|
||||
let area = frame.area();
|
||||
render::draw_scroll_overlay(frame.buffer_mut(), area, scroll, ui.scroll_offset, anim);
|
||||
ui.scroll_max = render::draw_scroll_overlay(frame.buffer_mut(), area, scroll, ui.scroll_offset, anim);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user