mod area;
mod cgptr;
use std::{
fmt::{self, Debug},
ops::{Index, IndexMut, Range},
};
use area::FrameArea;
use cgptr::CgPtr;
use crate::{
Event, InputState, StateEvent,
grid::Grid,
input::{FrameInput, Nop},
output::StyledChar,
pos::{Pos, Rect, Size, X, Y},
};
#[cfg(test)]
mod test_draw;
#[cfg(test)]
mod test_split;
pub struct Frame<'b, Input> {
buf: Option<CgPtr<'b>>,
input: Input,
area: FrameArea,
}
impl<Input: Debug> fmt::Debug for Frame<'_, Input> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Frame<'_>")
.field("buf", &..)
.field("input", &self.input)
.field("area", &self.area)
.finish()
}
}
impl Frame<'static, Nop> {
pub fn nop(size: Size) -> Self {
Self::null(Nop, size)
}
}
impl<Input> Frame<'static, Input> {
pub fn null(input: Input, size: Size) -> Self {
Self { buf: None, input, area: FrameArea::full(size) }
}
}
impl<'b, Input: FrameInput> Frame<'b, Input> {
pub fn new(input: Input, chars: &'b mut Grid<StyledChar>) -> Self {
let size = chars.size();
if size.is_empty() {
return Frame::null(input, chars.size());
}
Self { buf: Some(CgPtr::new(chars)), input, area: FrameArea::full(size) }
}
pub fn split_v(self, y: Y) -> Option<(Self, Self)> {
if y > self.size().height {
return None;
}
let (ta, ba) = self.area.split_v(y);
let (ti, bi) = self.input.split_v(y);
Some((
Self { buf: self.buf.clone(), input: ti, area: ta },
Self { buf: self.buf.clone(), input: bi, area: ba },
))
}
pub fn split_h(self, x: X) -> Option<(Self, Self)> {
if x > self.size().width {
return None;
}
let (la, ra) = self.area.split_h(x);
let (li, ri) = self.input.split_h(x);
Some((
Self { buf: self.buf.clone(), input: li, area: la },
Self { buf: self.buf.clone(), input: ri, area: ra },
))
}
pub fn input(&self) -> &Input {
&self.input
}
pub fn size(&self) -> Size {
self.area.size()
}
pub fn is_empty(&self) -> bool {
self.size().is_empty()
}
pub fn vgrow(&mut self, tl: Size, br: Size) {
self.area.vgrow(tl, br);
}
pub fn fill(&mut self, ch: StyledChar) {
for ry in self.rows() {
let mut row = self.row(ry);
row.fill(ch);
}
}
pub fn rows(&self) -> impl Iterator<Item = Y> + use<Input> {
(0..self.size().height.0).map(Y)
}
pub fn row<'s>(&'s mut self, y: Y) -> Row<'s> {
let vwidth = self.size().width;
let Some(ref mut cg) = self.buf else {
return Row::vir(vwidth);
};
let Some(ri) = self.area.rowinfo(y) else {
return Row::vir(vwidth);
};
let slice = unsafe { cg.slice(ri.slice, ri.real_y) };
Row::abs(vwidth, slice, ri.xs)
}
}
impl<'g> Frame<'g, Event> {
pub fn with<'i>(self, state: &'i mut InputState) -> Frame<'g, StateEvent<'i>> {
state.update(&self.input);
Frame {
buf: self.buf.clone(),
input: StateEvent {
event: self.input,
state: &*state,
rect: Rect::tlsz(Pos::ZERO, self.area.size()),
},
area: self.area,
}
}
}
pub struct Row<'b> {
vwidth: X,
absolute: Option<(&'b mut [StyledChar], Range<usize>)>,
dummy: StyledChar,
}
impl fmt::Debug for Row<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Row")
.field("vwidth", &self.vwidth.0)
.field("absolute", &self.absolute.as_ref().map(|(_, r)| ([..], r)))
.finish()
}
}
impl<'b> Row<'b> {
fn vir(vwidth: X) -> Self {
Self { vwidth, absolute: None, dummy: StyledChar::BLANK }
}
fn abs(vwidth: X, slice: &'b mut [StyledChar], range: Range<usize>) -> Self {
Self { vwidth, absolute: Some((slice, range)), dummy: StyledChar::BLANK }
}
pub fn fill(&mut self, ch: StyledChar) {
if let Some((s, _)) = &mut self.absolute {
s.fill(ch);
}
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut StyledChar> {
[].into_iter()
}
}
impl<'b> Index<X> for Row<'b> {
type Output = StyledChar;
fn index(&self, x: X) -> &Self::Output {
assert!(x < self.vwidth, "x={x} is out of bounds ({})", self.vwidth);
let Some((s, r)) = &self.absolute else {
return &self.dummy;
};
if !r.contains(&x.0) { &self.dummy } else { &s[x.0 - r.start] }
}
}
impl<'b> IndexMut<X> for Row<'b> {
fn index_mut(&mut self, x: X) -> &mut Self::Output {
assert!(x < self.vwidth, "x={x} is out of bounds ({})", self.vwidth);
let Some((s, r)) = &mut self.absolute else {
return &mut self.dummy;
};
if !r.contains(&x.0) { &mut self.dummy } else { &mut s[x.0 - r.start] }
}
}