Skip to main content

romm_cli/tui/app/
run.rs

1//! Main TUI event loop.
2
3use anyhow::Result;
4
5use crate::tui::runtime::{LoopControl, RuntimeOptions, TuiSession};
6
7use super::event::{Action, AppEvent};
8use super::App;
9
10impl App {
11    pub async fn run(&mut self) -> Result<()> {
12        let mut session = TuiSession::enter(RuntimeOptions::default())?;
13        let mut quit = false;
14
15        while !quit {
16            let events = self.poll_frame_events()?;
17            for event in events {
18                for action in self.map_event(event) {
19                    if self.update(action).await? {
20                        quit = true;
21                        break;
22                    }
23                }
24                if quit {
25                    break;
26                }
27            }
28
29            session.terminal_mut().draw(|f| self.render(f))?;
30
31            if self.on_frame_end().await? == LoopControl::Break {
32                quit = true;
33            }
34        }
35
36        session.leave()?;
37        Ok(())
38    }
39
40    fn poll_frame_events(&mut self) -> Result<Vec<AppEvent>> {
41        let mut events = self.drain_background_events();
42        if self
43            .startup_splash
44            .as_ref()
45            .is_some_and(|s| s.should_auto_dismiss())
46        {
47            events.push(AppEvent::AutoDismissSplash);
48        }
49
50        if self
51            .startup_update_prompt
52            .as_ref()
53            .is_some_and(|p| p.updating)
54        {
55            return Ok(events);
56        }
57
58        if crossterm::event::poll(std::time::Duration::from_millis(100))? {
59            match crossterm::event::read()? {
60                crossterm::event::Event::Key(key) => events.push(AppEvent::Key(key)),
61                crossterm::event::Event::Paste(text) => events.push(AppEvent::Paste(text)),
62                _ => {}
63            }
64        }
65        Ok(events)
66    }
67
68    async fn on_frame_end(&mut self) -> Result<LoopControl> {
69        if self
70            .startup_update_prompt
71            .as_ref()
72            .is_some_and(|p| p.updating)
73        {
74            self.update(Action::ApplyStartupUpdate).await?;
75            return Ok(LoopControl::Continue);
76        }
77        self.update(Action::ProcessDeferredRomLoad).await?;
78        Ok(LoopControl::Continue)
79    }
80}