cursor/screen clamping

This commit is contained in:
alice pellerin
2026-03-17 00:38:40 -05:00
parent 813e48920c
commit 67dc63acec
2 changed files with 57 additions and 15 deletions
+48 -15
View File
@@ -8,7 +8,7 @@ mod widget;
#[derive(Debug)] #[derive(Debug)]
pub struct App { pub struct App {
pub contents: Vec<u8>, pub contents: Vec<u8>,
pub window_rows: u16, pub window_rows: usize,
pub scroll_position: usize, pub scroll_position: usize,
pub cursor: Cursor, pub cursor: Cursor,
pub should_quit: bool pub should_quit: bool
@@ -33,7 +33,7 @@ impl App {
Self { Self {
contents, contents,
window_rows: window_size().unwrap().rows, window_rows: window_size().unwrap().rows as usize,
scroll_position: 0, scroll_position: 0,
cursor: Cursor::default(), cursor: Cursor::default(),
should_quit: false should_quit: false
@@ -45,7 +45,7 @@ impl App {
#[allow(clippy::collapsible_match)] #[allow(clippy::collapsible_match)]
match event::read().unwrap() { match event::read().unwrap() {
Event::Resize(_, height) => { Event::Resize(_, height) => {
self.window_rows = height; self.window_rows = height as usize;
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('q') => { Event::Key(key_event) if key_event.code == KeyCode::Char('q') => {
self.should_quit = true; self.should_quit = true;
@@ -56,89 +56,114 @@ impl App {
self.scroll_position + BYTES_PER_LINE, self.scroll_position + BYTES_PER_LINE,
self.contents.len() - (5 * BYTES_PER_LINE) self.contents.len() - (5 * BYTES_PER_LINE)
); );
self.cursor.clamp(self.scroll_position, self.window_rows);
} }
Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) && Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) &&
key_event.code == KeyCode::Char('y') => { key_event.code == KeyCode::Char('y') => {
self.scroll_position = self.scroll_position.saturating_sub(BYTES_PER_LINE); self.scroll_position = self.scroll_position.saturating_sub(BYTES_PER_LINE);
self.cursor.clamp(self.scroll_position, self.window_rows);
} }
Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) && Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) &&
key_event.code == KeyCode::Char('d') => { key_event.code == KeyCode::Char('d') => {
self.scroll_position = min( self.scroll_position = min(
self.scroll_position + ((self.window_rows / 2) as usize * BYTES_PER_LINE), self.scroll_position + ((self.window_rows / 2) * BYTES_PER_LINE),
self.contents.len() - (5 * BYTES_PER_LINE) self.contents.len() - (5 * BYTES_PER_LINE)
); );
self.cursor.clamp(self.scroll_position, self.window_rows);
} }
Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) && Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) &&
key_event.code == KeyCode::Char('u') => { key_event.code == KeyCode::Char('u') => {
self.scroll_position = self.scroll_position.saturating_sub( self.scroll_position = self.scroll_position.saturating_sub(
(self.window_rows / 2) as usize * BYTES_PER_LINE (self.window_rows / 2) * BYTES_PER_LINE
); );
self.cursor.clamp(self.scroll_position, self.window_rows);
} }
Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) && Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) &&
key_event.code == KeyCode::Char('f') => { key_event.code == KeyCode::Char('f') => {
self.scroll_position = min( self.scroll_position = min(
self.scroll_position + (self.window_rows as usize * BYTES_PER_LINE), self.scroll_position + (self.window_rows * BYTES_PER_LINE),
self.contents.len() - (5 * BYTES_PER_LINE) self.contents.len() - (5 * BYTES_PER_LINE)
); );
self.cursor.clamp(self.scroll_position, self.window_rows);
} }
Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) && Event::Key(key_event) if key_event.modifiers.contains(KeyModifiers::CONTROL) &&
key_event.code == KeyCode::Char('b') => { key_event.code == KeyCode::Char('b') => {
self.scroll_position = self.scroll_position.saturating_sub( self.scroll_position = self.scroll_position.saturating_sub(
self.window_rows as usize * BYTES_PER_LINE self.window_rows * BYTES_PER_LINE
); );
self.cursor.clamp(self.scroll_position, self.window_rows);
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('i') => { Event::Key(key_event) if key_event.code == KeyCode::Char('i') => {
if self.cursor.head >= 0x10 { if self.cursor.head >= BYTES_PER_LINE {
self.cursor.head -= 0x10; self.cursor.head -= BYTES_PER_LINE;
self.cursor.collapse(); self.cursor.collapse();
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('j') => { Event::Key(key_event) if key_event.code == KeyCode::Char('j') => {
if self.cursor.head >= 1 { if self.cursor.head >= 1 {
self.cursor.head -= 1; self.cursor.head -= 1;
self.cursor.collapse(); self.cursor.collapse();
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('k') => { Event::Key(key_event) if key_event.code == KeyCode::Char('k') => {
if self.contents.len() - 1 - self.cursor.head >= 0x10 { if self.contents.len() - 1 - self.cursor.head >= BYTES_PER_LINE {
self.cursor.head += 0x10; self.cursor.head += BYTES_PER_LINE;
self.cursor.collapse(); self.cursor.collapse();
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('l') => { Event::Key(key_event) if key_event.code == KeyCode::Char('l') => {
if self.contents.len() - 1 - self.cursor.head >= 1 { if self.contents.len() - 1 - self.cursor.head >= 1 {
self.cursor.head += 1; self.cursor.head += 1;
self.cursor.collapse(); self.cursor.collapse();
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('I') => { Event::Key(key_event) if key_event.code == KeyCode::Char('I') => {
if self.cursor.head >= 0x10 { if self.cursor.head >= BYTES_PER_LINE {
self.cursor.head -= 0x10; self.cursor.head -= BYTES_PER_LINE;
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('J') => { Event::Key(key_event) if key_event.code == KeyCode::Char('J') => {
if self.cursor.head >= 1 { if self.cursor.head >= 1 {
self.cursor.head -= 1; self.cursor.head -= 1;
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('K') => { Event::Key(key_event) if key_event.code == KeyCode::Char('K') => {
if self.contents.len() - 1 - self.cursor.head >= 0x10 { if self.contents.len() - 1 - self.cursor.head >= BYTES_PER_LINE {
self.cursor.head += 0x10; self.cursor.head += BYTES_PER_LINE;
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('L') => { Event::Key(key_event) if key_event.code == KeyCode::Char('L') => {
if self.contents.len() - 1 - self.cursor.head >= 1 { if self.contents.len() - 1 - self.cursor.head >= 1 {
self.cursor.head += 1; self.cursor.head += 1;
self.clamp_screen_to_cursor();
} }
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('w') => { Event::Key(key_event) if key_event.code == KeyCode::Char('w') => {
self.cursor.move_to_next_word(self.contents.len() - 1); self.cursor.move_to_next_word(self.contents.len() - 1);
self.clamp_screen_to_cursor();
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('e') => { Event::Key(key_event) if key_event.code == KeyCode::Char('e') => {
self.cursor.move_to_next_end(self.contents.len() - 1); self.cursor.move_to_next_end(self.contents.len() - 1);
self.clamp_screen_to_cursor();
} }
Event::Key(key_event) if key_event.code == KeyCode::Char('b') => { Event::Key(key_event) if key_event.code == KeyCode::Char('b') => {
self.cursor.move_to_previous_beginning(); self.cursor.move_to_previous_beginning();
self.clamp_screen_to_cursor();
} }
Event::Key(key_event) if key_event.code == KeyCode::Char(';') => { Event::Key(key_event) if key_event.code == KeyCode::Char(';') => {
self.cursor.collapse(); self.cursor.collapse();
@@ -146,4 +171,12 @@ impl App {
_ => {} _ => {}
} }
} }
const fn clamp_screen_to_cursor(&mut self) {
if self.cursor.head < self.scroll_position {
self.scroll_position -= BYTES_PER_LINE;
} else if self.cursor.head > self.scroll_position + (self.window_rows * BYTES_PER_LINE) - 1 {
self.scroll_position += BYTES_PER_LINE;
}
}
} }
+9
View File
@@ -1,3 +1,5 @@
use crate::BYTES_PER_LINE;
#[derive(Debug, Default, PartialEq, Eq)] #[derive(Debug, Default, PartialEq, Eq)]
pub struct Cursor { pub struct Cursor {
pub head: usize, pub head: usize,
@@ -31,6 +33,13 @@ impl Cursor {
self.tail = self.head; self.tail = self.head;
} }
pub fn clamp(&mut self, scroll_position: usize, window_rows: usize) {
let max_row = scroll_position + window_rows * BYTES_PER_LINE - 1;
self.head = self.head.clamp(scroll_position, max_row);
self.tail = self.tail.clamp(scroll_position, max_row);
}
pub fn move_to_next_word(&mut self, max: usize) { pub fn move_to_next_word(&mut self, max: usize) {
if self.head == max { return } if self.head == max { return }