#[cfg(test)]
mod tests;
pub trait Cursor {
fn index(self) -> u32;
}
impl Cursor for u32 {
fn index(self) -> u32 {
self
}
}
pub trait VariableSizeBuffer {
type Cursor: Cursor + Copy;
type Item<'a>
where
Self: 'a;
fn count(&self) -> u32;
fn size(item: &Self::Item<'_>) -> usize;
fn before(&self, cursor: Self::Cursor) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
fn after(&self, cursor: Self::Cursor) -> impl DoubleEndedIterator<Item = Self::Item<'_>>;
fn sizes_before(&self, cursor: Self::Cursor) -> impl DoubleEndedIterator<Item = usize> {
self.before(cursor).map(|item| Self::size(&item))
}
fn sizes_after(&self, cursor: Self::Cursor) -> impl DoubleEndedIterator<Item = usize> {
self.after(cursor).map(|item| Self::size(&item))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LayoutView<'a> {
pub below: &'a [u16],
pub above: &'a [u16],
}
#[derive(Debug, Default)]
pub struct Layout {
previous_cursor: u32,
screen_index: u16,
below: Vec<u16>,
above: Vec<u16>,
}
impl Layout {
fn view(&self) -> LayoutView {
debug_assert!(self.below.iter().sum::<u16>() == self.screen_index + 1);
LayoutView {
below: &self.below,
above: &self.above,
}
}
#[inline]
fn extend_layout<I: Iterator<Item = usize>>(
buffer: &mut Vec<u16>,
remaining_size: u16,
required_sizes: I,
) -> Option<u16> {
Self::extend_layout_excess(buffer, remaining_size, required_sizes).ok()
}
#[inline]
fn extend_layout_excess<I: Iterator<Item = usize>>(
buffer: &mut Vec<u16>,
mut remaining_size: u16,
required_sizes: I,
) -> Result<u16, usize> {
for size in required_sizes {
if size >= remaining_size.into() {
if remaining_size != 0 {
buffer.push(remaining_size);
}
return Err(size - remaining_size as usize);
}
remaining_size -= size as u16;
buffer.push(size as u16);
}
Ok(remaining_size)
}
#[inline]
fn clamp_indices<B: VariableSizeBuffer>(&mut self, size: u16, padding_top: u16, buffer: &B) {
self.screen_index = self.screen_index.min(size - padding_top - 1);
self.previous_cursor = self.previous_cursor.min(buffer.count().saturating_sub(1));
}
#[must_use]
pub fn recompute<B: VariableSizeBuffer>(
&mut self,
total_size: u16,
padding_bottom: u16,
padding_top: u16,
cursor: <B as VariableSizeBuffer>::Cursor,
buffer: &B,
) -> LayoutView {
debug_assert!(padding_bottom + padding_top < total_size);
debug_assert!(cursor.index() < buffer.count());
self.clamp_indices(total_size, padding_top, buffer);
self.below.clear();
self.above.clear();
self.screen_index = if cursor.index() >= self.previous_cursor {
self.recompute_above(total_size, padding_top, cursor, buffer)
} else {
self.recompute_below(total_size, padding_bottom, padding_top, cursor, buffer)
};
self.previous_cursor = cursor.index();
debug_assert!(self.screen_index < total_size);
let view = self.view();
debug_assert!(
view.above.iter().sum::<u16>() + view.below.iter().sum::<u16>() <= total_size
);
view
}
#[inline]
fn recompute_above<B: VariableSizeBuffer>(
&mut self,
total_size: u16,
padding_top: u16,
cursor: <B as VariableSizeBuffer>::Cursor,
buffer: &B,
) -> u16 {
let mut sizes_below_incl = buffer.sizes_before(cursor);
let sizes_above = buffer.sizes_after(cursor);
let remaining_space_above = match Self::extend_layout(
&mut self.below,
total_size - padding_top,
sizes_below_incl
.by_ref()
.take((cursor.index() - self.previous_cursor + 1) as usize),
) {
None => {
padding_top
}
Some(remaining_space_below) => {
let threshold = (self.screen_index + 1).saturating_sub(*self.below.last().unwrap());
let (remaining_space_below, remaining_space_above) =
if threshold < remaining_space_below {
(threshold, padding_top + remaining_space_below - threshold)
} else {
(remaining_space_below, padding_top)
};
remaining_space_above
+ Self::extend_layout(&mut self.below, remaining_space_below, sizes_below_incl)
.unwrap_or(0)
}
};
Self::extend_layout(&mut self.above, remaining_space_above, sizes_above);
total_size - remaining_space_above - 1
}
#[inline]
fn recompute_below<B: VariableSizeBuffer>(
&mut self,
total_size: u16,
padding_bottom: u16,
padding_top: u16,
cursor: <B as VariableSizeBuffer>::Cursor,
buffer: &B,
) -> u16 {
let mut sizes_below_incl = buffer.sizes_before(cursor);
let mut sizes_above = buffer.sizes_after(cursor);
match Self::extend_layout(
&mut self.below,
total_size - padding_top,
sizes_below_incl.by_ref().take(1),
) {
None => {
Self::extend_layout(&mut self.above, padding_top, sizes_above);
total_size - padding_top - 1
}
Some(remaining) => {
let selection_size = total_size - padding_top - remaining;
let (total_bottom_size, bottom_item_excess) = if selection_size > padding_bottom {
(selection_size, 0)
} else {
match Self::extend_layout_excess(
&mut self.below,
padding_bottom - selection_size + 1,
sizes_below_incl.by_ref(),
) {
Ok(remaining_bottom_padding) => {
Self::extend_layout(
&mut self.above,
total_size - padding_bottom - 1 + remaining_bottom_padding,
sizes_above,
);
return padding_bottom - remaining_bottom_padding;
}
Err(bottom_item_excess) => (padding_bottom + 1, bottom_item_excess),
}
};
total_bottom_size - 1
+ match Self::extend_layout(
&mut self.above,
total_size - total_bottom_size,
sizes_above
.by_ref()
.take((self.previous_cursor - cursor.index()) as usize),
) {
None => {
0
}
Some(remaining_space) => {
let max_space_above = total_size - self.screen_index - 1;
let (remaining_space_below, remaining_space_above) =
if max_space_above < remaining_space {
(remaining_space - max_space_above, max_space_above)
} else {
(0, remaining_space)
};
if self.previous_cursor + 1 < buffer.count() {
Self::extend_layout(
&mut self.above,
remaining_space_above,
sizes_above,
);
}
if bottom_item_excess >= remaining_space_below as usize {
*self.below.last_mut().unwrap() += remaining_space_below;
remaining_space_below
} else {
*self.below.last_mut().unwrap() += bottom_item_excess as u16;
remaining_space_below
- Self::extend_layout(
&mut self.below,
remaining_space_below - bottom_item_excess as u16,
sizes_below_incl,
)
.unwrap_or(0)
}
}
}
}
}
}
}