editor ui
This commit is contained in:
+62
-33
@@ -10,19 +10,31 @@
|
||||
mod animation;
|
||||
mod cp437;
|
||||
mod editor;
|
||||
mod input;
|
||||
mod log;
|
||||
mod menu;
|
||||
mod mode;
|
||||
mod overlay;
|
||||
mod render;
|
||||
mod scroll_overlay;
|
||||
mod speech;
|
||||
mod term;
|
||||
mod transition;
|
||||
mod utils;
|
||||
mod speech;
|
||||
mod ui;
|
||||
mod input;
|
||||
mod utils;
|
||||
|
||||
use crate::animation::{AnimWidget, AnimationLayer};
|
||||
use crate::editor::{EditorState, draw_editor, handle_dialog_input};
|
||||
use crate::input::{
|
||||
InputMode, current_input_mode, handle_board_input, handle_editor_input, handle_menu_input,
|
||||
handle_scroll_input,
|
||||
};
|
||||
use crate::log::{LogWidget, log_preview_line};
|
||||
use crate::menu::MenuWidget;
|
||||
use crate::mode::{Mode, PendingMode};
|
||||
use crate::scroll_overlay::ScrollOverlayWidget;
|
||||
use crate::speech::SpeechBubblesWidget;
|
||||
use crate::ui::Ui;
|
||||
use kiln_core::game::GameState;
|
||||
use kiln_core::log::LogLine;
|
||||
use kiln_core::world;
|
||||
@@ -39,18 +51,6 @@ use std::io;
|
||||
use std::process::ExitCode;
|
||||
use std::time::{Duration, Instant};
|
||||
use term::TerminalCaps;
|
||||
use crate::animation::{AnimationLayer, AnimWidget};
|
||||
use crate::editor::{draw_editor, EditorState};
|
||||
use crate::input::{
|
||||
current_input_mode, handle_board_input, handle_editor_input, handle_menu_input,
|
||||
handle_scroll_input, InputMode,
|
||||
};
|
||||
use crate::log::{log_preview_line, LogWidget};
|
||||
use crate::menu::MenuWidget;
|
||||
use crate::mode::{Mode, PendingMode};
|
||||
use crate::scroll_overlay::ScrollOverlayWidget;
|
||||
use crate::speech::SpeechBubblesWidget;
|
||||
use crate::ui::Ui;
|
||||
|
||||
/// Entry point: parse the map path, load the board, then run the play loop with
|
||||
/// the terminal in raw/alternate-screen mode (restored on every exit path).
|
||||
@@ -96,7 +96,10 @@ fn main() -> ExitCode {
|
||||
let mut mode = Mode::Play(game);
|
||||
|
||||
// Remember the world path so the editor (and play-reload) can reload from it.
|
||||
let mut ui = Ui { world_path: path.clone(), ..Ui::default() };
|
||||
let mut ui = Ui {
|
||||
world_path: path.clone(),
|
||||
..Ui::default()
|
||||
};
|
||||
let result = run(&mut terminal, &mut mode, &mut ui);
|
||||
|
||||
if caps.keyboard_enhancement {
|
||||
@@ -121,11 +124,7 @@ const FRAME: Duration = Duration::from_nanos(1_000_000_000 / 30);
|
||||
/// only until the next frame deadline (`event::poll`), then advances the game by
|
||||
/// the real time elapsed since the last tick. `poll` returns the instant a key
|
||||
/// arrives, so input stays responsive while the game keeps ticking on its own.
|
||||
fn run(
|
||||
terminal: &mut ratatui::DefaultTerminal,
|
||||
mode: &mut Mode,
|
||||
ui: &mut Ui,
|
||||
) -> io::Result<()> {
|
||||
fn run(terminal: &mut ratatui::DefaultTerminal, mode: &mut Mode, ui: &mut Ui) -> io::Result<()> {
|
||||
let mut last_tick = Instant::now();
|
||||
loop {
|
||||
// Redraw every iteration; ratatui diffs against the previous frame so
|
||||
@@ -158,14 +157,18 @@ fn run(
|
||||
handle_board_input(event, ui.log.open, size.height, game, ui);
|
||||
}
|
||||
InputMode::Scroll => handle_scroll_input(event, game, ui),
|
||||
_ => unreachable!("Menu and Ignore handled above; Editor input mode only occurs in Edit mode"),
|
||||
_ => unreachable!(
|
||||
"Menu and Ignore handled above; Editor input mode only occurs in Edit mode"
|
||||
),
|
||||
},
|
||||
Mode::Edit(ed) => match input_mode {
|
||||
InputMode::Editor => handle_editor_input(event, size.width, ed, ui),
|
||||
_ => unreachable!("Menu and Ignore handled above; Board/Scroll/Frozen input modes only occur in Play mode")
|
||||
InputMode::Dialog => handle_dialog_input(event, ed),
|
||||
_ => unreachable!(
|
||||
"Menu and Ignore handled above; Board/Scroll/Frozen input modes only occur in Play mode"
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +197,9 @@ fn run(
|
||||
/// [`GameState`] (so it reflects any edits) and re-runs object `init()` hooks. A
|
||||
/// load failure is logged to the play game (if any) and leaves the mode unchanged.
|
||||
fn apply_pending_mode(mode: &mut Mode, ui: &mut Ui) {
|
||||
let Some(pending) = ui.pending_mode.take() else { return };
|
||||
let Some(pending) = ui.pending_mode.take() else {
|
||||
return;
|
||||
};
|
||||
match world::load(&ui.world_path) {
|
||||
Ok(world) => match pending {
|
||||
PendingMode::EnterEditor => *mode = Mode::Edit(EditorState::new(world)),
|
||||
@@ -225,19 +230,38 @@ fn draw(frame: &mut Frame, mode: &mut Mode, ui: &mut Ui) {
|
||||
}
|
||||
|
||||
// Overlay layer: animation > menu > scroll (mutually exclusive at runtime).
|
||||
let is_overlay_anim = ui.active_animation.as_ref()
|
||||
let is_overlay_anim = ui
|
||||
.active_animation
|
||||
.as_ref()
|
||||
.map(|a| matches!(a.layer(), AnimationLayer::Overlay))
|
||||
.unwrap_or(false);
|
||||
|
||||
let area = frame.area();
|
||||
if is_overlay_anim {
|
||||
frame.render_widget(AnimWidget(ui.active_animation.as_ref().unwrap().as_ref()), area);
|
||||
frame.render_widget(
|
||||
AnimWidget(ui.active_animation.as_ref().unwrap().as_ref()),
|
||||
area,
|
||||
);
|
||||
} else if let Some(menu) = ui.menu.as_mut() {
|
||||
frame.render_stateful_widget(MenuWidget, area, menu);
|
||||
} else if let Mode::Play(game) = mode {
|
||||
// The scroll window only exists while playing.
|
||||
if let Some(scroll) = &game.active_scroll {
|
||||
frame.render_stateful_widget(ScrollOverlayWidget::new(&scroll.lines), area, &mut ui.overlay);
|
||||
} else {
|
||||
match mode {
|
||||
// The scroll window only exists while playing.
|
||||
Mode::Play(game) => {
|
||||
if let Some(scroll) = &game.active_scroll {
|
||||
frame.render_stateful_widget(
|
||||
ScrollOverlayWidget::new(&scroll.lines),
|
||||
area,
|
||||
&mut ui.overlay,
|
||||
);
|
||||
}
|
||||
}
|
||||
// The dialog overlay only exists while editing.
|
||||
Mode::Edit(ed) => {
|
||||
if let Some(d) = &ed.dialog {
|
||||
d.draw(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -284,13 +308,18 @@ fn draw_board_area(frame: &mut Frame, inner: Rect, game: &GameState, ui: &mut Ui
|
||||
ui.start_transition(&old_rc.borrow(), &new_rc.borrow(), inner);
|
||||
}
|
||||
|
||||
let is_board_anim = ui.active_animation.as_ref()
|
||||
let is_board_anim = ui
|
||||
.active_animation
|
||||
.as_ref()
|
||||
.map(|a| matches!(a.layer(), AnimationLayer::Board))
|
||||
.unwrap_or(false);
|
||||
|
||||
if is_board_anim {
|
||||
// Board-layer animation (wipe): replaces the board entirely.
|
||||
frame.render_widget(AnimWidget(ui.active_animation.as_ref().unwrap().as_ref()), inner);
|
||||
frame.render_widget(
|
||||
AnimWidget(ui.active_animation.as_ref().unwrap().as_ref()),
|
||||
inner,
|
||||
);
|
||||
} else {
|
||||
let board = &game.board();
|
||||
frame.render_widget(BoardWidget::new(board), inner);
|
||||
|
||||
Reference in New Issue
Block a user