Forbid stack frames from expanding the drawable area
This commit is contained in:
parent
c1907bb8ee
commit
464aefa6d7
2 changed files with 90 additions and 38 deletions
121
src/buffer.rs
121
src/buffer.rs
|
|
@ -157,6 +157,73 @@ impl Default for Cell {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct StackFrame {
|
||||||
|
pub pos: Pos,
|
||||||
|
pub size: Size,
|
||||||
|
pub drawable_area: Option<(Pos, Size)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackFrame {
|
||||||
|
fn intersect_areas(
|
||||||
|
a_start: Pos,
|
||||||
|
a_size: Size,
|
||||||
|
b_start: Pos,
|
||||||
|
b_size: Size,
|
||||||
|
) -> Option<(Pos, Size)> {
|
||||||
|
// The first row/column that is not part of the area any more
|
||||||
|
let a_end = a_start + a_size;
|
||||||
|
let b_end = b_start + b_size;
|
||||||
|
|
||||||
|
let x_start = a_start.x.max(b_start.x);
|
||||||
|
let x_end = a_end.x.min(b_end.x);
|
||||||
|
let y_start = a_start.y.max(b_start.y);
|
||||||
|
let y_end = a_end.y.min(b_end.y);
|
||||||
|
|
||||||
|
if x_start < x_end && y_start < y_end {
|
||||||
|
let start = Pos::new(x_start, y_start);
|
||||||
|
let size = Size::new((x_end - x_start) as u16, (y_end - y_start) as u16);
|
||||||
|
Some((start, size))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn then(&self, pos: Pos, size: Size) -> Self {
|
||||||
|
let pos = self.local_to_global(pos);
|
||||||
|
|
||||||
|
let drawable_area = self
|
||||||
|
.drawable_area
|
||||||
|
.and_then(|(da_pos, da_size)| Self::intersect_areas(da_pos, da_size, pos, size));
|
||||||
|
|
||||||
|
StackFrame {
|
||||||
|
pos,
|
||||||
|
size,
|
||||||
|
drawable_area,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn local_to_global(&self, local_pos: Pos) -> Pos {
|
||||||
|
local_pos + self.pos
|
||||||
|
}
|
||||||
|
|
||||||
|
pub 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<i32>, Range<i32>)> {
|
||||||
|
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;
|
||||||
|
Some((xrange, yrange))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Buffer {
|
pub struct Buffer {
|
||||||
size: Size,
|
size: Size,
|
||||||
|
|
@ -167,7 +234,7 @@ pub struct Buffer {
|
||||||
/// was the size of the topmost stack element, and characters are translated
|
/// was the size of the topmost stack element, and characters are translated
|
||||||
/// by the position of the topmost stack element. No characters can be
|
/// by the position of the topmost stack element. No characters can be
|
||||||
/// placed outside the area described by the topmost stack element.
|
/// placed outside the area described by the topmost stack element.
|
||||||
stack: Vec<(Pos, Size)>,
|
stack: Vec<StackFrame>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Buffer {
|
impl Buffer {
|
||||||
|
|
@ -187,6 +254,7 @@ impl Buffer {
|
||||||
pub fn at(&self, x: u16, y: u16) -> &Cell {
|
pub fn at(&self, x: u16, y: u16) -> &Cell {
|
||||||
assert!(x < self.size.width);
|
assert!(x < self.size.width);
|
||||||
assert!(y < self.size.height);
|
assert!(y < self.size.height);
|
||||||
|
|
||||||
let i = self.index(x, y);
|
let i = self.index(x, y);
|
||||||
&self.data[i]
|
&self.data[i]
|
||||||
}
|
}
|
||||||
|
|
@ -195,47 +263,27 @@ impl Buffer {
|
||||||
fn at_mut(&mut self, x: u16, y: u16) -> &mut Cell {
|
fn at_mut(&mut self, x: u16, y: u16) -> &mut Cell {
|
||||||
assert!(x < self.size.width);
|
assert!(x < self.size.width);
|
||||||
assert!(y < self.size.height);
|
assert!(y < self.size.height);
|
||||||
|
|
||||||
let i = self.index(x, y);
|
let i = self.index(x, y);
|
||||||
&mut self.data[i]
|
&mut self.data[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn current_frame(&self) -> StackFrame {
|
||||||
|
self.stack.last().copied().unwrap_or(StackFrame {
|
||||||
|
pos: Pos::ZERO,
|
||||||
|
size: self.size,
|
||||||
|
drawable_area: Some((Pos::ZERO, self.size)),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn push(&mut self, pos: Pos, size: Size) {
|
pub fn push(&mut self, pos: Pos, size: Size) {
|
||||||
let cur_pos = self.stack.last().map(|(p, _)| *p).unwrap_or(Pos::ZERO);
|
self.stack.push(self.current_frame().then(pos, size));
|
||||||
self.stack.push((cur_pos + pos, size));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pop(&mut self) {
|
pub fn pop(&mut self) {
|
||||||
self.stack.pop();
|
self.stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_to_global(&self, pos: Pos) -> Pos {
|
|
||||||
pos + self.stack.last().map(|(p, _)| *p).unwrap_or(Pos::ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn global_to_local(&self, pos: Pos) -> Pos {
|
|
||||||
pos - self.stack.last().map(|(p, _)| *p).unwrap_or(Pos::ZERO)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn drawable_area(&self) -> (Pos, Size) {
|
|
||||||
self.stack.last().copied().unwrap_or((Pos::ZERO, self.size))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Size of the currently drawable area.
|
|
||||||
pub fn size(&self) -> Size {
|
|
||||||
self.drawable_area().1
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Min (inclusive) and max (not inclusive) coordinates of the currently
|
|
||||||
/// drawable area.
|
|
||||||
fn legal_ranges(&self) -> (Range<i32>, Range<i32>) {
|
|
||||||
let (top_left, size) = self.drawable_area();
|
|
||||||
let min_x = top_left.x.max(0);
|
|
||||||
let min_y = top_left.y.max(0);
|
|
||||||
let max_x = (top_left.x + size.width as i32).min(self.size.width as i32);
|
|
||||||
let max_y = (top_left.y + size.height as i32).min(self.size.height as i32);
|
|
||||||
(min_x..max_x, min_y..max_y)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resize the buffer and reset its contents.
|
/// Resize the buffer and reset its contents.
|
||||||
///
|
///
|
||||||
/// The buffer's contents are reset even if the buffer is already the
|
/// The buffer's contents are reset even if the buffer is already the
|
||||||
|
|
@ -284,11 +332,14 @@ impl Buffer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write(&mut self, widthdb: &mut WidthDB, tab_width: u8, pos: Pos, styled: &Styled) {
|
pub fn write(&mut self, widthdb: &mut WidthDB, tab_width: u8, pos: Pos, styled: &Styled) {
|
||||||
let pos = self.drawable_area().0 + pos;
|
let frame = self.current_frame();
|
||||||
let (xrange, yrange) = self.legal_ranges();
|
let (xrange, yrange) = match frame.legal_ranges() {
|
||||||
// If we're not even visible, there's nothing to do
|
Some(ranges) => ranges,
|
||||||
|
None => return, // No drawable area
|
||||||
|
};
|
||||||
|
let pos = frame.local_to_global(pos);
|
||||||
if !yrange.contains(&pos.y) {
|
if !yrange.contains(&pos.y) {
|
||||||
return;
|
return; // Outside of drawable area
|
||||||
}
|
}
|
||||||
let y = pos.y as u16;
|
let y = pos.y as u16;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ impl Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn size(&self) -> Size {
|
pub fn size(&self) -> Size {
|
||||||
self.buffer.size()
|
self.buffer.current_frame().size
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self) {
|
pub fn reset(&mut self) {
|
||||||
|
|
@ -44,11 +44,12 @@ impl Frame {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cursor(&self) -> Option<Pos> {
|
pub fn cursor(&self) -> Option<Pos> {
|
||||||
self.cursor.map(|p| self.buffer.global_to_local(p))
|
self.cursor
|
||||||
|
.map(|p| self.buffer.current_frame().global_to_local(p))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_cursor(&mut self, pos: Option<Pos>) {
|
pub fn set_cursor(&mut self, pos: Option<Pos>) {
|
||||||
self.cursor = pos.map(|p| self.buffer.local_to_global(p));
|
self.cursor = pos.map(|p| self.buffer.current_frame().local_to_global(p));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn show_cursor(&mut self, pos: Pos) {
|
pub fn show_cursor(&mut self, pos: Pos) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue