diff options
Diffstat (limited to 'src/model/collapse.rs')
| -rw-r--r-- | src/model/collapse.rs | 100 |
1 files changed, 100 insertions, 0 deletions
diff --git a/src/model/collapse.rs b/src/model/collapse.rs new file mode 100644 index 00000000..5e7a25f8 --- /dev/null +++ b/src/model/collapse.rs @@ -0,0 +1,100 @@ +use super::{StyleChain, StyleVec, StyleVecBuilder}; + +/// A wrapper around a [`StyleVecBuilder`] that allows to collapse items. +pub struct CollapsingBuilder<'a, T> { + builder: StyleVecBuilder<'a, T>, + staged: Vec<(T, StyleChain<'a>, Option<u8>)>, + last: Last, +} + +/// What the last non-ignorant item was. +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +enum Last { + Weak, + Destructive, + Supportive, +} + +impl<'a, T> CollapsingBuilder<'a, T> { + /// Create a new style-vec builder. + pub fn new() -> Self { + Self { + builder: StyleVecBuilder::new(), + staged: vec![], + last: Last::Destructive, + } + } + + /// Can only exist when there is at least one supportive item to its left + /// and to its right, with no destructive items or weak items in between to + /// its left and no destructive items in between to its right. There may be + /// ignorant items in between in both directions. + pub fn weak(&mut self, item: T, strength: u8, styles: StyleChain<'a>) { + if self.last != Last::Destructive { + if self.last == Last::Weak { + if let Some(i) = self + .staged + .iter() + .position(|(.., prev)| prev.map_or(false, |p| p < strength)) + { + self.staged.remove(i); + } else { + return; + } + } + + self.staged.push((item, styles, Some(strength))); + self.last = Last::Weak; + } + } + + /// Forces nearby weak items to collapse. + pub fn destructive(&mut self, item: T, styles: StyleChain<'a>) { + self.flush(false); + self.push(item, styles); + self.last = Last::Destructive; + } + + /// Allows nearby weak items to exist. + pub fn supportive(&mut self, item: T, styles: StyleChain<'a>) { + self.flush(true); + self.push(item, styles); + self.last = Last::Supportive; + } + + /// Has no influence on other items. + pub fn ignorant(&mut self, item: T, styles: StyleChain<'a>) { + self.staged.push((item, styles, None)); + } + + /// Iterate over the contained items. + pub fn items(&self) -> impl DoubleEndedIterator<Item = &T> { + self.builder.items().chain(self.staged.iter().map(|(item, ..)| item)) + } + + /// Return the finish style vec and the common prefix chain. + pub fn finish(mut self) -> (StyleVec<T>, StyleChain<'a>) { + self.flush(false); + self.builder.finish() + } + + /// Push the staged items, filtering out weak items if `supportive` is false. + fn flush(&mut self, supportive: bool) { + for (item, styles, strength) in self.staged.drain(..) { + if supportive || strength.is_none() { + self.builder.push(item, styles); + } + } + } + + /// Push a new item into the style vector. + fn push(&mut self, item: T, styles: StyleChain<'a>) { + self.builder.push(item, styles); + } +} + +impl<'a, T> Default for CollapsingBuilder<'a, T> { + fn default() -> Self { + Self::new() + } +} |
