use crate::{Region, sizer::Sizer};
use std::rc::Rc;
pub mod region;
const DEFAULT_HFB: u16 = 99;
#[derive(Clone)]
pub struct Page {
sizer: Sizer,
sy: i32,
sx: i32,
pub(super) cursor: Option<(i32, i32)>,
rows: Vec<Row>,
}
impl Page {
pub fn new(sy: i32, sx: i32, hfb: u16, sizer: Sizer) -> Self {
let sy = sy.max(0);
let sx = sx.max(0);
let empty = Row::new(hfb, sx);
let mut rows = Vec::with_capacity(sy as usize);
rows.resize_with(sy as usize, || empty.clone());
Self {
sizer,
sy,
sx,
cursor: None,
rows,
}
}
pub fn empty() -> Self {
Self::new(0, 0, DEFAULT_HFB, crate::sizer::SimpleSizer::new())
}
#[inline]
pub fn sx(&self) -> i32 {
self.sx
}
#[inline]
pub fn sy(&self) -> i32 {
self.sy
}
#[inline]
pub fn full(&mut self) -> Region<'_> {
let sy = self.sy;
let sx = self.sx;
Region {
page: self,
oy: 0,
ox: 0,
sy,
sx,
cy0: 0,
cx0: 0,
cy1: sy,
cx1: sx,
wy: 0,
wx: 0,
hfb: DEFAULT_HFB,
}
}
#[inline]
pub fn region(&mut self, y: i32, x: i32, sy: i32, sx: i32) -> Region<'_> {
let page_sy = self.sy;
let page_sx = self.sx;
Region {
page: self,
oy: y,
ox: x,
sy,
sx,
cy0: y.max(0),
cx0: x.max(0),
cy1: (y + sy).min(page_sy),
cx1: (x + sx).min(page_sx),
wy: 0,
wx: 0,
hfb: DEFAULT_HFB,
}
}
pub fn measure(&mut self, text: &str) -> i32 {
region::Scan::new(text.as_bytes()).measure_rest(&self.sizer) as i32
}
pub fn set_cursor(&mut self, cursor: Option<(i32, i32)>) {
if let Some((y, x)) = cursor
&& y >= 0
&& y < self.sy
&& x >= 0
&& x < self.sx
{
self.cursor = cursor;
} else {
self.cursor = None;
}
}
pub fn cursor(&self) -> Option<(i32, i32)> {
self.cursor
}
pub fn scroll_up(&mut self, y0: i32, y1: i32, count: i32, hfb: u16) {
let y0 = y0.max(0);
let y1 = y1.min(self.sy);
let mov = y1 - y0 - count;
let count = count.min(y1 - y0);
if mov > 0 {
for i in 0..mov {
self.rows.swap((y0 + i) as usize, (y0 + i + count) as usize);
}
}
let empty = Row::new(hfb, self.sx);
for i in 0..count {
self.rows[(y1 - count + i) as usize] = empty.clone();
}
}
pub fn scroll_down(&mut self, y0: i32, y1: i32, count: i32, hfb: u16) {
let y0 = y0.max(0);
let y1 = y1.min(self.sy);
let mov = y1 - y0 - count;
let count = count.min(y1 - y0);
if mov > 0 {
for i in 1..=mov {
self.rows.swap((y1 - i) as usize, (y1 - i - count) as usize);
}
}
let empty = Row::new(hfb, self.sx);
for i in 0..count {
self.rows[(y0 + i) as usize] = empty.clone();
}
}
pub fn copy_lines(&mut self, mut ya: i32, mut yb: i32, mut sy: i32) {
let yadj = 0.max(-ya).max(-yb);
ya += yadj;
yb += yadj;
sy -= yadj;
let syadj = 0.max(ya + sy - self.sy).max(yb + sy - self.sy);
sy -= syadj;
if sy > 0 {
if ya >= yb {
for i in 0..sy {
self.rows[(yb + i) as usize] = self.rows[(ya + i) as usize].clone();
}
} else {
for i in 1..=sy {
self.rows[(yb + sy - i) as usize] = self.rows[(ya + sy - i) as usize].clone();
}
}
}
}
pub fn copy_region(
&mut self,
mut ya: i32,
mut xa: i32,
mut yb: i32,
mut xb: i32,
mut sy: i32,
mut sx: i32,
) {
let yadj = 0.max(-ya).max(-yb);
ya += yadj;
yb += yadj;
sy -= yadj;
let syadj = 0.max(ya + sy - self.sy).max(yb + sy - self.sy);
sy -= syadj;
let xadj = 0.max(-xa).max(-xb);
xa += xadj;
xb += xadj;
sx -= xadj;
let sxadj = 0.max(xa + sx - self.sx).max(xb + sx - self.sx);
sx -= sxadj;
if sx > 0 && sy > 0 {
let mut buf = Vec::new();
if ya >= yb {
for i in 0..sy {
self.rows[(ya + i) as usize].get_subrow(xa, xa + sx, &mut buf);
self.rows[(yb + i) as usize].set_subrow(xb, &buf);
}
} else {
for i in 1..=sy {
self.rows[(ya + sy - i) as usize].get_subrow(xa, xa + sx, &mut buf);
self.rows[(yb + sy - i) as usize].set_subrow(xb, &buf);
}
}
}
}
pub(super) fn changes(
old: &mut Self,
new: &mut Self,
mut cb: impl FnMut(i32, &mut [bool], &Row),
) {
let sx = new.sx;
let sy = new.sy;
if old.sx != sx || old.sy != sy {
for y in 0..sy {
let mut bitmap = vec![true; sx as usize];
let row = new.rows.get_mut(y as usize).unwrap();
cb(y, &mut bitmap, row);
}
} else {
let mut bitmap = vec![false; sx as usize];
for y in 0..sy {
let r0 = &mut old.rows[y as usize];
let r1 = &mut new.rows[y as usize];
if r0.changes_bitmap(r1, &mut bitmap) {
cb(y, &mut bitmap, r1);
}
}
}
}
}
impl Default for Page {
fn default() -> Self {
Self::empty()
}
}
impl std::fmt::Debug for Page {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Page sy={} sx={} cursor={:?}:",
self.sy, self.sx, self.cursor
)?;
for row in &self.rows {
write!(f, "\n {row:?}")?;
}
Ok(())
}
}
#[derive(Clone)]
pub(super) struct Row {
info: RowInfo,
cells: Rc<Vec<u8>>,
}
#[derive(Clone)]
struct RowInfo {
width: i32,
cell_len: usize,
}
impl Row {
fn new(hfb: u16, width: i32) -> Self {
let width = width.max(0);
let mut data = Vec::with_capacity(4 * width as usize);
for _ in 0..width {
data.push((hfb >> 8) as u8);
data.push(hfb as u8);
data.push(32);
data.push(0);
}
Self {
info: RowInfo { width, cell_len: 4 },
cells: Rc::new(data),
}
}
pub fn set_hfb(&mut self, x: i32, hfb: u16) {
if x >= 0 && x < self.info.width {
let off = x as usize * self.info.cell_len;
let cells = Rc::make_mut(&mut self.cells);
let p = &mut cells[off..off + 2];
p[0] = (hfb >> 8) as u8;
p[1] = hfb as u8;
}
}
pub fn set(&mut self, x: i32, hfb: u16, data: &[u8]) {
if x >= 0 && x < self.info.width {
let cells = Rc::make_mut(&mut self.cells);
self.info.make_space(cells, data.len());
self.info.qset(cells, x, hfb, data);
}
}
pub fn set_left(&mut self, x: i32, hfb: u16, data: &[u8]) {
if x >= 0 && x < self.info.width {
let cells = Rc::make_mut(&mut self.cells);
self.info.make_space(cells, data.len() + 1); let off = x as usize * self.info.cell_len;
let p = &mut cells[off..off + self.info.cell_len];
let data_len = data.len();
p[0] = (hfb >> 8) as u8;
p[1] = hfb as u8;
p[2] = 0xFF;
p[3..3 + data_len].copy_from_slice(data);
p[3 + data_len..].fill(0);
}
}
pub fn set_right(&mut self, x: i32, hfb: u16) {
if x >= 0 && x < self.info.width {
let cells = Rc::make_mut(&mut self.cells);
self.info.qset(cells, x, hfb, b"\xFE".as_slice());
}
}
pub fn set_repl(&mut self, x: i32, hfb: u16) {
if x >= 0 && x < self.info.width {
let cells = Rc::make_mut(&mut self.cells);
self.info.qset(cells, x, hfb, b"".as_slice());
}
}
pub fn setn(&mut self, x0: i32, x1: i32, hfb: u16, data: &[u8]) {
let x0 = x0.max(0);
let x1 = x1.min(self.info.width);
if x0 < x1 {
let cells = Rc::make_mut(&mut self.cells);
self.info.make_space(cells, data.len());
for x in x0..x1 {
self.info.qset(cells, x, hfb, data);
}
}
}
pub fn set_subrow(&mut self, x: i32, data: &[u8]) {
let mut x = x;
let mut p = data;
while p.len() >= 3 {
let hfb = ((p[0] as u16) << 8) | (p[1] as u16);
p = &p[2..];
let len = p.len();
let end = p.iter().position(|&c| c == 0).unwrap_or(len);
self.set(x, hfb, &p[..end]);
p = &p[(end + 1).min(len)..];
x += 1;
}
}
pub fn get(&self, x: i32) -> Option<(u16, &[u8])> {
if x >= 0 && x < self.info.width {
return Some(self.qget(x));
}
None
}
pub fn get_subrow(&self, x0: i32, x1: i32, buf: &mut Vec<u8>) {
let x0 = x0.max(0);
let x1 = x1.min(self.info.width);
buf.clear();
for x in x0..x1 {
let (hfb, data) = self.qget(x);
buf.push((hfb >> 8) as u8);
buf.push(hfb as u8);
buf.extend_from_slice(data);
buf.push(0);
}
}
fn qget(&self, x: i32) -> (u16, &[u8]) {
let off = x as usize * self.info.cell_len;
let p = &self.cells[off..off + self.info.cell_len];
let hfb = ((p[0] as u16) << 8) | (p[1] as u16);
let mut data = &p[2..];
while let Some((last, rest)) = data.split_last() {
if *last == 0 {
data = rest;
} else {
break;
}
}
(hfb, data)
}
pub fn changes_bitmap(&self, new: &Row, bitmap: &mut [bool]) -> bool {
let mut rv = false;
for x in 0..self.info.width.min(new.info.width) {
let (h0, d0) = self.qget(x);
let (h1, d1) = new.qget(x);
let flag = h0 != h1 || d0 != d1;
bitmap[x as usize] = flag;
rv = rv || flag;
}
rv
}
}
impl RowInfo {
#[inline(always)]
fn make_space(&mut self, cells: &mut Vec<u8>, data_len: usize) {
if data_len + 2 > self.cell_len {
self.make_space_aux(cells, data_len);
}
}
#[inline(never)]
fn make_space_aux(&mut self, cells: &mut Vec<u8>, data_len: usize) {
let new_cell_len = (((data_len + 2) + 1) | 1) - 1;
let mut new_cells = Vec::with_capacity(new_cell_len * self.width as usize);
for cell in cells.chunks_exact(self.cell_len) {
new_cells.extend_from_slice(cell);
new_cells.resize(new_cells.len() + new_cell_len - self.cell_len, 0);
}
*cells = new_cells;
self.cell_len = new_cell_len;
}
#[inline(always)]
fn qset(&mut self, cells: &mut [u8], x: i32, hfb: u16, data: &[u8]) {
let off = x as usize * self.cell_len;
let p = &mut cells[off..off + self.cell_len];
let data_len = data.len();
p[0] = (hfb >> 8) as u8;
p[1] = hfb as u8;
p[2..2 + data_len].copy_from_slice(data);
p[2 + data_len..].fill(0);
}
}
impl std::fmt::Debug for Row {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Row width={} cell_len={}: ",
self.info.width, self.info.cell_len
)?;
let mut cc = 65535;
for x in 0..self.info.width {
if let Some((hfb, s)) = self.get(x) {
if hfb != cc {
write!(f, "({hfb})")?;
cc = hfb;
}
f.write_str(std::str::from_utf8(s).unwrap_or("?"))?;
} else {
break;
}
}
Ok(())
}
}