From 24fd0050fbfdd72ac2f03029148370b362291777 Mon Sep 17 00:00:00 2001 From: Joscha Date: Thu, 8 Sep 2022 18:13:13 +0200 Subject: [PATCH] Fix drawing widgets on cursor not removing cursor --- src/buffer.rs | 61 ++++++++++++++++++++++++++++++++++++++++++--------- src/frame.rs | 9 +++----- 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/buffer.rs b/src/buffer.rs index ca95782..44d50d5 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -157,7 +157,7 @@ impl Default for Cell { } #[derive(Debug, Clone, Copy)] -pub struct StackFrame { +struct StackFrame { pub pos: Pos, pub size: Size, pub drawable_area: Option<(Pos, Size)>, @@ -188,7 +188,7 @@ impl StackFrame { } } - pub fn then(&self, pos: Pos, size: Size) -> Self { + fn then(&self, pos: Pos, size: Size) -> Self { let pos = self.local_to_global(pos); let drawable_area = self @@ -202,17 +202,17 @@ impl StackFrame { } } - pub fn local_to_global(&self, local_pos: Pos) -> Pos { + fn local_to_global(&self, local_pos: Pos) -> Pos { local_pos + self.pos } - pub fn global_to_local(&self, global_pos: Pos) -> Pos { + fn global_to_local(&self, global_pos: Pos) -> Pos { global_pos - self.pos } /// Ranges along the x and y axis where drawing is allowed, in global /// coordinates. - pub fn legal_ranges(&self) -> Option<(Range, Range)> { + fn legal_ranges(&self) -> Option<(Range, Range)> { if let Some((pos, size)) = self.drawable_area { let xrange = pos.x..pos.x + size.width as i32; let yrange = pos.y..pos.y + size.height as i32; @@ -227,6 +227,8 @@ impl StackFrame { pub struct Buffer { size: Size, data: Vec, + cursor: Option, + /// A stack of rectangular drawing areas. /// /// When rendering to the buffer with a nonempty stack, it behaves as if it @@ -237,6 +239,9 @@ pub struct Buffer { } impl Buffer { + /// Index in `data` of the cell at the given position. The position must + /// be inside the buffer. + /// /// Ignores the stack. fn index(&self, x: u16, y: u16) -> usize { assert!(x < self.size.width); @@ -249,6 +254,9 @@ impl Buffer { y * width + x } + /// A reference to the cell at the given position. The position must be + /// inside the buffer. + /// /// Ignores the stack. pub fn at(&self, x: u16, y: u16) -> &Cell { assert!(x < self.size.width); @@ -258,6 +266,9 @@ impl Buffer { &self.data[i] } + /// A mutable reference to the cell at the given position. The position must + /// be inside the buffer. + /// /// Ignores the stack. fn at_mut(&mut self, x: u16, y: u16) -> &mut Cell { assert!(x < self.size.width); @@ -267,7 +278,7 @@ impl Buffer { &mut self.data[i] } - pub fn current_frame(&self) -> StackFrame { + fn current_frame(&self) -> StackFrame { self.stack.last().copied().unwrap_or(StackFrame { pos: Pos::ZERO, size: self.size, @@ -283,6 +294,19 @@ impl Buffer { self.stack.pop(); } + /// Size of the current drawable area, respecting the stack. + pub fn size(&self) -> Size { + self.current_frame().size + } + + pub fn cursor(&self) -> Option { + self.cursor.map(|p| self.current_frame().global_to_local(p)) + } + + pub fn set_cursor(&mut self, pos: Option) { + self.cursor = pos.map(|p| self.current_frame().local_to_global(p)); + } + /// Resize the buffer and reset its contents. /// /// The buffer's contents are reset even if the buffer is already the @@ -300,6 +324,8 @@ impl Buffer { self.data.resize_with(len, Cell::default); } + self.cursor = None; + self.stack.clear(); } @@ -307,29 +333,35 @@ impl Buffer { /// /// `buf.reset()` is equivalent to `buf.resize(buf.size())`. pub fn reset(&mut self) { - self.data.fill_with(Cell::default); - self.stack.clear(); + self.resize(self.size); } /// Remove the grapheme at the specified coordinates from the buffer. /// /// Removes the entire grapheme, not just the cell at the coordinates. - /// Preserves the style of the affected cells. Works even if the coordinates - /// don't point to the beginning of the grapheme. + /// Preserves the style of the affected cells. Preserves the cursor. Works + /// even if the coordinates don't point to the beginning of the grapheme. /// /// Ignores the stack. fn erase(&mut self, x: u16, y: u16) { let cell = self.at(x, y); let width: u16 = cell.width.into(); let offset: u16 = cell.offset.into(); + for x in (x - offset)..(x - offset + width) { let cell = self.at_mut(x, y); let style = cell.style; + *cell = Cell::default(); cell.style = style; } } + /// Write styled text to the buffer, respecting the width of individual + /// graphemes. + /// + /// The initial x position is considered the first column for tab width + /// calculations. pub fn write(&mut self, widthdb: &mut WidthDB, pos: Pos, styled: &Styled) { let frame = self.current_frame(); let (xrange, yrange) = match frame.legal_ranges() { @@ -359,6 +391,8 @@ impl Buffer { } } + /// Write a single grapheme to the buffer, respecting its width. + /// /// Assumes that `pos.y` is in range. fn write_grapheme( &mut self, @@ -403,6 +437,13 @@ impl Buffer { }; } } + + if let Some(pos) = self.cursor { + if pos.y == y as i32 && start_x <= pos.x && pos.x <= end_x { + // The cursor lies within the bounds of the current grapheme and + self.cursor = None; + } + } } pub fn cells(&self) -> Cells<'_> { diff --git a/src/frame.rs b/src/frame.rs index ae0c364..38fab3f 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -10,7 +10,6 @@ use crate::wrap; pub struct Frame { pub(crate) widthdb: WidthDB, pub(crate) buffer: Buffer, - cursor: Option, } impl Frame { @@ -23,21 +22,19 @@ impl Frame { } pub fn size(&self) -> Size { - self.buffer.current_frame().size + self.buffer.size() } pub fn reset(&mut self) { self.buffer.reset(); - self.cursor = None; } pub fn cursor(&self) -> Option { - self.cursor - .map(|p| self.buffer.current_frame().global_to_local(p)) + self.buffer.cursor() } pub fn set_cursor(&mut self, pos: Option) { - self.cursor = pos.map(|p| self.buffer.current_frame().local_to_global(p)); + self.buffer.set_cursor(pos); } pub fn show_cursor(&mut self, pos: Pos) {