Scrolling in the editor by following the cursor now works.

This commit is contained in:
Nathan Vegdahl 2014-12-27 13:59:41 -08:00
parent 833e92c5a0
commit 74cd79745f
3 changed files with 158 additions and 18 deletions

View File

@ -61,6 +61,7 @@ impl TextBuffer {
} }
/// Creates an iterator at the first character
pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> { pub fn root_iter<'a>(&'a self) -> TextBufferIter<'a> {
let mut node_stack: Vec<&'a TextNode> = Vec::new(); let mut node_stack: Vec<&'a TextNode> = Vec::new();
let mut cur_node = &self.root; let mut cur_node = &self.root;
@ -86,6 +87,49 @@ impl TextBuffer {
} }
} }
} }
/// Creates an iterator starting at the specified character index.
/// If the index is past the end of the text, then the iterator will
/// return None on next().
pub fn iter_at_char<'a>(&'a self, index: uint) -> TextBufferIter<'a> {
let mut node_stack: Vec<&'a TextNode> = Vec::new();
let mut cur_node = &self.root;
let mut char_i = index;
loop {
match cur_node.data {
TextNodeData::Leaf(_) => {
let mut char_iter = match cur_node.data {
TextNodeData::Leaf(ref tb) => tb.as_str().chars(),
_ => panic!("This should never happen.")
};
while char_i > 0 {
char_iter.next();
char_i -= 1;
}
return TextBufferIter {
node_stack: node_stack,
cur_block: char_iter,
};
},
TextNodeData::Branch(ref left, ref right) => {
if left.char_count > char_i {
node_stack.push(&(**right));
cur_node = &(**left);
}
else {
cur_node = &(**right);
char_i -= left.char_count;
}
}
}
}
}
} }
impl fmt::Show for TextBuffer { impl fmt::Show for TextBuffer {
@ -129,6 +173,30 @@ impl<'a> TextBufferIter<'a> {
} }
} }
} }
// Skips the iterator n characters ahead, unless it hits a newline
// character. If it hits a newline character, returns true, otherwise,
// false.
pub fn skip_non_newline_chars(&mut self, n: uint) -> bool {
// TODO: more efficient implementation, taking advantage of rope
// structure.
for _ in range(0, n) {
match self.next() {
Option::Some(c) => {
if c == '\n' {
return true;
}
},
Option::None => {
break;
}
}
}
return false;
}
} }

View File

@ -57,6 +57,26 @@ impl Editor {
self.view_dim = (h, w); self.view_dim = (h, w);
} }
/// Moves the editor's view the minimum amount to show the cursor
pub fn move_view_to_cursor(&mut self) {
// Horizontal
if self.cursor.1 < self.view_pos.1 {
self.view_pos.1 = self.cursor.1;
}
else if self.cursor.1 >= (self.view_pos.1 + self.view_dim.1) {
self.view_pos.1 = 1 + self.cursor.1 - self.view_dim.1;
}
// Vertical
if self.cursor.0 < self.view_pos.0 {
self.view_pos.0 = self.cursor.0;
}
else if self.cursor.0 >= (self.view_pos.0 + self.view_dim.0) {
self.view_pos.0 = 1 + self.cursor.0 - self.view_dim.0;
}
}
pub fn insert_text_at_cursor(&mut self, text: &str) { pub fn insert_text_at_cursor(&mut self, text: &str) {
let pos = self.buffer.pos_2d_to_closest_1d(self.cursor); let pos = self.buffer.pos_2d_to_closest_1d(self.cursor);
let str_len = char_count(text); let str_len = char_count(text);
@ -68,6 +88,8 @@ impl Editor {
// Move cursor // Move cursor
self.cursor = self.buffer.pos_1d_to_closest_2d(p + str_len); self.cursor = self.buffer.pos_1d_to_closest_2d(p + str_len);
self.move_view_to_cursor();
} }
pub fn insert_text_at_char(&mut self, text: &str, pos: uint) { pub fn insert_text_at_char(&mut self, text: &str, pos: uint) {
@ -87,6 +109,8 @@ impl Editor {
self.buffer.remove_text(pos_a, pos_b); self.buffer.remove_text(pos_a, pos_b);
self.dirty = true; self.dirty = true;
self.move_view_to_cursor();
} }
pub fn cursor_left(&mut self) { pub fn cursor_left(&mut self) {
@ -98,22 +122,30 @@ impl Editor {
else { else {
self.cursor = self.buffer.pos_1d_to_closest_2d(0); self.cursor = self.buffer.pos_1d_to_closest_2d(0);
} }
self.move_view_to_cursor();
} }
pub fn cursor_right(&mut self) { pub fn cursor_right(&mut self) {
let p = self.buffer.pos_2d_to_closest_1d(self.cursor); let p = self.buffer.pos_2d_to_closest_1d(self.cursor);
self.cursor = self.buffer.pos_1d_to_closest_2d(p + 1); self.cursor = self.buffer.pos_1d_to_closest_2d(p + 1);
self.move_view_to_cursor();
} }
pub fn cursor_up(&mut self) { pub fn cursor_up(&mut self) {
if self.cursor.0 > 0 { if self.cursor.0 > 0 {
self.cursor.0 -= 1; self.cursor.0 -= 1;
} }
self.move_view_to_cursor();
} }
pub fn cursor_down(&mut self) { pub fn cursor_down(&mut self) {
if self.cursor.0 < self.buffer.newline_count() { if self.cursor.0 < self.buffer.newline_count() {
self.cursor.0 += 1; self.cursor.0 += 1;
} }
self.move_view_to_cursor();
} }
} }

View File

@ -48,6 +48,7 @@ impl TermUI {
let mut width = self.rb.width(); let mut width = self.rb.width();
let mut height = self.rb.height(); let mut height = self.rb.height();
self.editor.update_dim(height, width);
loop { loop {
// Draw the editor to screen // Draw the editor to screen
@ -121,6 +122,7 @@ impl TermUI {
Ok(rustbox::Event::ResizeEvent(w, h)) => { Ok(rustbox::Event::ResizeEvent(w, h)) => {
width = w as uint; width = w as uint;
height = h as uint; height = h as uint;
self.editor.update_dim(height, width);
} }
_ => { _ => {
@ -140,12 +142,14 @@ impl TermUI {
} }
pub fn draw_editor(&self, editor: &Editor, c1: (uint, uint), c2: (uint, uint)) { pub fn draw_editor(&self, editor: &Editor, c1: (uint, uint), c2: (uint, uint)) {
let mut tb_iter = editor.buffer.root_iter(); let mut tb_iter = editor.buffer.iter_at_char(editor.buffer.pos_2d_to_closest_1d(editor.view_pos));
let mut line: uint = 0; let mut pline = c1.0;
let mut column: uint = 0; let mut pcol = c1.1;
let mut pos: uint = 0; let mut line = editor.view_pos.0;
let height = c2.0 - c1.0; let mut column = editor.view_pos.1;
let width = c2.1 - c1.1; let mut pos = editor.buffer.pos_2d_to_closest_1d(editor.view_pos);
let max_line = line + (c2.0 - c1.0);
let max_col = column + (c2.1 - c1.1);
let cursor_pos = editor.buffer.pos_2d_to_closest_1d(editor.cursor); let cursor_pos = editor.buffer.pos_2d_to_closest_1d(editor.cursor);
@ -153,20 +157,23 @@ impl TermUI {
if let Option::Some(c) = tb_iter.next() { if let Option::Some(c) = tb_iter.next() {
if c == '\n' { if c == '\n' {
if pos == cursor_pos { if pos == cursor_pos {
self.rb.print(column, line, rustbox::RB_NORMAL, Color::Black, Color::White, " ".to_string().as_slice()); self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::Black, Color::White, " ".to_string().as_slice());
} }
pline += 1;
pcol = c1.1;
line += 1; line += 1;
column = 0; column = 0;
} }
else { else {
if pos == cursor_pos { if pos == cursor_pos {
self.rb.print(column, line, rustbox::RB_NORMAL, Color::Black, Color::White, c.to_string().as_slice()); self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::Black, Color::White, c.to_string().as_slice());
} }
else { else {
self.rb.print(column, line, rustbox::RB_NORMAL, Color::White, Color::Black, c.to_string().as_slice()); self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::White, Color::Black, c.to_string().as_slice());
} }
pcol += 1;
column += 1; column += 1;
} }
} }
@ -174,24 +181,57 @@ impl TermUI {
// Show cursor at end of document if it's past the end of // Show cursor at end of document if it's past the end of
// the document // the document
if cursor_pos >= pos { if cursor_pos >= pos {
self.rb.print(column, line, rustbox::RB_NORMAL, Color::Black, Color::White, " "); self.rb.print(pcol, pline, rustbox::RB_NORMAL, Color::Black, Color::White, " ");
} }
break; return;
} }
pos += 1; if line > max_line {
return;
if line > height {
break;
} }
if column > width { // If we're past the edge of the display, go to the next line
if column > max_col {
tb_iter.next_line(); tb_iter.next_line();
pline += 1;
pcol = c1.1;
line += 1; line += 1;
column = 0; column = 0;
pos = editor.buffer.pos_2d_to_closest_1d((line, column));
if line > max_line {
return;
}
} }
// If we're before the edge of the display, move forward to get
// to it.
loop {
if column < editor.view_pos.1 {
let nl = tb_iter.skip_non_newline_chars(editor.view_pos.1);
if !nl {
column = editor.view_pos.1;
break;
}
else {
pline += 1;
line += 1;
}
if line > max_line {
return;
}
}
else {
break;
}
}
// Get the 1d position of the char to be printed next
pos = editor.buffer.pos_2d_to_closest_1d((line, column));
} }
} }
} }