core: Add the ability to iterate over the depth list of any container
This commit is contained in:
parent
14430b9eb0
commit
0011753914
|
@ -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<Depth>;
|
||||
/// have a depth less than the provided value.
|
||||
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
|
||||
/// 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<Depth> {
|
||||
self.0.read().$field.highest_depth()
|
||||
fn highest_depth(self, less_than: Depth) -> Option<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(
|
||||
|
@ -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<Depth> {
|
||||
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<Depth> {
|
||||
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.
|
||||
|
@ -1066,3 +1101,74 @@ impl<'gc> DoubleEndedIterator for RenderIter<'gc> {
|
|||
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))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue