Add text
This commit is contained in:
parent
fdf5d0332a
commit
b092ef65e9
@ -1,7 +1,7 @@
|
|||||||
use crate::app::{UpdateResult, WindowCanvas};
|
use crate::app::{UpdateResult, WindowCanvas};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::file::*;
|
use crate::ui::file::*;
|
||||||
use crate::file::editor_file::EditorFile;
|
use crate::ui::file::editor_file::EditorFile;
|
||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
use crate::ui::caret::Caret;
|
use crate::ui::caret::Caret;
|
||||||
@ -10,6 +10,7 @@ use sdl2::rect::Point;
|
|||||||
use std::boxed::Box;
|
use std::boxed::Box;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use sdl2::rect::Rect;
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
menu_bar: MenuBar,
|
menu_bar: MenuBar,
|
||||||
@ -40,6 +41,85 @@ impl AppState {
|
|||||||
pub fn caret(&mut self) -> &mut Caret {
|
pub fn caret(&mut self) -> &mut Caret {
|
||||||
&mut self.caret
|
&mut self.caret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn delete_front(&mut self, config: &Config) {
|
||||||
|
let file: &mut EditorFile = if let Some(file) = self.files.get_mut(self.current_file) {
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut buffer: String = file.buffer();
|
||||||
|
let caret: &mut Caret = &mut self.caret;
|
||||||
|
let position: usize = caret.text_position();
|
||||||
|
if position == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer.remove(position);
|
||||||
|
match file.get_character_at(position - 1) {
|
||||||
|
Some(character) => {
|
||||||
|
let dest: &Rect = character.dest();
|
||||||
|
caret.move_caret(position - 1, Point::new(
|
||||||
|
dest.x(), dest.y(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
caret.reset_caret();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let new_file = EditorFile::new(
|
||||||
|
file.path(),
|
||||||
|
buffer,
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
self.files[self.current_file] = new_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_back(&mut self, config: &Config) {
|
||||||
|
let file: &mut EditorFile = if let Some(file) = self.files.get_mut(self.current_file) {
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut buffer: String = file.buffer();
|
||||||
|
let caret: &mut Caret = &mut self.caret;
|
||||||
|
let position: usize = caret.text_position();
|
||||||
|
if position >= buffer.len() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buffer.remove(position);
|
||||||
|
let new_file = EditorFile::new(
|
||||||
|
file.path(),
|
||||||
|
buffer,
|
||||||
|
config,
|
||||||
|
);
|
||||||
|
self.files[self.current_file] = new_file;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn insert_character(&mut self, character: char, renderer: &mut Renderer) {
|
||||||
|
let file: &mut EditorFile = if let Some(file) = self.files.get_mut(self.current_file) {
|
||||||
|
file
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let mut buffer: String = file.buffer();
|
||||||
|
let caret: &mut Caret = &mut self.caret;
|
||||||
|
let position: usize = caret.text_position();
|
||||||
|
buffer.insert(position, character.clone());
|
||||||
|
let new_file = EditorFile::new(
|
||||||
|
file.path(),
|
||||||
|
buffer,
|
||||||
|
renderer.config(),
|
||||||
|
);
|
||||||
|
if let Some(rect) = get_text_character_rect(character, renderer) {
|
||||||
|
if let Some(current) = file.get_character_at(position) {
|
||||||
|
caret.move_caret(position + 1, Point::new(
|
||||||
|
current.dest().x() + rect.width() as i32,
|
||||||
|
current.dest().y(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.files[self.current_file] = new_file;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for AppState {
|
impl Render for AppState {
|
||||||
@ -72,8 +152,8 @@ impl ClickHandler for AppState {
|
|||||||
if let Some(current_file) = self.files.get_mut(self.current_file) {
|
if let Some(current_file) = self.files.get_mut(self.current_file) {
|
||||||
if current_file.is_left_click_target(point) {
|
if current_file.is_left_click_target(point) {
|
||||||
match current_file.on_left_click(point, config) {
|
match current_file.on_left_click(point, config) {
|
||||||
UpdateResult::MoveCaret(rect) => {
|
UpdateResult::MoveCaret(rect, position) => {
|
||||||
self.caret.move_caret(Point::new(rect.x(), rect.y()));
|
self.caret.move_caret(position, Point::new(rect.x(), rect.y()));
|
||||||
}
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
};
|
};
|
||||||
|
281
src/app/keyboard_handler.rs
Normal file
281
src/app/keyboard_handler.rs
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
use sdl2::keyboard::{Keycode, Scancode};
|
||||||
|
use sdl2::event::EventPollIterator;
|
||||||
|
use sdl2::EventPump;
|
||||||
|
|
||||||
|
use crate::app::UpdateResult;
|
||||||
|
|
||||||
|
pub fn resolve_action(key_code: Keycode, event_pump: &mut EventPump) -> UpdateResult {
|
||||||
|
match key_code {
|
||||||
|
Keycode::Backspace => return UpdateResult::DeleteFront,
|
||||||
|
Keycode::Delete => return UpdateResult::DeleteBack,
|
||||||
|
|
||||||
|
Keycode::Escape => return UpdateResult::NoOp,
|
||||||
|
|
||||||
|
Keycode::Space => {
|
||||||
|
let character: char = ' ';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Exclaim => {
|
||||||
|
let character: char = '!';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Quotedbl => {
|
||||||
|
let character: char = '"';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Hash => {
|
||||||
|
let character: char = '#';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Dollar => {
|
||||||
|
let character: char = '$';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Percent => {
|
||||||
|
let character: char = '%';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Ampersand => {
|
||||||
|
let character: char = '&';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Quote => {
|
||||||
|
let character: char = '\'';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::LeftParen => {
|
||||||
|
let character: char = '{';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::RightParen => {
|
||||||
|
let character: char = '}';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Asterisk => {
|
||||||
|
let character: char = '*';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Plus => {
|
||||||
|
let character: char = '+';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Comma => {
|
||||||
|
let character: char = ',';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Minus => {
|
||||||
|
let character: char = '-';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Period => {
|
||||||
|
let character: char = '.';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Slash => {
|
||||||
|
let character: char = '/';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num0 => {
|
||||||
|
let character: char = '0';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num1 => {
|
||||||
|
let character: char = '1';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num2 => {
|
||||||
|
let character: char = '2';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num3 => {
|
||||||
|
let character: char = '3';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num4 => {
|
||||||
|
let character: char = '4';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num5 => {
|
||||||
|
let character: char = '5';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num6 => {
|
||||||
|
let character: char = '6';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num7 => {
|
||||||
|
let character: char = '7';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num8 => {
|
||||||
|
let character: char = '8';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Num9 => {
|
||||||
|
let character: char = '9';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Colon => {
|
||||||
|
let character: char = ':';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Semicolon => {
|
||||||
|
let character: char = ';';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Less => {
|
||||||
|
let character: char = '<';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Equals => {
|
||||||
|
let character: char = '=';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Greater => {
|
||||||
|
let character: char = '>';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Question => {
|
||||||
|
let character: char = '?';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::At => {
|
||||||
|
let character: char = '@';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::LeftBracket => {
|
||||||
|
let character: char = '[';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Backslash => {
|
||||||
|
let character: char = '\\';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::RightBracket => {
|
||||||
|
let character: char = ']';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Caret => {
|
||||||
|
let character: char = '^';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Underscore => {
|
||||||
|
let character: char = '_';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Backquote => {
|
||||||
|
let character: char = '`';
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::A => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'A' } else { 'a' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::B => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'B' } else { 'b' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::C => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'C' } else { 'c' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::D => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'D' } else { 'd' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::E => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'E' } else { 'e' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::F => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'F' } else { 'f' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::G => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'G' } else { 'g' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::H => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'H' } else { 'h' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::I => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'I' } else { 'i' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::J => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'J' } else { 'j' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::K => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'K' } else { 'k' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::L => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'L' } else { 'l' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::M => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'M' } else { 'm' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::N => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'N' } else { 'n' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::O => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'O' } else { 'o' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::P => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'P' } else { 'p' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Q => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'Q' } else { 'q' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::R => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'R' } else { 'r' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::S => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'S' } else { 's' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::T => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'T' } else { 't' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::U => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'U' } else { 'u' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::V => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'V' } else { 'v' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::W => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'W' } else { 'w' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::X => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'X' } else { 'x' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Y => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'Y' } else { 'y' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
Keycode::Z => {
|
||||||
|
let character: char = if with_shift(event_pump) { 'Z' } else { 'z' };
|
||||||
|
return UpdateResult::Input(character);
|
||||||
|
}
|
||||||
|
_ => UpdateResult::NoOp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_shift(event_pump: &mut EventPump) -> bool {
|
||||||
|
event_pump.keyboard_state().is_scancode_pressed(Scancode::LShift) ||
|
||||||
|
event_pump.keyboard_state().is_scancode_pressed(Scancode::RShift)
|
||||||
|
}
|
@ -3,9 +3,11 @@ use crate::config::Config;
|
|||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use crate::themes::*;
|
use crate::themes::*;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
|
|
||||||
use sdl2::{Sdl, TimerSubsystem};
|
use sdl2::{Sdl, TimerSubsystem};
|
||||||
use sdl2::event::Event;
|
use sdl2::event::Event;
|
||||||
use sdl2::EventPump;
|
use sdl2::EventPump;
|
||||||
|
use sdl2::keyboard::{Keycode, Mod};
|
||||||
use sdl2::hint;
|
use sdl2::hint;
|
||||||
use sdl2::mouse::MouseButton;
|
use sdl2::mouse::MouseButton;
|
||||||
use sdl2::pixels::Color;
|
use sdl2::pixels::Color;
|
||||||
@ -16,6 +18,7 @@ use std::thread::sleep;
|
|||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
pub mod app_state;
|
pub mod app_state;
|
||||||
|
pub mod keyboard_handler;
|
||||||
|
|
||||||
pub type WindowCanvas = Canvas<Window>;
|
pub type WindowCanvas = Canvas<Window>;
|
||||||
|
|
||||||
@ -25,7 +28,10 @@ pub enum UpdateResult {
|
|||||||
Stop,
|
Stop,
|
||||||
RefreshPositions,
|
RefreshPositions,
|
||||||
MouseLeftClicked(Point),
|
MouseLeftClicked(Point),
|
||||||
MoveCaret(Rect),
|
MoveCaret(Rect, usize),
|
||||||
|
DeleteFront,
|
||||||
|
DeleteBack,
|
||||||
|
Input(char)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Task {
|
pub enum Task {
|
||||||
@ -86,15 +92,24 @@ impl Application {
|
|||||||
UpdateResult::Stop => break 'running,
|
UpdateResult::Stop => break 'running,
|
||||||
UpdateResult::RefreshPositions => (),
|
UpdateResult::RefreshPositions => (),
|
||||||
UpdateResult::NoOp => (),
|
UpdateResult::NoOp => (),
|
||||||
UpdateResult::MoveCaret(_) => (),
|
UpdateResult::MoveCaret(_, _pos) => (),
|
||||||
UpdateResult::MouseLeftClicked(point) => {
|
UpdateResult::MouseLeftClicked(point) => {
|
||||||
app_state.on_left_click(&point, renderer.config());
|
app_state.on_left_click(&point, renderer.config());
|
||||||
}
|
}
|
||||||
|
UpdateResult::DeleteFront => {
|
||||||
|
app_state.delete_front(renderer.config());
|
||||||
|
},
|
||||||
|
UpdateResult::DeleteBack => {
|
||||||
|
app_state.delete_back(renderer.config());
|
||||||
|
},
|
||||||
|
UpdateResult::Input(text_character) => {
|
||||||
|
app_state.insert_character(text_character, &mut renderer);
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for task in self.tasks.iter() {
|
for task in self.tasks.iter() {
|
||||||
match task {
|
match task {
|
||||||
Task::OpenFile { file_path } => {
|
Task::OpenFile { file_path } => {
|
||||||
use crate::file::editor_file::*;
|
use crate::ui::file::editor_file::*;
|
||||||
app_state.open_file(file_path.clone(), renderer.config());
|
app_state.open_file(file_path.clone(), renderer.config());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,6 +149,17 @@ impl Application {
|
|||||||
MouseButton::Left => return UpdateResult::MouseLeftClicked(Point::new(x, y)),
|
MouseButton::Left => return UpdateResult::MouseLeftClicked(Point::new(x, y)),
|
||||||
_ => (),
|
_ => (),
|
||||||
},
|
},
|
||||||
|
Event::KeyDown { keycode, .. } => {
|
||||||
|
let keycode = if keycode.is_some() {
|
||||||
|
keycode.unwrap()
|
||||||
|
} else {
|
||||||
|
return UpdateResult::NoOp;
|
||||||
|
};
|
||||||
|
return keyboard_handler::resolve_action(
|
||||||
|
keycode,
|
||||||
|
event_pump
|
||||||
|
);
|
||||||
|
}
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ use crate::app::Application;
|
|||||||
|
|
||||||
pub mod app;
|
pub mod app;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
pub mod file;
|
|
||||||
pub mod lexer;
|
pub mod lexer;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
pub mod themes;
|
pub mod themes;
|
||||||
|
@ -14,12 +14,14 @@ enum CaretState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct Caret {
|
pub struct Caret {
|
||||||
state: CaretState,
|
pending: bool,
|
||||||
|
text_position: usize,
|
||||||
blink_delay: u8,
|
blink_delay: u8,
|
||||||
position: Rect,
|
state: CaretState,
|
||||||
|
dest: Rect,
|
||||||
|
reset_position: Rect,
|
||||||
bright_character_color: Color,
|
bright_character_color: Color,
|
||||||
blur_character_color: Color,
|
blur_character_color: Color,
|
||||||
pending: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Caret {
|
impl Caret {
|
||||||
@ -29,7 +31,13 @@ impl Caret {
|
|||||||
Self {
|
Self {
|
||||||
state: CaretState::Bright,
|
state: CaretState::Bright,
|
||||||
blink_delay: 0,
|
blink_delay: 0,
|
||||||
position: Rect::new(
|
dest: Rect::new(
|
||||||
|
config.editor_left_margin(),
|
||||||
|
config.editor_top_margin(),
|
||||||
|
4,
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
reset_position: Rect::new(
|
||||||
config.editor_left_margin(),
|
config.editor_left_margin(),
|
||||||
config.editor_top_margin(),
|
config.editor_top_margin(),
|
||||||
4,
|
4,
|
||||||
@ -38,6 +46,7 @@ impl Caret {
|
|||||||
bright_character_color,
|
bright_character_color,
|
||||||
blur_character_color,
|
blur_character_color,
|
||||||
pending: true,
|
pending: true,
|
||||||
|
text_position: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,9 +58,19 @@ impl Caret {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_caret(&mut self, pos: Point) {
|
pub fn reset_caret(&mut self) {
|
||||||
self.position.set_x(pos.x());
|
self.dest = self.reset_position.clone();
|
||||||
self.position.set_y(pos.y());
|
self.text_position = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_caret(&mut self, position: usize, pos: Point) {
|
||||||
|
self.text_position = position;
|
||||||
|
self.dest.set_x(pos.x());
|
||||||
|
self.dest.set_y(pos.y());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text_position(&self) -> usize {
|
||||||
|
self.text_position
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,14 +87,15 @@ impl Render for Caret {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|_| panic!("Unable to load font"));
|
.unwrap_or_else(|_| panic!("Unable to load font"));
|
||||||
if let Ok((_, h)) = font.size_of_char('W') {
|
if let Ok((_, h)) = font.size_of_char('W') {
|
||||||
self.position.set_height(h);
|
self.dest.set_height(h);
|
||||||
|
self.reset_position = self.dest.clone();
|
||||||
}
|
}
|
||||||
self.pending = false;
|
self.pending = false;
|
||||||
}
|
}
|
||||||
let start = Point::new(self.position.x(), self.position.y());
|
let start = Point::new(self.dest.x(), self.dest.y());
|
||||||
let end = Point::new(
|
let end = Point::new(
|
||||||
self.position.x(),
|
self.dest.x(),
|
||||||
self.position.y() + self.position.height() as i32,
|
self.dest.y() + self.dest.height() as i32,
|
||||||
);
|
);
|
||||||
let color = match self.state {
|
let color = match self.state {
|
||||||
CaretState::Bright => &self.bright_character_color,
|
CaretState::Bright => &self.bright_character_color,
|
||||||
@ -107,6 +127,6 @@ impl ClickHandler for Caret {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn is_left_click_target(&self, point: &Point) -> bool {
|
fn is_left_click_target(&self, point: &Point) -> bool {
|
||||||
is_in_rect(point, &self.position)
|
is_in_rect(point, &self.dest)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,52 @@
|
|||||||
|
use sdl2::rect::{Point, Rect};
|
||||||
|
|
||||||
use crate::app::{UpdateResult, WindowCanvas};
|
use crate::app::{UpdateResult, WindowCanvas};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::file::editor_file_section::EditorFileSection;
|
use crate::ui::file::editor_file_section::EditorFileSection;
|
||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
use sdl2::rect::{Point, Rect};
|
use crate::ui::text_character::TextCharacter;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EditorFile {
|
pub struct EditorFile {
|
||||||
path: String,
|
path: String,
|
||||||
sections: Vec<EditorFileSection>,
|
sections: Vec<EditorFileSection>,
|
||||||
render_position: Rect,
|
render_position: Rect,
|
||||||
|
buffer: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EditorFile {
|
impl EditorFile {
|
||||||
pub fn new(path: String, buffer: String, config: &Config) -> Self {
|
pub fn new(path: String, buffer: String, config: &Config) -> Self {
|
||||||
let section = EditorFileSection::new(buffer, config);
|
let sections = vec![
|
||||||
let sections = vec![section];
|
EditorFileSection::new(buffer.clone(), config)
|
||||||
|
];
|
||||||
let x = config.editor_left_margin();
|
let x = config.editor_left_margin();
|
||||||
let y = config.editor_top_margin();
|
let y = config.editor_top_margin();
|
||||||
Self {
|
Self {
|
||||||
path,
|
path,
|
||||||
sections,
|
sections,
|
||||||
render_position: Rect::new(x, y, 0, 0),
|
render_position: Rect::new(x, y, 0, 0),
|
||||||
|
buffer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn buffer(&self) -> String {
|
||||||
|
self.buffer.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> String {
|
||||||
|
self.path.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_character_at(&self, index: usize) -> Option<&TextCharacter> {
|
||||||
|
for section in self.sections.iter() {
|
||||||
|
if let Some(text_character) = section.get_character_at(index) {
|
||||||
|
return Some(text_character)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn refresh_characters_position(&mut self, config: &Config) {
|
fn refresh_characters_position(&mut self, config: &Config) {
|
||||||
let mut current: Rect = self.render_position.clone();
|
let mut current: Rect = self.render_position.clone();
|
||||||
for section in self.sections.iter_mut() {
|
for section in self.sections.iter_mut() {
|
@ -1,10 +1,12 @@
|
|||||||
|
use sdl2::rect::{Point, Rect};
|
||||||
|
|
||||||
use crate::app::{UpdateResult, WindowCanvas};
|
use crate::app::{UpdateResult, WindowCanvas};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::file::editor_file_token::EditorFileToken;
|
use crate::ui::file::editor_file_token::EditorFileToken;
|
||||||
use crate::lexer::Language;
|
use crate::lexer::Language;
|
||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
use sdl2::rect::{Point, Rect};
|
use crate::ui::text_character::TextCharacter;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct EditorFileSection {
|
pub struct EditorFileSection {
|
||||||
@ -31,6 +33,15 @@ impl EditorFileSection {
|
|||||||
c.update_position(current, config);
|
c.update_position(current, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_character_at(&self, index: usize) -> Option<&TextCharacter> {
|
||||||
|
for token in self.tokens.iter() {
|
||||||
|
if let Some(text_character) = token.get_character_at(index) {
|
||||||
|
return Some(text_character)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for EditorFileSection {
|
impl Render for EditorFileSection {
|
@ -31,6 +31,15 @@ impl EditorFileToken {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_character_at(&self, index: usize) -> Option<&TextCharacter> {
|
||||||
|
for character in self.characters.iter() {
|
||||||
|
if character.position() == index {
|
||||||
|
return Some(&character)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
fn update_view(&mut self, renderer: &mut Renderer) -> UpdateResult {
|
fn update_view(&mut self, renderer: &mut Renderer) -> UpdateResult {
|
||||||
let config = renderer.config().theme().code_highlighting();
|
let config = renderer.config().theme().code_highlighting();
|
||||||
let color: Color = match self.token_type {
|
let color: Color = match self.token_type {
|
||||||
@ -44,9 +53,13 @@ impl EditorFileToken {
|
|||||||
TokenType::Operator { .. } => config.operator().color().into(),
|
TokenType::Operator { .. } => config.operator().color().into(),
|
||||||
TokenType::Separator { .. } => config.separator().color().into(),
|
TokenType::Separator { .. } => config.separator().color().into(),
|
||||||
};
|
};
|
||||||
for c in self.token_type.text().chars() {
|
for (index, c) in self.token_type.text().chars().enumerate() {
|
||||||
let mut text_character =
|
let mut text_character = TextCharacter::new(
|
||||||
TextCharacter::new(c.clone(), self.token_type.line(), color.clone());
|
c.clone(),
|
||||||
|
self.token_type.start() + index,
|
||||||
|
self.token_type.line(),
|
||||||
|
color.clone()
|
||||||
|
);
|
||||||
text_character.update_view(renderer);
|
text_character.update_view(renderer);
|
||||||
self.characters.push(text_character);
|
self.characters.push(text_character);
|
||||||
}
|
}
|
@ -1,11 +1,14 @@
|
|||||||
|
use sdl2::rect::{Point, Rect};
|
||||||
|
|
||||||
use crate::app::{UpdateResult, WindowCanvas};
|
use crate::app::{UpdateResult, WindowCanvas};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use sdl2::rect::{Point, Rect};
|
use crate::renderer::managers::FontDetails;
|
||||||
|
|
||||||
pub mod caret;
|
pub mod caret;
|
||||||
pub mod menu_bar;
|
pub mod menu_bar;
|
||||||
pub mod text_character;
|
pub mod text_character;
|
||||||
|
pub mod file;
|
||||||
|
|
||||||
pub fn is_in_rect(point: &Point, rect: &Rect) -> bool {
|
pub fn is_in_rect(point: &Point, rect: &Rect) -> bool {
|
||||||
let start = Point::new(rect.x(), rect.y());
|
let start = Point::new(rect.x(), rect.y());
|
||||||
@ -16,6 +19,22 @@ pub fn is_in_rect(point: &Point, rect: &Rect) -> bool {
|
|||||||
start.x() <= point.x() && start.y() <= point.y() && end.x() >= point.x() && end.y() >= point.y()
|
start.x() <= point.x() && start.y() <= point.y() && end.x() >= point.x() && end.y() >= point.y()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_text_character_rect(c: char, renderer: &mut Renderer) -> Option<Rect> {
|
||||||
|
let config = renderer.config().editor_config();
|
||||||
|
let font_details =
|
||||||
|
FontDetails::new(config.font_path().as_str(), config.character_size().clone());
|
||||||
|
let font = renderer
|
||||||
|
.font_manager()
|
||||||
|
.load(&font_details)
|
||||||
|
.unwrap_or_else(|_| panic!("Font not found {:?}", font_details));
|
||||||
|
|
||||||
|
if let Ok((width, height)) = font.size_of_char(c) {
|
||||||
|
Some(Rect::new(0, 0, width, height))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait Render {
|
pub trait Render {
|
||||||
fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult;
|
fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult;
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,9 @@ use crate::renderer::managers::FontDetails;
|
|||||||
use crate::renderer::managers::TextDetails;
|
use crate::renderer::managers::TextDetails;
|
||||||
use crate::renderer::Renderer;
|
use crate::renderer::Renderer;
|
||||||
use crate::ui::*;
|
use crate::ui::*;
|
||||||
|
|
||||||
use sdl2::pixels::Color;
|
use sdl2::pixels::Color;
|
||||||
use sdl2::rect::Rect;
|
use sdl2::rect::{Rect, Point};
|
||||||
use sdl2::render::Texture;
|
use sdl2::render::Texture;
|
||||||
use sdl2::ttf::Font;
|
use sdl2::ttf::Font;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
@ -15,6 +16,7 @@ use std::rc::Rc;
|
|||||||
pub struct TextCharacter {
|
pub struct TextCharacter {
|
||||||
pending: bool,
|
pending: bool,
|
||||||
text_character: char,
|
text_character: char,
|
||||||
|
position: usize,
|
||||||
line: usize,
|
line: usize,
|
||||||
source: Rect,
|
source: Rect,
|
||||||
dest: Rect,
|
dest: Rect,
|
||||||
@ -22,10 +24,11 @@ pub struct TextCharacter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TextCharacter {
|
impl TextCharacter {
|
||||||
pub fn new(text_character: char, line: usize, color: Color) -> Self {
|
pub fn new(text_character: char, position: usize, line: usize, color: Color) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pending: true,
|
pending: true,
|
||||||
text_character,
|
text_character,
|
||||||
|
position,
|
||||||
line,
|
line,
|
||||||
source: Rect::new(0, 0, 0, 0),
|
source: Rect::new(0, 0, 0, 0),
|
||||||
dest: Rect::new(0, 0, 0, 0),
|
dest: Rect::new(0, 0, 0, 0),
|
||||||
@ -68,20 +71,19 @@ impl TextCharacter {
|
|||||||
.load(&font_details)
|
.load(&font_details)
|
||||||
.unwrap_or_else(|_| panic!("Font not found {:?}", font_details));
|
.unwrap_or_else(|_| panic!("Font not found {:?}", font_details));
|
||||||
|
|
||||||
let c = self.text_character.clone();
|
if let Some(rect) = get_text_character_rect(self.text_character.clone(), renderer) {
|
||||||
if let Ok((width, height)) = font.size_of_char(c) {
|
self.source = rect.clone();
|
||||||
self.source = Rect::new(0, 0, width, height);
|
self.dest = rect.clone();
|
||||||
self.dest = Rect::new(0, 0, width, height);
|
|
||||||
}
|
}
|
||||||
let mut details = TextDetails {
|
let mut details = TextDetails {
|
||||||
text: c.to_string(),
|
text: self.text_character.to_string(),
|
||||||
color: self.color.clone(),
|
color: self.color.clone(),
|
||||||
font: font_details.clone(),
|
font: font_details.clone(),
|
||||||
};
|
};
|
||||||
renderer
|
renderer
|
||||||
.texture_manager()
|
.texture_manager()
|
||||||
.load_text(&mut details, &font)
|
.load_text(&mut details, &font)
|
||||||
.unwrap_or_else(|_| panic!("Could not create texture for {:?}", c));
|
.unwrap_or_else(|_| panic!("Could not create texture for {:?}", self.text_character));
|
||||||
|
|
||||||
self.pending = false;
|
self.pending = false;
|
||||||
UpdateResult::RefreshPositions
|
UpdateResult::RefreshPositions
|
||||||
@ -96,6 +98,10 @@ impl TextCharacter {
|
|||||||
pub fn is_pending(&self) -> bool {
|
pub fn is_pending(&self) -> bool {
|
||||||
self.pending
|
self.pending
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn position(&self) -> usize {
|
||||||
|
self.position
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Render for TextCharacter {
|
impl Render for TextCharacter {
|
||||||
@ -140,7 +146,10 @@ impl Update for TextCharacter {
|
|||||||
|
|
||||||
impl ClickHandler for TextCharacter {
|
impl ClickHandler for TextCharacter {
|
||||||
fn on_left_click(&mut self, _point: &Point, _config: &Config) -> UpdateResult {
|
fn on_left_click(&mut self, _point: &Point, _config: &Config) -> UpdateResult {
|
||||||
UpdateResult::MoveCaret(self.dest().clone())
|
UpdateResult::MoveCaret(
|
||||||
|
self.dest().clone(),
|
||||||
|
self.position()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_left_click_target(&self, point: &Point) -> bool {
|
fn is_left_click_target(&self, point: &Point) -> bool {
|
||||||
|
Loading…
Reference in New Issue
Block a user