diff --git a/core/src/display_object/container.rs b/core/src/display_object/container.rs index d7ebbcb4e..a416b27eb 100644 --- a/core/src/display_object/container.rs +++ b/core/src/display_object/container.rs @@ -13,7 +13,7 @@ use ruffle_macros::enum_trait_object; use std::cmp::Ordering; use std::collections::BTreeMap; 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 /// grandchildren, recursively. @@ -188,8 +188,12 @@ pub trait TDisplayObjectContainer<'gc>: fn num_children(self) -> usize; /// Returns the highest depth on the render list, or `None` if no children - /// exist on the depth list. - fn highest_depth(self) -> Option; + /// have a depth less than the provided value. + fn highest_depth(self, less_than: Depth) -> Option; + + /// 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; /// Insert a child display object into the container at a specific 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()) } + /// 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. fn render_children(self, context: &mut RenderContext<'_, 'gc>) { let mut clip_depth = 0; @@ -381,8 +399,12 @@ macro_rules! impl_display_object_container { self.0.read().$field.num_children() } - fn highest_depth(self) -> Option { - self.0.read().$field.highest_depth() + fn highest_depth(self, less_than: Depth) -> Option { + self.0.read().$field.highest_depth(less_than) + } + + fn lowest_depth(self, greater_than: Depth) -> Option { + self.0.read().$field.lowest_depth(greater_than) } 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 - /// are no children. - pub fn highest_depth(&self) -> Option { - self.depth_list.keys().copied().rev().next() + /// Returns the highest depth on the render list, or `None` if no children + /// have a depth less than the provided value. + pub fn highest_depth(&self, less_than: Depth) -> Option { + 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 { + self.depth_list + .range((Bound::Excluded(greater_than), Bound::::Unbounded)) + .map(|(k, _v)| *k) + .next() } /// Determine if the render list is empty. @@ -1066,3 +1101,74 @@ impl<'gc> DoubleEndedIterator for RenderIter<'gc> { this } } + +pub struct DepthIter<'gc> { + src: DisplayObjectContainer<'gc>, + depth: Option, + neg_depth: Option, +} + +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 { + 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 { + 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)) + } +}