From 3e04ffdfa4395ec9a7f7d1bd93169b314ed136a7 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Tue, 18 Oct 2022 12:42:37 -0600 Subject: [PATCH] Handle scroll in TextBuffer --- examples/editor-libcosmic/src/main.rs | 5 +- examples/editor-libcosmic/src/text_box.rs | 19 ++-- examples/editor-orbclient/src/main.rs | 57 ++++++----- src/buffer.rs | 109 +++++++++++++++++----- 4 files changed, 129 insertions(+), 61 deletions(-) diff --git a/examples/editor-libcosmic/src/main.rs b/examples/editor-libcosmic/src/main.rs index 8068381..c7693ee 100644 --- a/examples/editor-libcosmic/src/main.rs +++ b/examples/editor-libcosmic/src/main.rs @@ -108,12 +108,13 @@ impl Application for Window { default_text.to_string() }; - let line_x = 8; let buffer = Arc::new(RwLock::new(TextBuffer::new( unsafe { FONT_MATCHES.as_ref().unwrap() }, &text, font_sizes[font_size_i].0, - 600 - line_x * 2, + font_sizes[font_size_i].1, + 0, + 0 ))); let window = Window { diff --git a/examples/editor-libcosmic/src/text_box.rs b/examples/editor-libcosmic/src/text_box.rs index 603fc4e..c23915b 100644 --- a/examples/editor-libcosmic/src/text_box.rs +++ b/examples/editor-libcosmic/src/text_box.rs @@ -56,11 +56,7 @@ where { let mut buffer = self.buffer.write().unwrap(); - let font_size = buffer.font_size(); - let line_height = font_size + 8; - - buffer.set_line_width(size.width as i32); - buffer.shape_until(size.height as i32 / line_height); + buffer.set_size(size.width as i32, size.height as i32); } layout::Node::new(size) } @@ -78,7 +74,6 @@ where let buffer = self.buffer.read().unwrap(); let font_size = buffer.font_size(); let line_height = font_size + 8; /*TODO: store somewhere else */ - let scroll = 0; let instant = Instant::now(); @@ -99,7 +94,7 @@ where for (line_i, line) in buffer .layout_lines() .iter() - .skip(scroll as usize) + .skip(buffer.scroll as usize) .enumerate() { if line_y >= (layout.bounds().y + layout.bounds().height) as i32 { @@ -111,7 +106,7 @@ where start_line_opt = Some(end_line); } - if buffer.cursor.line == line_i + scroll as usize { + if buffer.cursor.line == line_i + buffer.scroll as usize { if buffer.cursor.glyph >= line.glyphs.len() { let x = match line.glyphs.last() { Some(glyph) => glyph.x + glyph.w, @@ -248,6 +243,14 @@ where buffer.action(TextAction::Delete); Status::Captured }, + KeyCode::PageUp => { + buffer.action(TextAction::PageUp); + Status::Captured + }, + KeyCode::PageDown => { + buffer.action(TextAction::PageDown); + Status::Captured + }, _ => Status::Ignored, } }, diff --git a/examples/editor-orbclient/src/main.rs b/examples/editor-orbclient/src/main.rs index e089413..e4785a3 100644 --- a/examples/editor-orbclient/src/main.rs +++ b/examples/editor-orbclient/src/main.rs @@ -86,7 +86,9 @@ fn main() { &font_matches, &text, font_sizes[font_size_i].0 * display_scale, + font_sizes[font_size_i].1 * display_scale, window.width() as i32 - line_x * 2, + window.height() as i32 ); let mut ctrl_pressed = false; @@ -94,22 +96,11 @@ fn main() { let mut mouse_y = -1; let mut mouse_left = false; let mut rehit = false; - let mut scroll = 0; loop { let font_size = buffer.font_size(); - let line_height = font_sizes[font_size_i].1 * display_scale; + let line_height = buffer.line_height(); - let window_lines = (window.height() as i32 + line_height - 1) / line_height; - - buffer.shape_until(scroll + window_lines); - - scroll = cmp::max( - 0, - cmp::min( - buffer.layout_lines().len() as i32 - (window_lines - 1), - scroll, - ), - ); + buffer.shape_until_scroll(); if rehit { let instant = Instant::now(); @@ -120,7 +111,7 @@ fn main() { for (line_i, line) in buffer .layout_lines() .iter() - .skip(scroll as usize) + .skip(buffer.scroll as usize) .enumerate() { if line_y >= window.height() as i32 { @@ -131,7 +122,7 @@ fn main() { && mouse_y >= line_y - font_size && mouse_y < line_y - font_size + line_height { - let new_cursor_line = line_i + scroll as usize; + let new_cursor_line = line_i + buffer.scroll as usize; let mut new_cursor_glyph = line.glyphs.len(); for (glyph_i, glyph) in line.glyphs.iter().enumerate() { if mouse_x >= line_x + glyph.x as i32 @@ -170,7 +161,7 @@ fn main() { for (line_i, line) in buffer .layout_lines() .iter() - .skip(scroll as usize) + .skip(buffer.scroll as usize) .enumerate() { if line_y >= window.height() as i32 { @@ -182,7 +173,7 @@ fn main() { start_line_opt = Some(end_line); } - if buffer.cursor.line == line_i + scroll as usize { + if buffer.cursor.line == line_i + buffer.scroll as usize { if buffer.cursor.glyph >= line.glyphs.len() { let x = match line.glyphs.last() { Some(glyph) => glyph.x + glyph.w, @@ -259,28 +250,31 @@ fn main() { orbclient::K_DOWN if event.pressed => buffer.action(TextAction::Down), orbclient::K_BKSP if event.pressed => buffer.action(TextAction::Backspace), orbclient::K_DEL if event.pressed => buffer.action(TextAction::Delete), - orbclient::K_PGUP if event.pressed => { - scroll -= window_lines; - buffer.redraw = true; - }, - orbclient::K_PGDN if event.pressed => { - scroll += window_lines; - buffer.redraw = true; - }, + orbclient::K_PGUP if event.pressed => buffer.action(TextAction::PageUp), + orbclient::K_PGDN if event.pressed => buffer.action(TextAction::PageDown), orbclient::K_0 if event.pressed && ctrl_pressed => { font_size_i = font_size_default; - buffer.set_font_size(font_sizes[font_size_i].0 * display_scale); + buffer.set_font_metrics( + font_sizes[font_size_i].0 * display_scale, + font_sizes[font_size_i].1 * display_scale, + ); }, orbclient::K_MINUS if event.pressed && ctrl_pressed => { if font_size_i > 0 { font_size_i -= 1; - buffer.set_font_size(font_sizes[font_size_i].0 * display_scale); + buffer.set_font_metrics( + font_sizes[font_size_i].0 * display_scale, + font_sizes[font_size_i].1 * display_scale, + ); } }, orbclient::K_EQUALS if event.pressed && ctrl_pressed => { if font_size_i + 1 < font_sizes.len() { font_size_i += 1; - buffer.set_font_size(font_sizes[font_size_i].0 * display_scale); + buffer.set_font_metrics( + font_sizes[font_size_i].0 * display_scale, + font_sizes[font_size_i].1 * display_scale, + ); } }, orbclient::K_D if event.pressed && ctrl_pressed => { @@ -314,10 +308,13 @@ fn main() { } } EventOption::Resize(event) => { - buffer.set_line_width(event.width as i32 - line_x * 2); + buffer.set_size( + event.width as i32 - line_x * 2, + event.height as i32, + ); } EventOption::Scroll(event) => { - scroll -= event.y * 3; + buffer.scroll -= event.y * 3; buffer.redraw = true; } EventOption::Quit(_) => return, diff --git a/src/buffer.rs b/src/buffer.rs index dda6c69..8eb7f09 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -1,4 +1,7 @@ -use std::time::Instant; +use std::{ + cmp, + time::Instant, +}; use crate::{FontLayoutLine, FontLineIndex, FontMatches, FontShapeLine}; @@ -9,6 +12,8 @@ pub enum TextAction { Down, Backspace, Delete, + PageUp, + PageDown, Insert(char), } @@ -30,13 +35,23 @@ pub struct TextBuffer<'a> { shape_lines: Vec>, layout_lines: Vec>, font_size: i32, - line_width: i32, + line_height: i32, + width: i32, + height: i32, pub cursor: TextCursor, pub redraw: bool, + pub scroll: i32, } impl<'a> TextBuffer<'a> { - pub fn new(font_matches: &'a FontMatches<'a>, text: &str, font_size: i32, line_width: i32) -> Self { + pub fn new( + font_matches: &'a FontMatches<'a>, + text: &str, + font_size: i32, + line_height: i32, + width: i32, + height: i32, + ) -> Self { let mut text_lines: Vec = text.lines().map(String::from).collect(); if text_lines.is_empty() { text_lines.push(String::new()); @@ -47,9 +62,12 @@ impl<'a> TextBuffer<'a> { shape_lines: Vec::new(), layout_lines: Vec::new(), font_size, - line_width, + line_height, + width, + height, cursor: TextCursor::default(), redraw: false, + scroll: 0, } } @@ -71,6 +89,21 @@ impl<'a> TextBuffer<'a> { } } + pub fn shape_until_scroll(&mut self) { + let lines = self.height / self.line_height; + + let scroll_end = self.scroll + lines; + self.shape_until(scroll_end); + + self.scroll = cmp::max( + 0, + cmp::min( + self.layout_lines().len() as i32 - (lines - 1), + self.scroll, + ), + ); + } + pub fn reshape_line(&mut self, line_i: FontLineIndex) { let instant = Instant::now(); @@ -97,7 +130,7 @@ impl<'a> TextBuffer<'a> { let layout_i = self.layout_lines.len(); line.layout( self.font_size, - self.line_width, + self.width, &mut self.layout_lines, layout_i, ); @@ -131,7 +164,7 @@ impl<'a> TextBuffer<'a> { let shape_line = &self.shape_lines[line_i.get()]; shape_line.layout( self.font_size, - self.line_width, + self.width, &mut self.layout_lines, insert_i, ); @@ -150,18 +183,40 @@ impl<'a> TextBuffer<'a> { self.font_size } - pub fn set_font_size(&mut self, font_size: i32) { - self.font_size = font_size; - self.relayout(); + pub fn line_height(&self) -> i32 { + self.line_height } - pub fn line_width(&self) -> i32 { - self.line_width + pub fn set_font_metrics(&mut self, font_size: i32, line_height: i32) { + if font_size != self.font_size { + self.font_size = font_size; + self.relayout(); + } + + if line_height != self.line_height { + self.line_height = line_height; + self.shape_until_scroll(); + } } - pub fn set_line_width(&mut self, line_width: i32) { - self.line_width = line_width; - self.relayout(); + pub fn width(&self) -> i32 { + self.width + } + + pub fn height(&self) -> i32 { + self.height + } + + pub fn set_size(&mut self, width: i32, height: i32) { + if width != self.width { + self.width = width; + self.relayout(); + } + + if height != self.height { + self.height = height; + self.shape_until_scroll(); + } } pub fn layout_lines(&self) -> &[FontLayoutLine] { @@ -184,7 +239,7 @@ impl<'a> TextBuffer<'a> { self.cursor.glyph -= 1; self.redraw = true; } - } + }, TextAction::Right => { let line = &self.layout_lines[self.cursor.line]; if self.cursor.glyph > line.glyphs.len() { @@ -195,19 +250,19 @@ impl<'a> TextBuffer<'a> { self.cursor.glyph += 1; self.redraw = true; } - } + }, TextAction::Up => { if self.cursor.line > 0 { self.cursor.line -= 1; self.redraw = true; } - } + }, TextAction::Down => { if self.cursor.line + 1 < self.layout_lines.len() { self.cursor.line += 1; self.redraw = true; } - } + }, TextAction::Backspace => { let line = &self.layout_lines[self.cursor.line]; if self.cursor.glyph > line.glyphs.len() { @@ -221,7 +276,7 @@ impl<'a> TextBuffer<'a> { text_line.remove(glyph.start); self.reshape_line(line.line_i); } - } + }, TextAction::Delete => { let line = &self.layout_lines[self.cursor.line]; if self.cursor.glyph < line.glyphs.len() { @@ -230,7 +285,19 @@ impl<'a> TextBuffer<'a> { text_line.remove(glyph.start); self.reshape_line(line.line_i); } - } + }, + TextAction::PageUp => { + let lines = self.height / self.line_height; + self.scroll -= lines; + self.redraw = true; + self.shape_until_scroll(); + }, + TextAction::PageDown => { + let lines = self.height / self.line_height; + self.scroll += lines; + self.redraw = true; + self.shape_until_scroll(); + }, TextAction::Insert(character) => { let line = &self.layout_lines[self.cursor.line]; if self.cursor.glyph >= line.glyphs.len() { @@ -255,7 +322,7 @@ impl<'a> TextBuffer<'a> { self.cursor.glyph += 1; self.reshape_line(line.line_i); } - } + }, } } }