From 8418bc9bc9f4366534fcd04bc5bb1c0eaa9e9a4d Mon Sep 17 00:00:00 2001 From: Kent Overstreet Date: Sun, 5 Apr 2026 17:59:33 -0400 Subject: [PATCH] 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 --- src/user/event_loop.rs | 65 ++++++++++++++++++++++++++++++++++++------ src/user/mod.rs | 2 +- 2 files changed, 58 insertions(+), 9 deletions(-) diff --git a/src/user/event_loop.rs b/src/user/event_loop.rs index c6df439..ad0c8db 100644 --- a/src/user/event_loop.rs +++ b/src/user/event_loop.rs @@ -380,6 +380,16 @@ pub async fn run( }); } let notify_rx = crate::thalamus::channels::subscribe_all(); + + // Overlay screens (F2-F5). Index 0 = no overlay (interact). + let mut screens: Vec> = 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 reader = EventStream::new(); @@ -387,6 +397,7 @@ pub async fn run( render_interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); let mut dirty = true; let mut prev_mind = shared_mind.lock().unwrap().clone(); + let mut pending_key: Option = None; terminal.hide_cursor()?; @@ -407,15 +418,37 @@ pub async fn run( match maybe_event { Some(Ok(Event::Key(key))) => { if key.kind != KeyEventKind::Press { continue; } - app.handle_key(key); idle_state.user_activity(); - if false { // TODO: check active screen is thalamus - let tx = channel_tx.clone(); - tokio::spawn(async move { - let result = crate::thalamus::channels::fetch_all_channels().await; - let _ = tx.send(result).await; - }); + + // 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(); + tokio::spawn(async move { + let result = crate::thalamus::channels::fetch_all_channels().await; + let _ = tx.send(result).await; + }); + } + 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))) => { @@ -512,7 +545,23 @@ pub async fn run( } if dirty { - terminal.draw(|f| app.draw(f))?; + let key = pending_key.take(); + let mut screen_action = None; + if active_screen == 0 { + 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; } diff --git a/src/user/mod.rs b/src/user/mod.rs index 0304d5e..dcde774 100644 --- a/src/user/mod.rs +++ b/src/user/mod.rs @@ -244,7 +244,7 @@ pub enum ScreenAction { } /// 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, key: Option, app: &App) -> Option; fn label(&self) -> &'static str;