use crate::layout::{GridCellInfo, GridDimensions};
use crate::{Node, Tile, Widget};
use kas_macros::impl_self;
use std::ops::RangeBounds;
pub trait Collection {
type Data;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn len(&self) -> usize;
fn get_tile(&self, index: usize) -> Option<&dyn Tile>;
fn get_mut_tile(&mut self, index: usize) -> Option<&mut dyn Tile>;
fn child_node<'n>(&'n mut self, data: &'n Self::Data, index: usize) -> Option<Node<'n>>;
fn iter_tile(&self, range: impl RangeBounds<usize>) -> CollectionIterTile<'_, Self> {
use std::ops::Bound::{Excluded, Included, Unbounded};
let start = match range.start_bound() {
Included(start) => *start,
Excluded(start) => *start + 1,
Unbounded => 0,
};
let end = match range.end_bound() {
Included(end) => *end + 1,
Excluded(end) => *end,
Unbounded => self.len(),
};
CollectionIterTile {
start,
end,
collection: self,
}
}
fn binary_search_by<'a, F>(&'a self, mut f: F) -> Option<Result<usize, usize>>
where
F: FnMut(&'a dyn Tile) -> std::cmp::Ordering,
{
use std::cmp::Ordering::{Greater, Less};
let mut size = self.len();
let mut left = 0;
let mut right = size;
while left < right {
let mid = left + size / 2;
let cmp = f(self.get_tile(mid)?);
if cmp == Less {
left = mid + 1;
} else if cmp == Greater {
right = mid;
} else {
return Some(Ok(mid));
}
size = right - left;
}
Some(Err(left))
}
}
pub trait CellCollection: Collection {
fn cell_info(&self, index: usize) -> Option<GridCellInfo>;
fn iter_cell_info(&self, range: impl RangeBounds<usize>) -> CollectionIterCellInfo<'_, Self> {
use std::ops::Bound::{Excluded, Included, Unbounded};
let start = match range.start_bound() {
Included(start) => *start,
Excluded(start) => *start + 1,
Unbounded => 0,
};
let end = match range.end_bound() {
Included(end) => *end + 1,
Excluded(end) => *end,
Unbounded => self.len(),
};
CollectionIterCellInfo {
start,
end,
collection: self,
}
}
fn grid_dimensions(&self) -> GridDimensions {
let mut dim = GridDimensions::default();
let (mut last_col, mut last_row) = (0, 0);
for cell_info in self.iter_cell_info(..) {
last_col = last_col.max(cell_info.last_col);
last_row = last_row.max(cell_info.last_row);
if cell_info.last_col > cell_info.col {
dim.col_spans += 1;
}
if cell_info.last_row > cell_info.row {
dim.row_spans += 1;
}
}
dim.cols = last_col + 1;
dim.rows = last_row + 1;
dim
}
}
#[impl_self]
mod CollectionIterTile {
pub struct CollectionIterTile<'a, C: Collection + ?Sized> {
start: usize,
end: usize,
collection: &'a C,
}
impl Iterator for Self {
type Item = &'a dyn Tile;
fn next(&mut self) -> Option<Self::Item> {
let index = self.start;
if index < self.end {
self.start += 1;
self.collection.get_tile(index)
} else {
None
}
}
}
impl DoubleEndedIterator for Self {
fn next_back(&mut self) -> Option<Self::Item> {
if self.start < self.end {
let index = self.end - 1;
self.end = index;
self.collection.get_tile(index)
} else {
None
}
}
}
impl ExactSizeIterator for Self {}
}
#[impl_self]
mod CollectionIterCellInfo {
pub struct CollectionIterCellInfo<'a, C: CellCollection + ?Sized> {
start: usize,
end: usize,
collection: &'a C,
}
impl Iterator for Self {
type Item = GridCellInfo;
fn next(&mut self) -> Option<Self::Item> {
let index = self.start;
if index < self.end {
self.start += 1;
self.collection.cell_info(index)
} else {
None
}
}
}
impl DoubleEndedIterator for Self {
fn next_back(&mut self) -> Option<Self::Item> {
if self.start < self.end {
let index = self.end - 1;
self.end = index;
self.collection.cell_info(index)
} else {
None
}
}
}
impl ExactSizeIterator for Self {}
}
macro_rules! impl_slice {
(($($gg:tt)*) for $t:ty as $w:ident in $pat:pat) => {
impl<$($gg)*> Collection for $t {
type Data = W::Data;
#[inline]
fn len(&self) -> usize {
<[_]>::len(self)
}
#[inline]
fn get_tile(&self, index: usize) -> Option<&dyn Tile> {
self.get(index).map(|$pat| $w as &dyn Tile)
}
#[inline]
fn get_mut_tile(&mut self, index: usize) -> Option<&mut dyn Tile> {
self.get_mut(index).map(|$pat| $w as &mut dyn Tile)
}
#[inline]
fn child_node<'n>(
&'n mut self,
data: &'n Self::Data,
index: usize,
) -> Option<Node<'n>> {
self.get_mut(index).map(|$pat| $w.as_node(data))
}
#[inline]
fn binary_search_by<'a, F>(&'a self, mut f: F) -> Option<Result<usize, usize>>
where
F: FnMut(&'a dyn Tile) -> std::cmp::Ordering,
{
Some(<[_]>::binary_search_by(self, move |$pat| f($w.as_tile())))
}
}
};
}
impl_slice!((const N: usize, W: Widget) for [W; N] as w in w);
impl_slice!((W: Widget) for [W] as w in w);
impl_slice!((W: Widget) for Vec<W> as w in w);
impl_slice!((const N: usize, W: Widget) for [(GridCellInfo, W); N] as w in (_, w));
impl_slice!((W: Widget) for [(GridCellInfo, W)] as w in (_, w));
impl_slice!((W: Widget) for Vec<(GridCellInfo, W)> as w in (_, w));
impl<W: Widget, C: Collection> CellCollection for C
where
C: std::ops::Deref<Target = [(GridCellInfo, W)]>,
{
fn cell_info(&self, index: usize) -> Option<GridCellInfo> {
self.get(index).map(|(info, _)| *info)
}
}