Move caret right
This commit is contained in:
parent
9460b6931a
commit
3a5587dbcc
@ -5,6 +5,8 @@ use crate::ui::caret::Caret;
|
||||
use crate::ui::file::editor_file::EditorFile;
|
||||
use crate::ui::file::*;
|
||||
use crate::ui::menu_bar::MenuBar;
|
||||
use crate::ui::caret::{CaretPosition, MoveDirection};
|
||||
use crate::ui::text_character::TextCharacter;
|
||||
use crate::ui::*;
|
||||
use sdl2::rect::{Point, Rect};
|
||||
use sdl2::VideoSubsystem;
|
||||
@ -52,15 +54,29 @@ impl AppState {
|
||||
};
|
||||
let mut buffer: String = file.buffer();
|
||||
let caret: &mut Caret = &mut self.caret;
|
||||
let position: usize = caret.text_position();
|
||||
if position == 0 {
|
||||
let position: CaretPosition = caret.position().clone();
|
||||
if position.text_position() == 0 {
|
||||
return;
|
||||
}
|
||||
buffer.remove(position - 1);
|
||||
match file.get_character_at(position - 1) {
|
||||
let c: char = buffer.chars().collect::<Vec<char>>()[position.text_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) => {
|
||||
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();
|
||||
@ -101,12 +117,16 @@ impl AppState {
|
||||
_ => return,
|
||||
};
|
||||
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() {
|
||||
buffer.insert(position, character);
|
||||
buffer.insert(position.text_position(), character);
|
||||
if let Some(rect) = get_text_character_rect(character, renderer) {
|
||||
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());
|
||||
}
|
||||
}
|
||||
@ -115,9 +135,42 @@ impl AppState {
|
||||
self.files[self.current_file] = new_file;
|
||||
}
|
||||
|
||||
// fn current_file(&self) -> Option<&EditorFile> {
|
||||
// self.files.get(self.current_file)
|
||||
// }
|
||||
pub fn insert_new_line(&mut self, 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 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> {
|
||||
self.files.get_mut(self.current_file)
|
||||
@ -147,6 +200,54 @@ impl AppState {
|
||||
|
||||
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 {
|
||||
|
@ -3,6 +3,7 @@ use crate::config::Config;
|
||||
use crate::renderer::Renderer;
|
||||
use crate::themes::*;
|
||||
use crate::ui::*;
|
||||
use crate::ui::caret::{CaretPosition,MoveDirection};
|
||||
|
||||
use std::rc::Rc;
|
||||
use std::thread::sleep;
|
||||
@ -30,10 +31,15 @@ pub enum UpdateResult {
|
||||
Stop,
|
||||
RefreshPositions,
|
||||
MouseLeftClicked(Point),
|
||||
MoveCaret(Rect, usize),
|
||||
MoveCaret(Rect, CaretPosition),
|
||||
DeleteFront,
|
||||
DeleteBack,
|
||||
Input(String),
|
||||
InsertNewLine,
|
||||
MoveCaretLeft,
|
||||
MoveCaretRight,
|
||||
MoveCaretUp,
|
||||
MoveCaretDown,
|
||||
}
|
||||
|
||||
pub enum Task {
|
||||
@ -109,9 +115,23 @@ impl Application {
|
||||
app_state.delete_back();
|
||||
}
|
||||
UpdateResult::Input(text) => {
|
||||
println!("text input: {}", text);
|
||||
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() {
|
||||
match task {
|
||||
@ -165,6 +185,11 @@ impl Application {
|
||||
match keycode {
|
||||
Keycode::Backspace => return UpdateResult::DeleteFront,
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
196
src/ui/caret.rs
196
src/ui/caret.rs
@ -7,6 +7,7 @@ use sdl2::pixels::Color;
|
||||
use sdl2::rect::{Point, Rect};
|
||||
use sdl2::render::Texture;
|
||||
use std::rc::Rc;
|
||||
use std::ops::Deref;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
enum CaretState {
|
||||
@ -15,25 +16,138 @@ enum CaretState {
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Caret {
|
||||
pending: bool,
|
||||
pub enum MoveDirection {
|
||||
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,
|
||||
blink_delay: u8,
|
||||
// config: Rc<Config>,
|
||||
state: CaretState,
|
||||
line_number: usize,
|
||||
line_position: usize,
|
||||
}
|
||||
|
||||
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,
|
||||
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 {
|
||||
pub fn new(config: Rc<Config>) -> Self {
|
||||
let bright_character_color = config.theme().caret().bright().color().into();
|
||||
let blur_character_color = config.theme().caret().blur().color().into();
|
||||
let bright = config.theme().caret().bright().color().into();
|
||||
let blur = config.theme().caret().blur().color().into();
|
||||
Self {
|
||||
state: CaretState::Bright,
|
||||
blink_delay: 0,
|
||||
render_position: CaretRenderPosition {
|
||||
dest: Rect::new(
|
||||
config.editor_left_margin(),
|
||||
config.editor_top_margin(),
|
||||
@ -46,11 +160,15 @@ impl Caret {
|
||||
4,
|
||||
0,
|
||||
),
|
||||
bright_character_color,
|
||||
blur_character_color,
|
||||
},
|
||||
colors: CaretColor { bright, blur },
|
||||
pending: true,
|
||||
position: CaretPosition {
|
||||
text_position: 0,
|
||||
// config,
|
||||
line_number: 0,
|
||||
line_position: 0,
|
||||
},
|
||||
// config,
|
||||
}
|
||||
}
|
||||
|
||||
@ -63,44 +181,48 @@ impl Caret {
|
||||
}
|
||||
|
||||
pub fn reset_caret(&mut self) {
|
||||
self.dest = self.reset_position.clone();
|
||||
self.text_position = 0;
|
||||
self.render_position.reset();
|
||||
self.position.reset();
|
||||
}
|
||||
|
||||
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 move_caret(&mut self, position: CaretPosition, pos: Point) {
|
||||
self.position = position;
|
||||
self.render_position.move_to(&pos);
|
||||
}
|
||||
|
||||
pub fn text_position(&self) -> usize {
|
||||
self.text_position
|
||||
pub fn position(&self) -> &CaretPosition {
|
||||
&self.position
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Caret {
|
||||
type Target = CaretPosition;
|
||||
|
||||
fn deref(&self) -> &<Self as Deref>::Target {
|
||||
self.position()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for Caret {
|
||||
fn render(&mut self, canvas: &mut WindowCanvas, renderer: &mut Renderer) -> UpdateResult {
|
||||
if self.pending {
|
||||
use crate::renderer::managers::FontDetails;
|
||||
let config = renderer.config().clone();
|
||||
let font = renderer
|
||||
.font_manager()
|
||||
.load(&FontDetails {
|
||||
path: config.editor_config().font_path().clone(),
|
||||
size: config.editor_config().character_size(),
|
||||
})
|
||||
.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();
|
||||
if let Some(rect) = get_text_character_rect('W', renderer) {
|
||||
let mut dest = self.render_position.dest().clone();
|
||||
dest.set_height(rect.height());
|
||||
let reset_position = dest.clone();
|
||||
self.render_position = CaretRenderPosition {
|
||||
dest,
|
||||
reset_position,
|
||||
};
|
||||
}
|
||||
self.pending = false;
|
||||
}
|
||||
let start = Point::new(self.dest.x(), self.dest.y());
|
||||
let end = Point::new(self.dest.x(), self.dest.y() + self.dest.height() as i32);
|
||||
let dest = self.render_position.dest();
|
||||
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 {
|
||||
CaretState::Bright => &self.bright_character_color,
|
||||
CaretState::Blur => &self.blur_character_color,
|
||||
CaretState::Bright => self.colors.bright(),
|
||||
CaretState::Blur => self.colors.blur(),
|
||||
};
|
||||
canvas.set_draw_color(color.clone());
|
||||
canvas
|
||||
@ -128,6 +250,6 @@ impl ClickHandler for Caret {
|
||||
}
|
||||
|
||||
fn is_left_click_target(&self, point: &Point) -> bool {
|
||||
is_in_rect(point, &self.dest)
|
||||
is_in_rect(point, &self.render_position.dest())
|
||||
}
|
||||
}
|
||||
|
@ -48,6 +48,23 @@ impl EditorFile {
|
||||
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) {
|
||||
let mut current: Rect = self.render_position.clone();
|
||||
for section in self.sections.iter_mut() {
|
||||
|
@ -48,6 +48,21 @@ impl EditorFileSection {
|
||||
}
|
||||
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 {
|
||||
|
@ -42,6 +42,33 @@ impl EditorFileToken {
|
||||
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 {
|
||||
let config = renderer.config().theme().code_highlighting();
|
||||
let color: Color = match self.token_type {
|
||||
|
@ -1,10 +1,10 @@
|
||||
use crate::app::{UpdateResult, WindowCanvas};
|
||||
use crate::config::Config;
|
||||
use crate::lexer::TokenType;
|
||||
use crate::renderer::managers::FontDetails;
|
||||
use crate::renderer::managers::TextDetails;
|
||||
use crate::renderer::managers::{TextDetails, FontDetails};
|
||||
use crate::renderer::Renderer;
|
||||
use crate::ui::*;
|
||||
use crate::ui::caret::CaretPosition;
|
||||
|
||||
use sdl2::pixels::Color;
|
||||
use sdl2::rect::{Point, Rect};
|
||||
@ -79,7 +79,11 @@ impl TextCharacter {
|
||||
.load(&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.dest = rect.clone();
|
||||
}
|
||||
@ -98,7 +102,7 @@ impl TextCharacter {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn is_new_line(&self) -> bool {
|
||||
pub fn is_new_line(&self) -> bool {
|
||||
self.text_character == '\n'
|
||||
}
|
||||
|
||||
@ -110,6 +114,14 @@ impl TextCharacter {
|
||||
pub fn position(&self) -> usize {
|
||||
self.position
|
||||
}
|
||||
|
||||
pub fn line(&self) -> usize {
|
||||
self.line
|
||||
}
|
||||
|
||||
pub fn text_character(&self) -> char {
|
||||
self.text_character.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for TextCharacter {
|
||||
@ -154,7 +166,14 @@ impl Update for TextCharacter {
|
||||
|
||||
impl ClickHandler for TextCharacter {
|
||||
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 {
|
||||
|
Loading…
Reference in New Issue
Block a user