transition

This commit is contained in:
2026-06-13 18:43:29 -05:00
parent 6818f0545a
commit 299570bec8
6 changed files with 254 additions and 20 deletions
+36 -8
View File
@@ -12,6 +12,7 @@ mod log;
mod render;
mod scroll_overlay;
mod term;
mod transition;
mod utils;
mod speech;
mod ui;
@@ -25,7 +26,7 @@ use ratatui::crossterm::event::{
self, DisableMouseCapture, EnableMouseCapture, Event, KeyEventKind,
};
use ratatui::crossterm::execute;
use ratatui::layout::{Constraint, Layout, Spacing};
use ratatui::layout::{Constraint, Layout, Rect, Spacing};
use ratatui::symbols::merge::MergeStrategy;
use ratatui::widgets::Block;
use render::BoardWidget;
@@ -37,6 +38,7 @@ use crate::input::{current_input_mode, handle_board_input, handle_scroll_input,
use crate::log::{log_preview_line, LogWidget};
use crate::scroll_overlay::ScrollOverlayWidget;
use crate::speech::SpeechBubblesWidget;
use crate::transition::TransitionAnimation;
use crate::ui::Ui;
/// Entry point: parse the map path, load the board, then run the play loop with
@@ -130,12 +132,15 @@ fn run(
}
let terminal_h = terminal.size()?.height;
match current_input_mode(game, ui) {
InputMode::Board(log_open) => {
if handle_board_input(event, log_open, terminal_h, game, ui) {
InputMode::Board | InputMode::Frozen => {
if handle_board_input(event, ui.log.open, terminal_h, game, ui) {
return Ok(());
}
ui.post_transition_wait = false;
}
InputMode::Scroll => handle_scroll_input(event, game, ui),
// Wipe is running — discard all events.
InputMode::Transition => {}
}
}
@@ -170,8 +175,7 @@ fn draw(frame: &mut Frame, game: &GameState, ui: &mut Ui) {
.merge_borders(MergeStrategy::Exact);
let inner = block.inner(board_area);
frame.render_widget(block, board_area);
frame.render_widget(BoardWidget::new(board), inner);
frame.render_widget(SpeechBubblesWidget::new(board, &game.speech_bubbles), inner);
draw_board_area(frame, inner, game, ui);
frame.render_stateful_widget(LogWidget::new(&game.log), log_area, &mut ui.log);
} else {
// Panel closed: board fills the screen, latest message in the border.
@@ -179,14 +183,38 @@ fn draw(frame: &mut Frame, game: &GameState, ui: &mut Ui) {
block = block.title_bottom(log_preview_line(&game.log).left_aligned());
let inner = block.inner(frame.area());
frame.render_widget(block, frame.area());
frame.render_widget(BoardWidget::new(board), inner);
frame.render_widget(SpeechBubblesWidget::new(board, &game.speech_bubbles), inner);
draw_board_area(frame, inner, game, ui);
}
// Scroll overlay: drawn last so it sits above all other content.
if let Some(scroll) = &game.active_scroll {
// Suppressed during a wipe — enter_board() already cleared active_scroll.
if ui.transition.is_none() && let Some(scroll) = &game.active_scroll {
// Capture area before the mutable buffer borrow to satisfy the borrow checker.
let area = frame.area();
frame.render_stateful_widget(ScrollOverlayWidget::new(scroll), area, &mut ui.overlay);
}
}
/// Renders the inner board area: either the active wipe animation or the live board.
///
/// If `ui.pending_transition` is set, pre-renders both boards into a new
/// [`TransitionAnimation`] (now that we know the exact `inner` area) and activates it.
fn draw_board_area(frame: &mut Frame, inner: Rect, game: &GameState, ui: &mut Ui) {
// Initialize any pending transition now that we know the exact area.
if let Some((old_rc, new_rc)) = ui.pending_transition.take() {
ui.transition = Some(TransitionAnimation::new(
&old_rc.borrow(),
&new_rc.borrow(),
inner,
));
}
if let Some(ref tr) = ui.transition {
// Wipe in progress: blend old and new board buffers; suppress speech bubbles.
frame.render_widget(tr, inner);
} else {
let board = &game.board();
frame.render_widget(BoardWidget::new(board), inner);
frame.render_widget(SpeechBubblesWidget::new(board, &game.speech_bubbles), inner);
}
}