use super::*;
#[derive(Default)]
pub(super) struct WrapGeom {
pub heights: Vec<u16>,
pub offsets: Vec<usize>,
pub width: usize,
pub dirty: bool,
}
impl App {
pub(super) const UNI_PREFIX: usize = 13;
pub(super) const SPLIT_PREFIX: usize = 5;
pub(super) fn split_side_w(width: usize) -> usize {
width.saturating_sub(str_width(SPLIT_DIVIDER)) / 2
}
pub(super) fn row_h(&self, idx: usize) -> usize {
if !self.wrap {
return 1;
}
self.geom.heights.get(idx).copied().unwrap_or(1) as usize
}
pub(super) fn display_lines(&self, start: usize, end: usize) -> usize {
if !self.wrap {
return end.saturating_sub(start);
}
if end < self.geom.offsets.len() && start <= end {
return self.geom.offsets[end] - self.geom.offsets[start];
}
(start..end).map(|i| self.row_h(i)).sum()
}
pub(super) fn max_scroll_row(&self, start: usize, end: usize, height: usize) -> usize {
if end <= start {
return start;
}
let mut acc = 0usize;
let mut t = end;
while t > start {
let h = self.row_h(t - 1);
if acc + h > height {
break;
}
acc += h;
t -= 1;
}
t.min(end - 1).max(start)
}
pub(super) fn top_to_show(&self, start: usize, bottom: usize, height: usize) -> usize {
let mut acc = 0usize;
let mut t = bottom + 1;
while t > start {
let h = self.row_h(t - 1);
if acc + h > height {
break;
}
acc += h;
t -= 1;
}
t.min(bottom).max(start)
}
pub(super) fn update_heights(&mut self, width: usize) {
if !self.wrap {
if !self.geom.heights.is_empty() {
self.geom.heights.clear();
self.geom.offsets.clear();
}
self.geom.width = width;
self.geom.dirty = false;
return;
}
if !self.geom.dirty
&& self.geom.width == width
&& self.geom.heights.len() == self.active_len()
{
return;
}
let uni_budget = width.saturating_sub(Self::UNI_PREFIX);
let side_budget = Self::split_side_w(width).saturating_sub(Self::SPLIT_PREFIX);
let heights: Vec<u16> = match self.view {
View::Unified => self
.rows
.iter()
.map(|r| match &r.kind {
RowKind::Line { .. } => {
wrap_count(r.text.get(1..).unwrap_or(""), uni_budget).min(u16::MAX as usize)
as u16
}
_ => 1,
})
.collect(),
View::Split => self
.split_rows
.iter()
.map(|r| match &r.kind {
SplitRowKind::Pair { left, right } => {
let l = left
.as_ref()
.map_or(1, |c| wrap_count(&c.text, side_budget));
let rr = right
.as_ref()
.map_or(1, |c| wrap_count(&c.text, side_budget));
l.max(rr).min(u16::MAX as usize) as u16
}
_ => 1,
})
.collect(),
};
let mut offsets = Vec::with_capacity(heights.len() + 1);
let mut acc = 0usize;
offsets.push(0);
for &h in &heights {
acc += h as usize;
offsets.push(acc);
}
self.geom.heights = heights;
self.geom.offsets = offsets;
self.geom.width = width;
self.geom.dirty = false;
}
pub(super) fn toggle_wrap(&mut self) {
self.wrap = !self.wrap;
self.geom.dirty = true;
self.geom.heights.clear();
self.geom.offsets.clear();
self.ensure_visible();
self.status = if self.wrap {
"wrap on".into()
} else {
"wrap off".into()
};
}
}