use std::time::Duration;
use crate::backend::Backend;
use crate::event::{Rect, UiEvent};
use crate::types::WidgetId;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum Reaction {
Continue,
Redraw,
RedrawAfter(Duration),
Exit,
}
impl Reaction {
#[cfg_attr(not(any(feature = "tui", feature = "gtk")), allow(dead_code))]
pub(crate) fn merge(self, next: Reaction) -> Reaction {
match (self, next) {
(Reaction::Exit, _) | (_, Reaction::Exit) => Reaction::Exit,
(Reaction::Redraw, _) | (_, Reaction::Redraw) => Reaction::Redraw,
(Reaction::RedrawAfter(a), Reaction::RedrawAfter(b)) => Reaction::RedrawAfter(a.min(b)),
(Reaction::RedrawAfter(d), Reaction::Continue)
| (Reaction::Continue, Reaction::RedrawAfter(d)) => Reaction::RedrawAfter(d),
(Reaction::Continue, Reaction::Continue) => Reaction::Continue,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum StepOutcome {
Continue,
Exited,
}
pub trait AppLogic {
type AreaId: Copy + Eq + std::fmt::Debug + Default;
fn setup(&mut self, _backend: &mut dyn Backend) {}
fn render(&self, backend: &mut dyn Backend, area: Self::AreaId);
fn handle(&mut self, event: UiEvent, backend: &mut dyn Backend) -> Reaction;
fn tick(&mut self, _backend: &mut dyn Backend) -> Reaction {
Reaction::Continue
}
fn natural_scroll(&self) -> bool {
false
}
fn tab_stops(&self, _area: Self::AreaId) -> Vec<(WidgetId, Rect)> {
Vec::new()
}
}
#[cfg(test)]
mod reaction_merge_tests {
use super::*;
#[test]
fn exit_beats_everything_either_order() {
assert_eq!(Reaction::Continue.merge(Reaction::Exit), Reaction::Exit);
assert_eq!(Reaction::Exit.merge(Reaction::Redraw), Reaction::Exit);
assert_eq!(
Reaction::RedrawAfter(Duration::from_millis(5)).merge(Reaction::Exit),
Reaction::Exit
);
}
#[test]
fn redraw_beats_redraw_after_either_order() {
assert_eq!(
Reaction::RedrawAfter(Duration::from_millis(5)).merge(Reaction::Redraw),
Reaction::Redraw
);
assert_eq!(
Reaction::Redraw.merge(Reaction::RedrawAfter(Duration::from_millis(5))),
Reaction::Redraw
);
}
#[test]
fn continue_is_the_identity() {
assert_eq!(
Reaction::Continue.merge(Reaction::Continue),
Reaction::Continue
);
assert_eq!(
Reaction::Continue.merge(Reaction::RedrawAfter(Duration::from_millis(5))),
Reaction::RedrawAfter(Duration::from_millis(5))
);
}
#[test]
fn two_redraw_afters_keep_the_earlier_deadline_regardless_of_arrival_order() {
let long = Reaction::RedrawAfter(Duration::from_millis(100));
let short = Reaction::RedrawAfter(Duration::from_millis(5));
assert_eq!(long.merge(short), short);
assert_eq!(short.merge(long), short);
}
}