Files
creaturedex/src/navigation/navigation.rs
T

138 lines
4.0 KiB
Rust

use crate::alloc::string::ToString;
use crate::card::model::Card;
use crate::navigation::inputs::ButtonAction;
use crate::navigation::inputs::Inputs;
use crate::navigation::outputs::Outputs;
use crate::navigation::state::NavigationState;
use crate::peripherals::Peripherals;
use crate::views::error_view::ErrorView;
use crate::views::view::View;
use alloc::boxed::Box;
use alloc::string::String;
use core::error;
use embassy_futures::select::{Either, select};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_time::{Duration, Timer};
pub type Mutex<T> = embassy_sync::mutex::Mutex<CriticalSectionRawMutex, T>;
pub type Signal<T> = embassy_sync::signal::Signal<CriticalSectionRawMutex, T>;
pub static CARD_DATA: Mutex<Option<Card>> = Mutex::new(None);
const DEBOUNCE_DURATION_MILLIS: u64 = 100;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Action {
Button(ButtonAction),
Timer,
}
pub struct NewState {
pub view: View,
pub replace_view: bool,
pub redraw: bool,
}
pub trait Navigable {
fn display(
&self,
outputs: &mut Outputs,
peripherals: &Peripherals,
) -> impl core::future::Future<Output = Result<(), Box<dyn error::Error>>> + Send;
fn handle_input(&self, input: Action) -> impl core::future::Future<Output = NewState> + Send;
}
#[embassy_executor::task]
pub async fn run(mut inputs: Inputs, mut outputs: Outputs, peripherals: &'static Peripherals) {
async fn display_error(
error: String,
state: &mut NavigationState,
outputs: &mut Outputs,
peripherals: &Peripherals,
) -> () {
let error_view = ErrorView {
error: error.to_string(),
};
state.screens.push(View::Error(error_view));
state
.screens
.last()
// Unwrap: we just pushed the state
.unwrap()
.display(outputs, peripherals)
.await
// Unwrap: display will panic on error in error view
.unwrap();
}
log::info!("starting navigation");
let mut state = NavigationState::new();
if let Err(error) = state
.screens
.last()
.unwrap()
.display(&mut outputs, peripherals)
.await
{
display_error(error.to_string(), &mut state, &mut outputs, &peripherals).await;
};
loop {
let selection = select(
inputs.wait_for_press(),
Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)),
)
.await;
let action = match selection {
Either::First(button) => Action::Button(button),
Either::Second(_) => Action::Timer,
};
if Action::Timer != action {
log::info!("Action: {:?}", action);
}
let new_state = if action == Action::Button(ButtonAction::Back) {
if state.screens.len() > 1 {
state.screens.pop();
}
NewState {
view: state.screens.last().expect("no screen on stack").clone(),
replace_view: false,
redraw: true,
}
} else {
let new_state = state
.screens
.last()
.expect("no screen on stack")
.handle_input(action)
.await;
if action == Action::Timer && !new_state.redraw {
continue;
}
new_state
};
if new_state.replace_view {
state.screens.pop();
}
state.screens.push(new_state.view);
if action != Action::Timer || new_state.redraw {
if let Err(error) = state
.screens
.last()
.unwrap()
.display(&mut outputs, peripherals)
.await
{
display_error(error.to_string(), &mut state, &mut outputs, &peripherals).await;
}
}
Timer::after(Duration::from_millis(DEBOUNCE_DURATION_MILLIS)).await;
}
}