249 lines
7.3 KiB
Rust
249 lines
7.3 KiB
Rust
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<Frame<'a>, 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::<Vec<&str>>()
|
|
}
|
|
|
|
#[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<alloc::string::String>,
|
|
) -> Result<Self::Output, Error> {
|
|
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"),
|
|
}
|
|
}
|
|
}
|