Move caret right

This commit is contained in:
Adrian Wozniak 2019-01-04 17:47:58 +01:00
parent 9460b6931a
commit 3a5587dbcc
No known key found for this signature in database
GPG Key ID: 3B441F7808FC43C7
7 changed files with 394 additions and 68 deletions

View File

@ -5,6 +5,8 @@ use crate::ui::caret::Caret;
use crate::ui::file::editor_file::EditorFile; use crate::ui::file::editor_file::EditorFile;
use crate::ui::file::*; use crate::ui::file::*;
use crate::ui::menu_bar::MenuBar; use crate::ui::menu_bar::MenuBar;
use crate::ui::caret::{CaretPosition, MoveDirection};
use crate::ui::text_character::TextCharacter;
use crate::ui::*; use crate::ui::*;
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
use sdl2::VideoSubsystem; use sdl2::VideoSubsystem;
@ -52,15 +54,29 @@ impl AppState {
}; };
let mut buffer: String = file.buffer(); let mut buffer: String = file.buffer();
let caret: &mut Caret = &mut self.caret; let caret: &mut Caret = &mut self.caret;
let position: usize = caret.text_position(); let position: CaretPosition = caret.position().clone();
if position == 0 { if position.text_position() == 0 {
return; return;
} }
buffer.remove(position - 1); let c: char = buffer.chars().collect::<Vec<char>>()[position.text_position() - 1];
match file.get_character_at(position - 1) { buffer.remove(position.text_position() - 1);
let position = match c {
'\n' => CaretPosition::new(
position.text_position() - 1,
position.line_number() - 1,
0,
),
_ => CaretPosition::new(
position.text_position() - 1,
position.line_number(),
position.line_position(),
)
};
match file.get_character_at(position.text_position()) {
Some(character) => { Some(character) => {
let dest: &Rect = character.dest(); let dest: &Rect = character.dest();
caret.move_caret(position - 1, Point::new(dest.x(), dest.y())); caret.move_caret(position, Point::new(dest.x(), dest.y()));
} }
_ => { _ => {
caret.reset_caret(); caret.reset_caret();
@ -101,12 +117,16 @@ impl AppState {
_ => return, _ => return,
}; };
let mut pos = Point::new(current.dest().x(), current.dest().y()); let mut pos = Point::new(current.dest().x(), current.dest().y());
let mut position: usize = caret.text_position(); let mut position: CaretPosition = caret.position().clone();
for character in text.chars() { for character in text.chars() {
buffer.insert(position, character); buffer.insert(position.text_position(), character);
if let Some(rect) = get_text_character_rect(character, renderer) { if let Some(rect) = get_text_character_rect(character, renderer) {
pos = pos + Point::new(rect.width() as i32, 0); pos = pos + Point::new(rect.width() as i32, 0);
position += 1; position = CaretPosition::new(
position.text_position() + 1,
position.line_number(),
position.line_position(),
);
caret.move_caret(position, pos.clone()); caret.move_caret(position, pos.clone());
} }
} }
@ -115,9 +135,42 @@ impl AppState {
self.files[self.current_file] = new_file; self.files[self.current_file] = new_file;
} }
// fn current_file(&self) -> Option<&EditorFile> { pub fn insert_new_line(&mut self, renderer: &mut Renderer) {
// self.files.get(self.current_file) 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 current = match file.get_character_at(caret.text_position()) {
Some(c) => c,
_ => return,
};
let mut pos = Point::new(current.dest().x(), current.dest().y());
let mut position: CaretPosition = caret.position().clone();
buffer.insert(position.text_position(), '\n');
if let Some(rect) = get_text_character_rect('\n', renderer) {
pos = Point::new(
self.config.editor_left_margin(),
pos.y() + rect.height() as i32,
);
position = CaretPosition::new(
position.text_position(),
position.line_number() + 1,
0,
);
caret.move_caret(position, pos.clone());
}
let new_file = EditorFile::new(file.path(), buffer, self.config.clone());
self.files[self.current_file] = new_file;
}
fn current_file(&self) -> Option<&EditorFile> {
self.files.get(self.current_file)
}
fn current_file_mut(&mut self) -> Option<&mut EditorFile> { fn current_file_mut(&mut self) -> Option<&mut EditorFile> {
self.files.get_mut(self.current_file) self.files.get_mut(self.current_file)
@ -147,6 +200,54 @@ impl AppState {
UpdateResult::NoOp UpdateResult::NoOp
} }
pub fn move_caret(&mut self, dir: MoveDirection) {
match dir {
MoveDirection::Left => {}
MoveDirection::Right =>
self.move_caret_right(),
MoveDirection::Up => {}
MoveDirection::Down => {}
}
}
fn move_caret_right(&mut self) {
let file: &EditorFile = match self.current_file() {
None => return,
Some(f) => f,
};
let line = self.caret.line_number().clone();
let characters: Vec<&TextCharacter> = match file.get_line(&line) {
None =>
return,
Some(characters) => characters,
};
let mut idx = 0;
for (i, c) in characters.iter().enumerate() {
if c.position() == self.caret.text_position() {
idx = i + 1;
break;
}
};
let text_character: &TextCharacter = match characters.get(idx) {
Some(text_character) => text_character,
None => return,
};
let line = text_character.line() - self.caret.line_number();
let pos = self.caret
.position()
.moved(1, line, 0);
let mut d: Rect = text_character.dest().clone();
if text_character.is_new_line() {
let prev = match characters.get(idx - 1) {
Some(c) => c,
_ => return,
};
d = prev.dest().clone();
d.set_x(d.x() + d.width() as i32);
}
self.caret.move_caret(pos, Point::new(d.x(), d.y()));
}
} }
impl Render for AppState { impl Render for AppState {

View File

@ -3,6 +3,7 @@ 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 crate::ui::caret::{CaretPosition,MoveDirection};
use std::rc::Rc; use std::rc::Rc;
use std::thread::sleep; use std::thread::sleep;
@ -30,10 +31,15 @@ pub enum UpdateResult {
Stop, Stop,
RefreshPositions, RefreshPositions,
MouseLeftClicked(Point), MouseLeftClicked(Point),
MoveCaret(Rect, usize), MoveCaret(Rect, CaretPosition),
DeleteFront, DeleteFront,
DeleteBack, DeleteBack,
Input(String), Input(String),
InsertNewLine,
MoveCaretLeft,
MoveCaretRight,
MoveCaretUp,
MoveCaretDown,
} }
pub enum Task { pub enum Task {
@ -109,9 +115,23 @@ impl Application {
app_state.delete_back(); app_state.delete_back();
} }
UpdateResult::Input(text) => { UpdateResult::Input(text) => {
println!("text input: {}", text);
app_state.insert_text(text, &mut renderer); app_state.insert_text(text, &mut renderer);
} }
UpdateResult::InsertNewLine => {
app_state.insert_new_line(&mut renderer);
}
UpdateResult::MoveCaretLeft => {
app_state.move_caret(MoveDirection::Left);
}
UpdateResult::MoveCaretRight => {
app_state.move_caret(MoveDirection::Right);
}
UpdateResult::MoveCaretUp => {
app_state.move_caret(MoveDirection::Up);
}
UpdateResult::MoveCaretDown => {
app_state.move_caret(MoveDirection::Down);
}
} }
for task in self.tasks.iter() { for task in self.tasks.iter() {
match task { match task {
@ -165,6 +185,11 @@ impl Application {
match keycode { match keycode {
Keycode::Backspace => return UpdateResult::DeleteFront, Keycode::Backspace => return UpdateResult::DeleteFront,
Keycode::Delete => return UpdateResult::DeleteBack, Keycode::Delete => return UpdateResult::DeleteBack,
Keycode::KpEnter | Keycode::Return => return UpdateResult::InsertNewLine,
Keycode::Left => return UpdateResult::MoveCaretLeft,
Keycode::Right => return UpdateResult::MoveCaretRight,
Keycode::Up => return UpdateResult::MoveCaretUp,
Keycode::Down => return UpdateResult::MoveCaretDown,
_ => UpdateResult::NoOp, _ => UpdateResult::NoOp,
}; };
} }

View File

@ -7,6 +7,7 @@ use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
use sdl2::render::Texture; use sdl2::render::Texture;
use std::rc::Rc; use std::rc::Rc;
use std::ops::Deref;
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
enum CaretState { enum CaretState {
@ -15,42 +16,159 @@ enum CaretState {
} }
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct Caret { pub enum MoveDirection {
pending: bool, Left,
Right,
Up,
Down
}
//#[derive(Clone, Debug, PartialEq)]
//pub enum CaretLocation {
// FirstLineFirstCharacter,
// FirstLine(usize), // with character location
// LastLineFirstCharacter,
// LastLine(usize), // with character location
// FirstCharacter(usize),// with line number
// LastCharacter(usize), // with line number
// Other(usize, usize), // with line number and character number
//}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct CaretPosition {
text_position: usize, text_position: usize,
blink_delay: u8, line_number: usize,
// config: Rc<Config>, line_position: usize,
state: CaretState, }
impl CaretPosition {
pub fn new(text_position: usize, line_number: usize, line_position: usize,) -> Self {
Self {
text_position,
line_number,
line_position
}
}
pub fn text_position(&self) -> usize {
self.text_position.clone()
}
pub fn line_number(&self) -> usize {
self.line_number.clone()
}
pub fn line_position(&self) -> usize {
self.line_position.clone()
}
pub fn reset(&mut self) {
self.text_position = 0;
self.line_number = 0;
self.line_position = 0;
}
pub fn set_text_position(&mut self, n: usize) {
self.text_position = n;
}
pub fn set_line_number(&mut self, n: usize) {
self.line_number = n;
}
pub fn set_line_position(&mut self, n: usize) {
self.line_position = n;
}
pub fn moved(&self, text_position: usize, line_number: usize, line_position: usize) -> Self {
Self {
text_position: self.text_position + text_position,
line_number: self.line_number + line_number,
line_position: self.line_position + line_position
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CaretRenderPosition {
dest: Rect, dest: Rect,
reset_position: Rect, reset_position: Rect,
bright_character_color: Color, }
blur_character_color: Color,
impl CaretRenderPosition {
pub fn dest(&self) -> &Rect {
&self.dest
}
pub fn reset_position(&self) -> &Rect {
&self.reset_position
}
pub fn reset(&mut self) {
self.dest = self.reset_position.clone()
}
pub fn move_to(&mut self, p: &Point) {
self.dest.set_x(p.x());
self.dest.set_y(p.y());
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CaretColor {
bright: Color,
blur: Color,
}
impl CaretColor {
pub fn bright(&self) -> &Color {
&self.bright
}
pub fn blur(&self) -> &Color {
&self.blur
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Caret {
pending: bool,
blink_delay: u8,
state: CaretState,
position: CaretPosition,
render_position: CaretRenderPosition,
colors: CaretColor,
} }
impl Caret { impl Caret {
pub fn new(config: Rc<Config>) -> Self { pub fn new(config: Rc<Config>) -> Self {
let bright_character_color = config.theme().caret().bright().color().into(); let bright = config.theme().caret().bright().color().into();
let blur_character_color = config.theme().caret().blur().color().into(); let blur = config.theme().caret().blur().color().into();
Self { Self {
state: CaretState::Bright, state: CaretState::Bright,
blink_delay: 0, blink_delay: 0,
dest: Rect::new( render_position: CaretRenderPosition {
config.editor_left_margin(), dest: Rect::new(
config.editor_top_margin(), config.editor_left_margin(),
4, config.editor_top_margin(),
0, 4,
), 0,
reset_position: Rect::new( ),
config.editor_left_margin(), reset_position: Rect::new(
config.editor_top_margin(), config.editor_left_margin(),
4, config.editor_top_margin(),
0, 4,
), 0,
bright_character_color, ),
blur_character_color, },
colors: CaretColor { bright, blur },
pending: true, pending: true,
text_position: 0, position: CaretPosition {
// config, text_position: 0,
line_number: 0,
line_position: 0,
},
// config,
} }
} }
@ -63,44 +181,48 @@ impl Caret {
} }
pub fn reset_caret(&mut self) { pub fn reset_caret(&mut self) {
self.dest = self.reset_position.clone(); self.render_position.reset();
self.text_position = 0; self.position.reset();
} }
pub fn move_caret(&mut self, position: usize, pos: Point) { pub fn move_caret(&mut self, position: CaretPosition, pos: Point) {
self.text_position = position; self.position = position;
self.dest.set_x(pos.x()); self.render_position.move_to(&pos);
self.dest.set_y(pos.y());
} }
pub fn text_position(&self) -> usize { pub fn position(&self) -> &CaretPosition {
self.text_position &self.position
}
}
impl Deref for Caret {
type Target = CaretPosition;
fn deref(&self) -> &<Self as Deref>::Target {
self.position()
} }
} }
impl Render for Caret { impl Render for Caret {
fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult { fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult {
if self.pending { if self.pending {
use crate::renderer::managers::FontDetails; if let Some(rect) = get_text_character_rect('W', renderer) {
let config = renderer.config().clone(); let mut dest = self.render_position.dest().clone();
let font = renderer dest.set_height(rect.height());
.font_manager() let reset_position = dest.clone();
.load(&FontDetails { self.render_position = CaretRenderPosition {
path: config.editor_config().font_path().clone(), dest,
size: config.editor_config().character_size(), reset_position,
}) };
.unwrap_or_else(|_| panic!("Unable to load font"));
if let Ok((_, h)) = font.size_of_char('W') {
self.dest.set_height(h);
self.reset_position = self.dest.clone();
} }
self.pending = false; self.pending = false;
} }
let start = Point::new(self.dest.x(), self.dest.y()); let dest = self.render_position.dest();
let end = Point::new(self.dest.x(), self.dest.y() + self.dest.height() as i32); let start = Point::new(dest.x(), dest.y());
let end = Point::new(dest.x(), dest.y() + dest.height() as i32);
let color = match self.state { let color = match self.state {
CaretState::Bright => &self.bright_character_color, CaretState::Bright => self.colors.bright(),
CaretState::Blur => &self.blur_character_color, CaretState::Blur => self.colors.blur(),
}; };
canvas.set_draw_color(color.clone()); canvas.set_draw_color(color.clone());
canvas canvas
@ -128,6 +250,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.dest) is_in_rect(point, &self.render_position.dest())
} }
} }

View File

@ -48,6 +48,23 @@ impl EditorFile {
None None
} }
pub fn get_line(&self, line: &usize) -> Option<Vec<&TextCharacter>> {
let mut vec: Vec<&TextCharacter> = vec![];
for section in self.sections.iter() {
match section.get_line(line) {
Some(v) => vec.append(&mut v.clone()),
_ => (),
}
}
if vec.is_empty() {
None
} else {
Some(vec)
}
}
fn refresh_characters_position(&mut self) { fn refresh_characters_position(&mut self) {
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() {

View File

@ -48,6 +48,21 @@ impl EditorFileSection {
} }
None None
} }
pub fn get_line(&self, line: &usize) -> Option<Vec<&TextCharacter>> {
let mut vec: Vec<&TextCharacter> = vec![];
for token in self.tokens.iter() {
match token.get_line(line) {
Some(v) => vec.append(&mut v.clone()),
_ => (),
};
}
if vec.is_empty() {
None
} else {
Some(vec)
}
}
} }
impl Render for EditorFileSection { impl Render for EditorFileSection {

View File

@ -42,6 +42,33 @@ impl EditorFileToken {
None None
} }
pub fn get_line(&self, line: &usize) -> Option<Vec<&TextCharacter>> {
let mut vec: Vec<&TextCharacter> = vec![];
for c in self.characters.iter() {
let _tmp = (line.clone(), c.line().clone(), self.token_type.is_new_line(), c.text_character());
match (line.clone(), c.line().clone(), self.token_type.is_new_line()) {
(0, 0, true) => {
vec.push(c);
},
(a, b, true) if (a + 1) == b => {
vec.push(c);
},
(a, b, true) if a != (b + 1) =>
(),
(a, b, false) if a == b => {
vec.push(c);
}
_t =>
(),
}
}
if vec.is_empty() {
None
} else {
Some(vec)
}
}
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 {

View File

@ -1,10 +1,10 @@
use crate::app::{UpdateResult, WindowCanvas}; use crate::app::{UpdateResult, WindowCanvas};
use crate::config::Config; use crate::config::Config;
use crate::lexer::TokenType; use crate::lexer::TokenType;
use crate::renderer::managers::FontDetails; use crate::renderer::managers::{TextDetails, FontDetails};
use crate::renderer::managers::TextDetails;
use crate::renderer::Renderer; use crate::renderer::Renderer;
use crate::ui::*; use crate::ui::*;
use crate::ui::caret::CaretPosition;
use sdl2::pixels::Color; use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect}; use sdl2::rect::{Point, Rect};
@ -79,7 +79,11 @@ 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));
if let Some(rect) = get_text_character_rect(self.text_character.clone(), renderer) { let c = match self.text_character {
'\n' => 'W',
c => c,
};
if let Some(rect) = get_text_character_rect(c, renderer) {
self.source = rect.clone(); self.source = rect.clone();
self.dest = rect.clone(); self.dest = rect.clone();
} }
@ -98,7 +102,7 @@ impl TextCharacter {
} }
#[inline] #[inline]
fn is_new_line(&self) -> bool { pub fn is_new_line(&self) -> bool {
self.text_character == '\n' self.text_character == '\n'
} }
@ -110,6 +114,14 @@ impl TextCharacter {
pub fn position(&self) -> usize { pub fn position(&self) -> usize {
self.position self.position
} }
pub fn line(&self) -> usize {
self.line
}
pub fn text_character(&self) -> char {
self.text_character.clone()
}
} }
impl Render for TextCharacter { impl Render for TextCharacter {
@ -154,7 +166,14 @@ impl Update for TextCharacter {
impl ClickHandler for TextCharacter { impl ClickHandler for TextCharacter {
fn on_left_click(&mut self, _point: &Point) -> UpdateResult { fn on_left_click(&mut self, _point: &Point) -> UpdateResult {
UpdateResult::MoveCaret(self.dest().clone(), self.position()) UpdateResult::MoveCaret(
self.dest().clone(),
CaretPosition::new(
self.position(),
self.line(),
0,
),
)
} }
fn is_left_click_target(&self, point: &Point) -> bool { fn is_left_click_target(&self, point: &Point) -> bool {