pub mod browse;
pub mod editor;
pub mod settings;
pub mod start;
use async_trait::async_trait;
use ratatui::Frame;
use crate::components::event_state::EventState;
use crate::components::events::{AppEvent, AppTx, InputEvent};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScreenKind {
Start,
Browse,
Editor,
Settings,
}
#[async_trait]
pub trait AppScreen: Send {
async fn on_enter(&mut self, _tx: &AppTx) {}
fn handle_input(&mut self, event: &InputEvent, tx: &AppTx) -> EventState;
fn render(&mut self, f: &mut Frame);
async fn handle_app_message(&mut self, msg: AppEvent, _tx: &AppTx) -> Option<AppEvent> {
Some(msg)
}
fn get_kind(&self) -> ScreenKind;
async fn on_exit(&mut self, _tx: &AppTx) {}
}
#[cfg(test)]
mod tests {
use tokio::sync::mpsc::unbounded_channel;
use super::*;
use crate::app_screen::settings::SettingsScreen;
use crate::settings::AppSettings;
#[tokio::test]
async fn non_editor_screen_passes_focus_message_back() {
let (tx, _rx) = unbounded_channel::<AppEvent>();
let mut screen = SettingsScreen::new(AppSettings::default());
let result = screen.handle_app_message(AppEvent::FocusSidebar, &tx).await;
assert!(
result.is_some(),
"SettingsScreen should not consume FocusSidebar"
);
}
#[tokio::test]
async fn non_editor_screen_passes_focus_editor_message_back() {
let (tx, _rx) = unbounded_channel::<AppEvent>();
let mut screen = SettingsScreen::new(AppSettings::default());
let result = screen.handle_app_message(AppEvent::FocusEditor, &tx).await;
assert!(
result.is_some(),
"SettingsScreen should not consume FocusEditor"
);
}
#[tokio::test]
async fn on_exit_default_is_noop() {
let (tx, _rx) = unbounded_channel::<AppEvent>();
let mut screen = SettingsScreen::new(AppSettings::default());
screen.on_exit(&tx).await; }
}