use crate::arch::x86_64::drivers::keyboard::{KeyStroke, get_keystroke_async}; use crate::resources::font::Font; use crate::serial_print; use crate::serial_println; use crate::std::application::{ Application, Error, frame::Frame, render::RenderError, window::Window, }; use crate::std::ascii::Writer; use crate::std::maths::geometry::Vec2; use alloc::string::{String, ToString}; use alloc::vec::Vec; pub struct Editor { cursor_line: usize, cursor_col: usize, mode: Mode, buffer: String, window: Window, } impl Default for Editor { fn default() -> Self { Self::new() } } impl<'a> Editor { const PADDING: usize = 8; pub const fn new() -> Self { Self { cursor_line: 0, cursor_col: 0, mode: Mode::Nor, buffer: String::new(), window: Window::new(), } } fn render(&'a self) -> Result, RenderError> { let mut frame = Frame::new(&self.window); let font = Font::default(); let writer = Writer::new(&font); let (width, height) = writer.font_size().into(); let mut col = 0; let mut line = 0; let mut scale = 1; for ch in self.buffer.chars() { if ch == '\n' { line += scale; col = 0; scale = 1; continue; } if width * (col + 1) > frame.dimensions().x() - 2 * Self::PADDING { line += scale; col = 0; } writer.render_glyph( &mut frame, Vec2::new( col * width + Self::PADDING, line * height + Self::PADDING, ), ch as u8, scale, )?; col += scale; } writer .render_glyph( &mut frame, Vec2::new( self.cursor_col * width + Self::PADDING, self.cursor_line * height + Self::PADDING, ), b'_', scale, ) .expect("TODO: panic message"); Ok(frame) } #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)] fn get_lines(&self) -> Vec<&str> { self.buffer.split('\n').collect::>() } #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)] fn move_cursor(&mut self, x: i32, y: i32) { self.cursor_line = self .cursor_line .checked_add_signed(y as isize) .unwrap_or(self.cursor_line); self.cursor_col = self .cursor_col .checked_add_signed(x as isize) .unwrap_or(self.cursor_col); } #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)] fn delete_char(&mut self) { let i = self.get_char_idx(); self.buffer.remove(i); } #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)] fn insert_char(&mut self, c: char) { let i = self.get_char_idx(); self.buffer.insert(i, c); } #[allow(unused_variables, dead_code, clippy::needless_pass_by_ref_mut)] fn splitline(&mut self) { let i = self.get_char_idx(); self.buffer.insert(i, '\n'); } fn get_char_idx(&self) -> usize { let frame = Frame::new(&self.window); let font = Font::default(); let writer = Writer::new(&font); let (width, _height) = writer.font_size().into(); let mut col = 0; let mut line = 0; let mut scale = 1; for (i, ch) in self.buffer.chars().enumerate() { if ch == '\n' { line += scale; col = 0; scale = 1; continue; } if width * (col + 1) > frame.dimensions().x() - 2 * Self::PADDING { line += scale; col = 0; } if col == self.cursor_col && line == self.cursor_line { return i; } col += scale; } 0 } } impl Application for Editor { type Output = (); async fn run( &mut self, _args: Vec, ) -> Result { self.window.set_dimensions(Vec2::new(1280, 800)); self.window.set_position(Vec2::new(0, 0)); self.window.open(); self.buffer = "Hello world, this is a test init. idk test \n ewntuiewi gjk gfdfg gndf ngdfgnmdfg ndfgmndfg gdfndfnkg njkdgjkndfjnkg ngnjfgnfgnfg fgn fn gfj gnfg jnfgjfngjk fgnjfgnjk jnkdgjnkdfg gfnd njkgdfgjn d fjnkgjkndfgjkndfgjn gndfjnk njkgdfng jnkfgdjknd jnfkgnjk".to_string(); loop { if let Err(_err) = self.render().and_then(|frame| frame.render()) { // TODO: Handle error return Err(Error::ApplicationFailed( "Rendering failed".to_string(), )); } let keystroke = get_keystroke_async().await; match self.mode { Mode::Nor => match keystroke { KeyStroke::Char('i') => self.mode = Mode::Ins, KeyStroke::Char('`') => return Ok(()), _ => {} }, Mode::Ins => { match keystroke { KeyStroke::Char(c) => { match c { // escape '\x1B' => self.mode = Mode::Nor, // delete '\x7F' => self.delete_char(), // backspace '\x08' => { self.move_cursor(-1, 0); self.delete_char(); } // enter '\n' => self.splitline(), _ => { self.insert_char(c); self.move_cursor(1, 0); } } } KeyStroke::Left => { serial_println!("Left\n"); self.move_cursor(-1, 0); } KeyStroke::Right => { serial_println!("Right\n"); self.move_cursor(1, 0); } KeyStroke::Up => { serial_println!("Up\n"); self.move_cursor(0, -1); } KeyStroke::Down => { serial_println!("Down\n"); self.move_cursor(0, 1); } KeyStroke::None => {} _ => {} } } } } } } pub enum Mode { Nor, Ins, } impl core::fmt::Display for Mode { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Self::Nor => write!(f, "Normal"), Self::Ins => write!(f, "Insert"), } } }