Scrolling
This commit is contained in:
parent
1c237c9cb9
commit
a67f78680f
@ -8,3 +8,4 @@ edition = "2018"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
termion = "1"
|
termion = "1"
|
||||||
|
unicode-segmentation = "1.7.1"
|
||||||
|
@ -28,4 +28,8 @@ impl Document {
|
|||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.rows.is_empty()
|
self.rows.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.rows.len()
|
||||||
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@ pub struct Editor {
|
|||||||
should_quit: bool,
|
should_quit: bool,
|
||||||
terminal: Terminal,
|
terminal: Terminal,
|
||||||
cursor_position: Position,
|
cursor_position: Position,
|
||||||
|
offset: Position,
|
||||||
document: Document,
|
document: Document,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,6 +49,7 @@ impl Editor {
|
|||||||
terminal: Terminal::default().expect("Failed to initialize terminal"),
|
terminal: Terminal::default().expect("Failed to initialize terminal"),
|
||||||
document,
|
document,
|
||||||
cursor_position: Position::default(),
|
cursor_position: Position::default(),
|
||||||
|
offset: Position::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +61,10 @@ impl Editor {
|
|||||||
println!("Goodbye.\r");
|
println!("Goodbye.\r");
|
||||||
} else {
|
} else {
|
||||||
self.draw_rows();
|
self.draw_rows();
|
||||||
Terminal::cursor_position(&self.cursor_position);
|
Terminal::cursor_position(&Position {
|
||||||
|
x: self.cursor_position.x.saturating_sub(self.offset.x),
|
||||||
|
y: self.cursor_position.y.saturating_sub(self.offset.y),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
Terminal::cursor_show();
|
Terminal::cursor_show();
|
||||||
Terminal::flush()
|
Terminal::flush()
|
||||||
@ -79,14 +84,41 @@ impl Editor {
|
|||||||
| Key::Home => self.move_cursor(pressed_key),
|
| Key::Home => self.move_cursor(pressed_key),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.scroll();
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn scroll(&mut self) {
|
||||||
|
let Position { x, y } = self.cursor_position;
|
||||||
|
let width = self.terminal.size().width as usize;
|
||||||
|
let height = self.terminal.size().height as usize;
|
||||||
|
|
||||||
|
let mut offset = &mut self.offset;
|
||||||
|
|
||||||
|
if y < offset.y {
|
||||||
|
offset.y = y;
|
||||||
|
} else if y >= offset.y.saturating_add(height) {
|
||||||
|
offset.y = y.saturating_sub(height).saturating_add(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if x < offset.x {
|
||||||
|
offset.x = x;
|
||||||
|
} else if x >= offset.x.saturating_add(width) {
|
||||||
|
offset.x = x.saturating_sub(width).saturating_add(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn move_cursor(&mut self, key: Key) {
|
fn move_cursor(&mut self, key: Key) {
|
||||||
|
let terminal_height = self.terminal.size().height as usize;
|
||||||
let Position { mut y, mut x } = self.cursor_position;
|
let Position { mut y, mut x } = self.cursor_position;
|
||||||
let size = self.terminal.size();
|
let height = self.document.len();
|
||||||
let height = size.height.saturating_sub(1) as usize;
|
let mut width = if let Some(row) = self.document.row(y) {
|
||||||
let width = size.width.saturating_sub(1) as usize;
|
row.len()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
match key {
|
match key {
|
||||||
Key::Up => y = y.saturating_sub(1),
|
Key::Up => y = y.saturating_sub(1),
|
||||||
@ -95,19 +127,55 @@ impl Editor {
|
|||||||
y = y.saturating_add(1);
|
y = y.saturating_add(1);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Key::Left => x = x.saturating_sub(1),
|
Key::Left => {
|
||||||
Key::Right => {
|
if x > 0 {
|
||||||
if x < width {
|
x -= 1;
|
||||||
x = x.saturating_add(1);
|
} else if y > 0 {
|
||||||
|
y -= 1;
|
||||||
|
if let Some(row) = self.document.row(y) {
|
||||||
|
x = row.len()
|
||||||
|
} else {
|
||||||
|
x = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Key::Right => {
|
||||||
|
if x < width {
|
||||||
|
x += 1;
|
||||||
|
} else if y < height {
|
||||||
|
y += 1;
|
||||||
|
x = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Key::PageUp => {
|
||||||
|
y = if y > terminal_height {
|
||||||
|
y - terminal_height
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Key::PageDown => {
|
||||||
|
y = if y.saturating_add(terminal_height) < height {
|
||||||
|
y + terminal_height as usize
|
||||||
|
} else {
|
||||||
|
height
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Key::PageUp => y = 0,
|
|
||||||
Key::PageDown => y = height,
|
|
||||||
Key::Home => x = 0,
|
Key::Home => x = 0,
|
||||||
Key::End => x = width,
|
Key::End => x = width,
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
width = if let Some(row) = self.document.row(y) {
|
||||||
|
row.len()
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
if x > width {
|
||||||
|
x = width;
|
||||||
|
}
|
||||||
|
|
||||||
self.cursor_position = Position { x, y }
|
self.cursor_position = Position { x, y }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,8 +191,9 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_row(&self, row: &Row) {
|
pub fn draw_row(&self, row: &Row) {
|
||||||
let start = 0;
|
let width = self.terminal.size().width as usize;
|
||||||
let end = self.terminal.size().width as usize;
|
let start = self.offset.x;
|
||||||
|
let end = self.offset.x + width;
|
||||||
let row = row.render(start, end);
|
let row = row.render(start, end);
|
||||||
println!("{}\r", row);
|
println!("{}\r", row);
|
||||||
}
|
}
|
||||||
@ -132,10 +201,10 @@ impl Editor {
|
|||||||
fn draw_rows(&self) {
|
fn draw_rows(&self) {
|
||||||
let height = self.terminal.size().height;
|
let height = self.terminal.size().height;
|
||||||
|
|
||||||
for terminal_row in 0..height - 1 {
|
for terminal_row in 0..height {
|
||||||
Terminal::clear_current_line();
|
Terminal::clear_current_line();
|
||||||
|
|
||||||
if let Some(row) = self.document.row(terminal_row as usize) {
|
if let Some(row) = self.document.row(terminal_row as usize + self.offset.y) {
|
||||||
self.draw_row(row);
|
self.draw_row(row);
|
||||||
} else if self.document.is_empty() && terminal_row == height / 3 {
|
} else if self.document.is_empty() && terminal_row == height / 3 {
|
||||||
self.draw_welcome_message();
|
self.draw_welcome_message();
|
||||||
|
39
src/row.rs
39
src/row.rs
@ -1,14 +1,20 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
|
use unicode_segmentation::UnicodeSegmentation;
|
||||||
|
|
||||||
pub struct Row {
|
pub struct Row {
|
||||||
string: String,
|
string: String,
|
||||||
|
len: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&str> for Row {
|
impl From<&str> for Row {
|
||||||
fn from(slice: &str) -> Self {
|
fn from(slice: &str) -> Self {
|
||||||
Self {
|
let mut row = Self {
|
||||||
string: String::from(slice),
|
string: String::from(slice),
|
||||||
}
|
len: 0,
|
||||||
|
};
|
||||||
|
row.update_len();
|
||||||
|
row
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,6 +22,33 @@ impl Row {
|
|||||||
pub fn render(&self, start: usize, end: usize) -> String {
|
pub fn render(&self, start: usize, end: usize) -> String {
|
||||||
let end = cmp::min(end, self.string.len());
|
let end = cmp::min(end, self.string.len());
|
||||||
let start = cmp::min(start, end);
|
let start = cmp::min(start, end);
|
||||||
self.string.get(start..end).unwrap_or_default().to_string()
|
|
||||||
|
let mut result = String::new();
|
||||||
|
|
||||||
|
for grapheme in self.string[..]
|
||||||
|
.graphemes(true)
|
||||||
|
.skip(start)
|
||||||
|
.take(end - start)
|
||||||
|
{
|
||||||
|
if grapheme == "\t" {
|
||||||
|
result.push_str(" ");
|
||||||
|
} else {
|
||||||
|
result.push_str(grapheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn len(&self) -> usize {
|
||||||
|
self.len
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.len == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_len(&mut self) {
|
||||||
|
self.len = self.string[..].graphemes(true).count();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -20,7 +20,7 @@ impl Terminal {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
size: Size {
|
size: Size {
|
||||||
width: size.0,
|
width: size.0,
|
||||||
height: size.1,
|
height: size.1.saturating_sub(2),
|
||||||
},
|
},
|
||||||
_stdout: stdout().into_raw_mode()?,
|
_stdout: stdout().into_raw_mode()?,
|
||||||
})
|
})
|
||||||
|
Loading…
Reference in New Issue
Block a user