Compare commits

...

6 Commits

  1. 48
      src/input.rs
  2. 166
      src/main.rs
  3. 88
      src/render.rs

@ -0,0 +1,48 @@
use sdl2::keyboard::Keycode;
mod keybinds {
pub(super) enum Clipboard {
CutSelection,
CutLines,
CopySelection,
CopyLines,
Paste,
PasteOverwrite,
}
pub(super) enum Editing {
Undo,
Redo,
NewLines,
DeleteLines,
}
pub(super) enum Movement {
DocBegin,
DocEnd,
LineBack,
LineBegin,
LineForward,
LineEnd,
CharBack,
CharForward,
WordBack,
WordForward,
ParaBack,
ParaForward,
}
pub(super) enum Selection {
CharBack,
Forward,
WordBack,
WordForward,
ParaBack,
ParaForward,
WholeLines,
}
}
fn input(buffer: &str, modifiers: crate::ModifierKeys, key: Keycode) {
}

@ -1,13 +1,13 @@
extern crate sdl2;
use clipboard::{ClipboardProvider, ClipboardContext};
use num_format::{Locale, ToFormattedString};
use sdl2::event::{Event, WindowEvent};
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
use sdl2::{
event::{Event, WindowEvent},
keyboard::Keycode,
};
mod editor_render;
mod render;
mod input;
static SCREEN_WIDTH: u32 = 1280;
static SCREEN_HEIGHT: u32 = 720;
@ -16,7 +16,7 @@ static SCREEN_HEIGHT: u32 = 720;
static REFRESH_RATE: u32 = 50;
// TODO: Make this configurable
static UNDO_TIME: u32 = 500;
static UNDO_TIME: u32 = 250;
static UNDO_TIME_COUNT: u32 = (REFRESH_RATE as f32 * (UNDO_TIME as f32 / 1000.0)) as u32;
struct ModifierKeys {
@ -25,11 +25,16 @@ struct ModifierKeys {
shift: bool,
}
pub fn main() -> Result<(), String> {
let mut modifier_keys = ModifierKeys {alt: false, ctrl: false, shift: false};
// struct EditorGraphics {
// canvas: Canvas<Window>,
// glyph_atlas: GlyphAtlas,
// }
pub fn main() -> Result<(), String> {
// Initialize clipboard
let mut clipboard_context: ClipboardContext = ClipboardProvider::new().unwrap();
// Initialize SDL2, window, and canvas
let sdl_context = sdl2::init()?;
let video_subsys = sdl_context.video()?;
@ -43,83 +48,38 @@ pub fn main() -> Result<(), String> {
let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;
let mut window_size = canvas.output_size()?;
// Initalize buffer
let mut buffer = String::new();
let mut cursor_position = 0;
let mut selection_anchor: Option<usize> = None;
// Initialize undo data & values
let mut undo_history: Vec<(String, usize)> = vec![];
let mut undo_position: usize = 0;
let mut undo_timer: u32 = UNDO_TIME_COUNT;
let pad_offset = Point::new(10, 10);
let (glyph_atlas, glyph_metrics) = editor_render::generate_glyph_data();
let mut draw = |window_size: (u32, u32), text: &str, pos: usize| -> Result<(), String> {
// Draw background
canvas.set_draw_color(Color::RGB(32, 32, 32));
canvas.clear();
// Draw text
canvas.set_draw_color(Color::RGB(240, 240, 240));
let fb_text = editor_render::draw_text(
&glyph_atlas,
&glyph_metrics,
text,
pad_offset
);
canvas.draw_points(&fb_text[..])?;
// Draw info
let status = text.len().to_formatted_string(&Locale::en);
let status_position = Point::new(
pad_offset.x,
window_size.1 as i32 - glyph_metrics.height as i32 * 2
);
canvas.set_draw_color(Color::RGB(16, 64, 64));
canvas.fill_rect(Rect::new(0,
status_position.y - 5,
window_size.0,
glyph_metrics.height as u32 + 10
))?;
canvas.set_draw_color(Color::RGB(127, 240, 240));
let status_bar = editor_render::draw_text(
&glyph_atlas,
&glyph_metrics,
&status,
status_position
);
canvas.draw_points(&status_bar[..])?;
// Draw cursor
canvas.set_draw_color(Color::RGB(64, 240, 240));
let fb_cursor = editor_render::draw_cursor(
&glyph_metrics,
pos,
text,
pad_offset
);
canvas.draw_line(fb_cursor.0, fb_cursor.1)?;
canvas.present();
Ok(())
};
draw(window_size, "", cursor_position)?;
// Initialize input values
let mut modifier_keys = ModifierKeys {alt: false, ctrl: false, shift: false};
let mut cursor_position = 0;
let mut selection_anchor: Option<usize> = None;
// Initialize graphics data and values
let glyph_atlas = render::generate_glyph_data();
// Easier way to please the borrow checker
macro_rules! draw {
() => {
render::draw_everything(&mut canvas, &glyph_atlas, &buffer, cursor_position)?
};
}
draw!();
'mainloop: loop {
// TODO: Make this completely user-configurable instead of hardcoded
for event in sdl_context.event_pump()?.poll_iter() {
match event {
Event::Window { win_event, .. } => {
if let WindowEvent::Resized(w, h) = win_event {
window_size = (w as u32, h as u32);
draw(window_size, &buffer, cursor_position)?
if let WindowEvent::Resized(_w, _h) = win_event {
draw!();
}
}
// ESC or SIGKILL
@ -172,7 +132,8 @@ pub fn main() -> Result<(), String> {
selection_anchor = None;
buffer.remove(cursor_position);
draw(window_size, &buffer, cursor_position)?
draw!();
}
},
@ -184,7 +145,8 @@ pub fn main() -> Result<(), String> {
let key = '\n';
buffer.insert(cursor_position, key);
cursor_position += 1;
draw(window_size, &buffer, cursor_position)?
draw!();
},
// HOME key
@ -192,7 +154,8 @@ pub fn main() -> Result<(), String> {
selection_anchor = None;
cursor_position = 0;
draw(window_size, &buffer, cursor_position)?
draw!();
},
// END key
@ -200,7 +163,8 @@ pub fn main() -> Result<(), String> {
selection_anchor = None;
cursor_position = buffer.len();
draw(window_size, &buffer, cursor_position)?
draw!();
},
// Left/Back arrow
@ -209,7 +173,8 @@ pub fn main() -> Result<(), String> {
cursor_position = usize::checked_sub(cursor_position, 1)
.unwrap_or(0);
draw(window_size, &buffer, cursor_position)?
draw!();
},
// Right/Forward arrow
@ -217,7 +182,8 @@ pub fn main() -> Result<(), String> {
selection_anchor = None;
cursor_position = (cursor_position + 1).min(buffer.len());
draw(window_size, &buffer, cursor_position)?
draw!();
},
// BACKSPACE key
@ -229,7 +195,8 @@ pub fn main() -> Result<(), String> {
buffer.remove(cursor_position - 1);
cursor_position -= 1;
draw(window_size, &buffer, cursor_position)?
draw!();
}
},
@ -240,6 +207,15 @@ pub fn main() -> Result<(), String> {
// CTRL down
(false, true, false) => {
match keycode {
// Select all
Some(Keycode::A) => {
selection_anchor = Some(buffer.len());
cursor_position = 0;
draw!()
},
// Undo
Some(Keycode::Z) => {
if undo_position > 1 {
undo_position -= 1;
@ -247,15 +223,20 @@ pub fn main() -> Result<(), String> {
buffer = last_undo.0;
cursor_position = last_undo.1;
draw(window_size, &buffer, cursor_position)?
draw!()
}
},
// TODO: Cut
Some(Keycode::X) => println!("Cut"),
// Copy
// TODO: Use selection
Some(Keycode::C) => {
clipboard_context.set_contents(buffer.clone()).unwrap()
},
// Paste
Some(Keycode::V) => {
let paste = clipboard_context.get_contents().unwrap();
for character in paste.chars() {
@ -263,9 +244,8 @@ pub fn main() -> Result<(), String> {
cursor_position += 1;
undo_timer = UNDO_TIME_COUNT;
}
draw(window_size, &buffer, cursor_position)?
draw!()
},
// BACKSPACE key
@ -287,13 +267,14 @@ pub fn main() -> Result<(), String> {
buffer.remove(cursor_position - 1);
cursor_position -= 1;
draw(window_size, &buffer, cursor_position)?
draw!()
}
},
_ => (),
}
},
// SHIFT + CTRL down
(true, true, false) => {
match keycode {
Some(Keycode::Z) => {
@ -303,7 +284,7 @@ pub fn main() -> Result<(), String> {
buffer = last_redo.0;
cursor_position = last_redo.1;
draw(window_size, &buffer, cursor_position)?
draw!();
}
},
@ -318,15 +299,16 @@ pub fn main() -> Result<(), String> {
}
},
// Process user input
Event::TextInput { text, .. } => {
undo_timer = 0;
selection_anchor = None;
let input_char = text.chars().nth(0).expect("Empty");
buffer.insert(cursor_position, input_char);
cursor_position += 1;
draw(window_size, &buffer, cursor_position)?;
draw!();
},
_ => {}
@ -337,20 +319,18 @@ pub fn main() -> Result<(), String> {
if undo_timer < UNDO_TIME_COUNT {
undo_timer += 1;
} else if undo_timer == UNDO_TIME_COUNT {
// println!("Saving undo step.");
// Upon editing after an undo, this clears every action after such undo
if undo_position < undo_history.len() {
undo_history.truncate(undo_position);
}
undo_history.push((buffer.clone(), cursor_position));
undo_timer += 1;
undo_position += 1;
//println!("Undo data: {undo_history:?}");
}
::std::thread::sleep(std::time::Duration::new(0, 1_000_000_000 / REFRESH_RATE));
std::thread::sleep(std::time::Duration::new(0, 1_000_000_000 / REFRESH_RATE));
}
format!("{selection_anchor:?}");
println!("{buffer}");
Ok(())
}

@ -3,13 +3,21 @@
//! This takes a janky ass raw image file with width info inserted
//! and turns it into a bunch of points for SDL2 to read.
use num_format::{Locale, ToFormattedString};
use sdl2::{
pixels::Color,
rect::{Point, Rect},
render::Canvas,
video::Window,
};
use std::{fs::File, path::Path, io::Read};
use sdl2::rect::Point;
type Glyph = Vec<Point>;
pub struct GlyphMetrics {
pub width: usize,
pub height: usize,
struct GlyphMetrics {
width: usize,
height: usize,
}
impl GlyphMetrics {
@ -18,7 +26,10 @@ impl GlyphMetrics {
}
}
type Glyph = Vec<Point>;
pub struct GlyphAtlas {
glyphs: Vec<Glyph>,
metrics: GlyphMetrics,
}
/// Reads the file and turns it into a Vec of u8s
fn read_file(file_name: String) -> Vec<u8> {
@ -35,7 +46,7 @@ fn read_file(file_name: String) -> Vec<u8> {
file_content
}
pub fn generate_glyph_data() -> (Vec<Glyph>, GlyphMetrics) {
pub fn generate_glyph_data() -> GlyphAtlas {
// Retrieve font data from file
let file_path = String::from("./fonts/Terminus14x8.data");
let contents = read_file(file_path);
@ -49,7 +60,7 @@ pub fn generate_glyph_data() -> (Vec<Glyph>, GlyphMetrics) {
let width_right_byte = contents[1];
let width_bytes = [width_left_byte, width_right_byte];
let width = u16::from_be_bytes(width_bytes);
println!("Left Byte: {width_left_byte}, Right Byte: {width_right_byte}, Byte Pair: {width}");
// println!("Left Byte: {width_left_byte}, Right Byte: {width_right_byte}, Byte Pair: {width}");
let gtable_prune = &contents[width as usize + 2 ..];
@ -73,15 +84,15 @@ pub fn generate_glyph_data() -> (Vec<Glyph>, GlyphMetrics) {
}
glyph_atlas.push(new_glyph);
}
(glyph_atlas, glyph_metrics)
GlyphAtlas { glyphs: glyph_atlas, metrics: glyph_metrics }
}
/// Method for generating points to render, using given string
pub fn draw_text(glyph_atlas: &Vec<Glyph>, glyph_metrics: &GlyphMetrics, content: &str, offset: Point) -> Vec<Point> {
fn draw_text(glyph_atlas: &GlyphAtlas, content: &str, offset: Point) -> Vec<Point> {
let mut points: Vec<Point> = vec![];
let glyph_width = glyph_metrics.width;
let glyph_height = glyph_metrics.height;
let glyph_width = glyph_atlas.metrics.width;
let glyph_height = glyph_atlas.metrics.height;
let lines = content.split('\n');
for (y, chars) in lines.enumerate() {
@ -93,7 +104,7 @@ pub fn draw_text(glyph_atlas: &Vec<Glyph>, glyph_metrics: &GlyphMetrics, content
index = chara.to_ascii_uppercase() as usize;
}
for pixel in &glyph_atlas[index - 32] {
for pixel in &glyph_atlas.glyphs[index - 32] {
let x_glyph = x * glyph_width;
let y_glyph = y * glyph_height;
@ -108,9 +119,9 @@ pub fn draw_text(glyph_atlas: &Vec<Glyph>, glyph_metrics: &GlyphMetrics, content
points
}
pub fn draw_cursor(glyph_metrics: &GlyphMetrics, mut cursor_position: usize, content: &str, offset: Point) -> (Point, Point) {
let glyph_width = glyph_metrics.width;
let glyph_height = glyph_metrics.height;
pub fn draw_cursor(glyph_atlas: &GlyphAtlas, mut cursor_position: usize, content: &str, offset: Point) -> (Point, Point) {
let glyph_width = glyph_atlas.metrics.width;
let glyph_height = glyph_atlas.metrics.height;
let mut x = 0;
let mut y = 0;
@ -118,7 +129,6 @@ pub fn draw_cursor(glyph_metrics: &GlyphMetrics, mut cursor_position: usize, con
cursor_position = cursor_position.checked_sub(1).unwrap_or(0);
for (idx, chara) in content.chars().enumerate() {
x += 1;
if chara == '\n' {
x = 0;
y += 1;
@ -136,3 +146,49 @@ pub fn draw_cursor(glyph_metrics: &GlyphMetrics, mut cursor_position: usize, con
}
(Point::new(offset.x, offset.y), Point::new(offset.x, offset.y + glyph_height as i32))
}
/// Draw all contents to the window
pub fn draw_everything(canvas: &mut Canvas<Window>,
glyph_atlas: &GlyphAtlas,
buffer: &str,
cursor_position: usize) -> Result<(), String> {
// Quick initialization
let window_size = canvas.output_size()?;
let text_offset = Point::new(10, 10);
// Draw background
canvas.set_draw_color(Color::RGB(32, 32, 32));
canvas.clear();
// Draw text
canvas.set_draw_color(Color::RGB(240, 240, 240));
let fb_text = draw_text(&glyph_atlas, buffer, text_offset);
canvas.draw_points(&fb_text[..])?;
// Draw info
let status = buffer.len().to_formatted_string(&Locale::en);
let status_position = Point::new(
text_offset.x,
window_size.1 as i32 - glyph_atlas.metrics.height as i32 * 2
);
canvas.set_draw_color(Color::RGB(16, 64, 64));
canvas.fill_rect(Rect::new(0,
status_position.y - 5,
window_size.0,
glyph_atlas.metrics.height as u32 + 10
))?;
canvas.set_draw_color(Color::RGB(127, 240, 240));
let status_bar = draw_text(&glyph_atlas, &status, status_position);
canvas.draw_points(&status_bar[..])?;
// Draw cursor
canvas.set_draw_color(Color::RGB(64, 240, 240));
let fb_cursor = draw_cursor(&glyph_atlas, cursor_position, buffer, text_offset);
canvas.draw_line(fb_cursor.0, fb_cursor.1)?;
canvas.present();
Ok(())
}
Loading…
Cancel
Save