use bevy::ecs::world::{DeferredWorld, World};
use bevy::prelude::Entity;
use crate::View;
pub struct IndexedListItem<V: View> {
view: Option<V>,
state: V::State,
}
impl<V: View> IndexedListItem<V> {
fn nodes(&self, world: &World, out: &mut Vec<Entity>) {
self.view.as_ref().unwrap().nodes(world, &self.state, out);
}
}
#[doc(hidden)]
pub struct ForIndex<Item: Send + Clone, V: View, F: Fn(&Item, usize) -> V + Send, FB: View> {
items: Vec<Item>,
each: F,
fallback: Option<FB>,
}
impl<Item: Send + Clone, V: View, F: Fn(&Item, usize) -> V + Send> ForIndex<Item, V, F, ()> {
pub fn new(items: &[Item], each: F) -> Self {
Self {
items: Vec::from(items),
each,
fallback: None,
}
}
}
impl<Item: Send + Clone, V: View, F: Fn(&Item, usize) -> V + Send, FB: View>
ForIndex<Item, V, F, FB>
{
pub fn with_fallback<FB2: View>(self, fallback: FB2) -> ForIndex<Item, V, F, FB2> {
ForIndex::<Item, V, F, FB2> {
items: self.items,
each: self.each,
fallback: Some(fallback),
}
}
}
impl<
Item: Send + Sync + Clone + 'static,
V: View,
F: Fn(&Item, usize) -> V + Send + Sync + Clone + 'static,
FB: View,
> View for ForIndex<Item, V, F, FB>
where
V::State: Clone,
{
type State = (Vec<IndexedListItem<V>>, Option<FB::State>);
fn nodes(&self, world: &World, state: &Self::State, out: &mut Vec<Entity>) {
state.0.iter().for_each(|item| item.nodes(world, out));
if let Some(ref fallback) = self.fallback {
if let Some(ref fbstate) = state.1 {
fallback.nodes(world, fbstate, out);
}
}
}
fn build(&self, cx: &mut crate::Cx) -> Self::State {
let mut state = (Vec::new(), None);
self.rebuild(cx, &mut state);
state
}
fn rebuild(&self, cx: &mut crate::Cx, state: &mut Self::State) -> bool {
let next_len = self.items.len();
let mut prev_len = state.0.len();
let mut i = 0usize;
let mut changed = false;
while i < next_len && i < prev_len {
let child_state = &mut state.0[i];
child_state.view = Some((self.each)(&self.items[i], i));
changed |= child_state
.view
.as_ref()
.unwrap()
.rebuild(cx, &mut child_state.state);
i += 1;
}
while i < next_len {
let view = (self.each)(&self.items[i], i);
let st = view.build(cx);
state.0.push(IndexedListItem {
view: Some(view),
state: st,
});
i += 1;
changed = true;
}
while i < prev_len {
prev_len -= 1;
let child_state = &mut state.0[prev_len];
if let Some(ref view) = child_state.view {
view.raze(
&mut DeferredWorld::from(cx.world_mut()),
&mut child_state.state,
);
}
state.0.pop();
changed = true;
}
if let Some(ref fallback) = self.fallback {
match state.1 {
Some(ref mut fb_ent) if next_len > 0 => {
fallback.raze(&mut DeferredWorld::from(cx.world_mut()), fb_ent);
state.1 = None;
changed = true;
}
None if next_len == 0 => {
state.1 = Some(fallback.build(cx));
changed = true;
}
_ => {}
}
}
changed
}
fn raze(&self, world: &mut DeferredWorld, state: &mut Self::State) {
let prev_len = state.0.len();
let mut i = 0usize;
while i < prev_len {
let child_state = &mut state.0[i];
if let Some(ref view) = child_state.view {
view.raze(world, &mut child_state.state);
}
i += 1;
}
if let Some(ref mut fbstate) = state.1 {
self.fallback.as_ref().unwrap().raze(world, fbstate);
}
}
}