event_loop: wire F-key screen switching with ScreenView

Create overlay screens vec (ConsciousScreen, SubconsciousScreen,
UnconsciousScreen, ThalamusScreen). F-keys switch active_screen.
Screen tick() called during render phase with pending key event.
Screen actions (Switch, Hotkey) applied after draw.

Interact (F1) still draws via App::draw_main(). Overlay screens
draw via ScreenView::tick(). State persists across switches.

Co-Authored-By: Kent Overstreet <kent.overstreet@linux.dev>
This commit is contained in:
Kent Overstreet 2026-04-05 17:59:33 -04:00
parent 927cddd864
commit 8418bc9bc9
2 changed files with 58 additions and 9 deletions

View file

@ -380,6 +380,16 @@ pub async fn run(
}); });
} }
let notify_rx = crate::thalamus::channels::subscribe_all(); let notify_rx = crate::thalamus::channels::subscribe_all();
// Overlay screens (F2-F5). Index 0 = no overlay (interact).
let mut screens: Vec<Box<dyn tui::ScreenView>> = vec![
Box::new(crate::user::context::ConsciousScreen::new()),
Box::new(crate::user::subconscious::SubconsciousScreen::new()),
Box::new(crate::user::unconscious::UnconsciousScreen::new()),
Box::new(crate::user::thalamus::ThalamusScreen::new()),
];
let mut active_screen: usize = 0; // 0 = interact, 1-4 = overlay index + 1
let mut terminal = tui::init_terminal()?; let mut terminal = tui::init_terminal()?;
let mut reader = EventStream::new(); let mut reader = EventStream::new();
@ -387,6 +397,7 @@ pub async fn run(
render_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); render_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip);
let mut dirty = true; let mut dirty = true;
let mut prev_mind = shared_mind.lock().unwrap().clone(); let mut prev_mind = shared_mind.lock().unwrap().clone();
let mut pending_key: Option<ratatui::crossterm::event::KeyEvent> = None;
terminal.hide_cursor()?; terminal.hide_cursor()?;
@ -407,9 +418,16 @@ pub async fn run(
match maybe_event { match maybe_event {
Some(Ok(Event::Key(key))) => { Some(Ok(Event::Key(key))) => {
if key.kind != KeyEventKind::Press { continue; } if key.kind != KeyEventKind::Press { continue; }
app.handle_key(key);
idle_state.user_activity(); idle_state.user_activity();
if false { // TODO: check active screen is thalamus
// F-keys switch screens
if let ratatui::crossterm::event::KeyCode::F(n) = key.code {
active_screen = match n {
1 => 0,
n @ 2..=5 => n as usize - 1,
_ => active_screen,
};
if active_screen == 4 { // thalamus — refresh channels
let tx = channel_tx.clone(); let tx = channel_tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let result = crate::thalamus::channels::fetch_all_channels().await; let result = crate::thalamus::channels::fetch_all_channels().await;
@ -417,6 +435,21 @@ pub async fn run(
}); });
} }
dirty = true; dirty = true;
continue;
}
// App handles global keys (Ctrl combos) + interact keys
if active_screen == 0 {
app.handle_key(key);
} else {
// Global keys only
app.handle_key(key);
// Screen gets the key on next render tick
}
// Store pending key for active overlay screen
pending_key = Some(key);
dirty = true;
} }
Some(Ok(Event::Mouse(mouse))) => { Some(Ok(Event::Mouse(mouse))) => {
app.handle_mouse(mouse); app.handle_mouse(mouse);
@ -512,7 +545,23 @@ pub async fn run(
} }
if dirty { if dirty {
let key = pending_key.take();
let mut screen_action = None;
if active_screen == 0 {
terminal.draw(|f| app.draw(f))?; terminal.draw(|f| app.draw(f))?;
} else {
let screen = &mut screens[active_screen - 1];
terminal.draw(|f| {
let area = f.area();
screen_action = screen.tick(f, area, key, &app);
})?;
}
if let Some(action) = screen_action {
match action {
tui::ScreenAction::Switch(i) => { active_screen = i; dirty = true; continue; }
tui::ScreenAction::Hotkey(h) => app.hotkey_actions.push(h),
}
}
dirty = false; dirty = false;
} }

View file

@ -244,7 +244,7 @@ pub enum ScreenAction {
} }
/// A screen that can draw itself and handle input. /// A screen that can draw itself and handle input.
pub(crate) trait ScreenView { pub(crate) trait ScreenView: Send {
fn tick(&mut self, frame: &mut ratatui::Frame, area: ratatui::layout::Rect, fn tick(&mut self, frame: &mut ratatui::Frame, area: ratatui::layout::Rect,
key: Option<ratatui::crossterm::event::KeyEvent>, app: &App) -> Option<ScreenAction>; key: Option<ratatui::crossterm::event::KeyEvent>, app: &App) -> Option<ScreenAction>;
fn label(&self) -> &'static str; fn label(&self) -> &'static str;