use crate::{ event::{AppEvent, Event, EventHandler}, janet, }; use janet_binds::{CFunction, VM, janet_wrap_cfunction}; use lexopt::Arg; use ratatui::{ DefaultTerminal, crossterm::event::{KeyCode, KeyEvent}, }; static RUNTIME_IMAGE: &[u8] = include_bytes!("../runtime.jimage"); /// Application. #[derive(Debug)] pub struct App { /// Is the application running? pub running: bool, pub text: String, /// Janet Context pub janet: VM, } impl Default for App { fn default() -> Self { Self::new() } } struct Argument { pub r#type: String, pub value: String, } impl App { /// Constructs a new instance of [`App`]. pub fn new() -> Self { let mut janet = VM::new().unwrap(); janet .register_cfunction(CFunction::new( "koa/actions/insert-char", janet_wrap_cfunction!(janet::keymap::koa_insert_char).raw(), )) .unwrap(); janet .register_cfunction(CFunction::new( "koa/actions/exit", janet_wrap_cfunction!(janet::state::koa_exit).raw(), )) .unwrap(); janet.load_image(RUNTIME_IMAGE).unwrap(); let mut parser = lexopt::Parser::from_env(); let mut args = vec![]; while let Some(arg) = parser.next().unwrap() { match arg { Arg::Short(short) => args.push(Argument { r#type: "short".to_string(), value: short.to_string(), }), Arg::Long(long) => args.push(Argument { r#type: "long".to_string(), value: long.to_string(), }), Arg::Value(value) => args.push(Argument { r#type: "value".to_string(), value: value.to_string_lossy().to_string(), }), } } Self { running: true, text: String::new(), janet, } } /// Run the application's main loop. pub async fn run(mut self, mut terminal: DefaultTerminal) -> color_eyre::Result<()> { while self.running { terminal.draw(|frame| frame.render_widget(&self, frame.area()))?; match EventHandler::get().next().await? { Event::Tick => self.tick(), Event::Crossterm(event) => match event { crossterm::event::Event::Key(key_event) => self.handle_key_events(key_event)?, _ => {} }, Event::App(app_event) => match app_event { AppEvent::InsertChar(c) => self.text.push(c), AppEvent::Exit => self.quit(), }, } } Ok(()) } /// Handles the key events and updates the state of [`App`]. pub fn handle_key_events(&mut self, key_event: KeyEvent) -> color_eyre::Result<()> { // For now just send over the char events to Janet // let key_event_struct = structs! { // "code" => key_event.code.to_string().as_str(), // "modifiers" => &(key_event.modifiers.bits() as i32), // }; match key_event.code { // KeyCode::Char('c' | 'C') if key_event.modifiers == KeyModifiers::CONTROL => { // self.events.send(AppEvent::Quit) // } KeyCode::Char(c) => { let expression = format!(r#"(koa/keymap/handle-key "{c}")"#); self.janet.run(&expression).unwrap(); } _ => {} } Ok(()) } /// Handles the tick event of the terminal. /// /// The tick event is where you can update the state of your application with any logic that /// needs to be updated at a fixed frame rate. E.g. polling a server, updating an animation. pub fn tick(&self) {} /// Set running to false to quit the application. pub fn quit(&mut self) { self.running = false; } }