core: Add the ability to iterate over the depth list of any container

This commit is contained in:
David Wendt 2021-04-14 23:25:37 -04:00 committed by Mike Welsh
parent 14430b9eb0
commit 0011753914
1 changed files with 115 additions and 9 deletions

View File

@ -13,7 +13,7 @@ use ruffle_macros::enum_trait_object;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fmt::Debug; use std::fmt::Debug;
use std::ops::RangeBounds; use std::ops::{Bound, RangeBounds};
/// Dispatch the `removedFromStage` event on a child and all of it's /// Dispatch the `removedFromStage` event on a child and all of it's
/// grandchildren, recursively. /// grandchildren, recursively.
@ -188,8 +188,12 @@ pub trait TDisplayObjectContainer<'gc>:
fn num_children(self) -> usize; fn num_children(self) -> usize;
/// Returns the highest depth on the render list, or `None` if no children /// Returns the highest depth on the render list, or `None` if no children
/// exist on the depth list. /// have a depth less than the provided value.
fn highest_depth(self) -> Option<Depth>; fn highest_depth(self, less_than: Depth) -> Option<Depth>;
/// Returns the lowest depth on the render list, or `None` if no children
/// have a depth greater than the provided value.
fn lowest_depth(self, greater_than: Depth) -> Option<Depth>;
/// Insert a child display object into the container at a specific position /// Insert a child display object into the container at a specific position
/// in the depth list, removing any child already at that position. /// in the depth list, removing any child already at that position.
@ -312,6 +316,20 @@ pub trait TDisplayObjectContainer<'gc>:
RenderIter::from_container(self.into()) RenderIter::from_container(self.into())
} }
/// Iterates over the children of this display object in depth order. This
/// is different than execution or render orders.
///
/// This yields an iterator that does *not* lock the parent and can be
/// safely held in situations where display objects need to be unlocked.
/// This means that unexpected but legal and defined items may be yielded
/// due to intended or unintended list manipulation by the caller.
///
/// The iterator's concrete type is stated here due to Rust language
/// limitations.
fn iter_depth_list(self) -> DepthIter<'gc> {
DepthIter::from_container(self.into())
}
/// Renders the children of this container in render list order. /// Renders the children of this container in render list order.
fn render_children(self, context: &mut RenderContext<'_, 'gc>) { fn render_children(self, context: &mut RenderContext<'_, 'gc>) {
let mut clip_depth = 0; let mut clip_depth = 0;
@ -381,8 +399,12 @@ macro_rules! impl_display_object_container {
self.0.read().$field.num_children() self.0.read().$field.num_children()
} }
fn highest_depth(self) -> Option<Depth> { fn highest_depth(self, less_than: Depth) -> Option<Depth> {
self.0.read().$field.highest_depth() self.0.read().$field.highest_depth(less_than)
}
fn lowest_depth(self, greater_than: Depth) -> Option<Depth> {
self.0.read().$field.lowest_depth(greater_than)
} }
fn replace_at_depth( fn replace_at_depth(
@ -786,10 +808,23 @@ impl<'gc> ChildContainer<'gc> {
} }
} }
/// Returns the highest depth in use by this container, or `None` if there /// Returns the highest depth on the render list, or `None` if no children
/// are no children. /// have a depth less than the provided value.
pub fn highest_depth(&self) -> Option<Depth> { pub fn highest_depth(&self, less_than: Depth) -> Option<Depth> {
self.depth_list.keys().copied().rev().next() self.depth_list
.range(..less_than)
.rev()
.map(|(k, _v)| *k)
.next()
}
/// Returns the lowest depth on the render list, or `None` if no children
/// have a depth greater than the provided value.
pub fn lowest_depth(&self, greater_than: Depth) -> Option<Depth> {
self.depth_list
.range((Bound::Excluded(greater_than), Bound::<Depth>::Unbounded))
.map(|(k, _v)| *k)
.next()
} }
/// Determine if the render list is empty. /// Determine if the render list is empty.
@ -1066,3 +1101,74 @@ impl<'gc> DoubleEndedIterator for RenderIter<'gc> {
this this
} }
} }
pub struct DepthIter<'gc> {
src: DisplayObjectContainer<'gc>,
depth: Option<Depth>,
neg_depth: Option<Depth>,
}
impl<'gc> DepthIter<'gc> {
fn from_container(src: DisplayObjectContainer<'gc>) -> Self {
Self {
src,
depth: Some(Depth::MIN),
neg_depth: Some(Depth::MAX),
}
}
}
impl<'gc> Iterator for DepthIter<'gc> {
type Item = (Depth, DisplayObject<'gc>);
fn next(&mut self) -> Option<Self::Item> {
if self.depth.is_none()
|| self.neg_depth.is_none()
|| self.depth.unwrap() > self.neg_depth.unwrap()
{
return None;
}
let mut old_depth = self.depth.unwrap();
let (new_depth, new_next) = if let Some(next) = self.src.child_by_depth(old_depth) {
(self.src.lowest_depth(old_depth), Some(next))
} else if let Some(new_depth) = self.src.lowest_depth(old_depth) {
old_depth = new_depth;
(
self.src.lowest_depth(new_depth),
self.src.child_by_depth(new_depth),
)
} else {
(None, None)
};
self.depth = new_depth;
new_next.map(|n| (old_depth, n))
}
}
impl<'gc> DoubleEndedIterator for DepthIter<'gc> {
fn next_back(&mut self) -> Option<Self::Item> {
if self.depth.is_none() || self.neg_depth.is_none() || self.neg_depth <= self.depth {
return None;
}
let mut old_neg_depth = self.neg_depth.unwrap();
let (new_neg_depth, new_next) = if let Some(next) = self.src.child_by_depth(old_neg_depth) {
(self.src.highest_depth(old_neg_depth), Some(next))
} else if let Some(new_neg_depth) = self.src.highest_depth(old_neg_depth) {
old_neg_depth = new_neg_depth;
(
self.src.highest_depth(new_neg_depth),
self.src.child_by_depth(new_neg_depth),
)
} else {
(None, None)
};
self.neg_depth = new_neg_depth;
new_next.map(|n| (old_neg_depth, n))
}
}