avm2: Implement `removeChildren`.
This also changes the underlying `DisplayObjectContainer` method to accept any type of range. Turns out enum trait objects aren't actually trait objects and don't need to worry about object safety!
This commit is contained in:
parent
38fd29ae5f
commit
cf0ab2d82f
|
@ -11,6 +11,7 @@ use crate::avm2::Error;
|
|||
use crate::context::UpdateContext;
|
||||
use crate::display_object::{DisplayObject, TDisplayObject, TDisplayObjectContainer};
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
use std::cmp::min;
|
||||
|
||||
/// Implements `flash.display.DisplayObjectContainer`'s instance constructor.
|
||||
pub fn instance_init<'gc>(
|
||||
|
@ -370,6 +371,57 @@ pub fn remove_child_at<'gc>(
|
|||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Implements `DisplayObjectContainer.removeChildren`
|
||||
pub fn remove_children<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(parent) = this.and_then(|this| this.as_display_object()) {
|
||||
if let Some(mut ctr) = parent.as_container() {
|
||||
let from = args
|
||||
.get(0)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| 0.into())
|
||||
.coerce_to_i32(activation)?;
|
||||
let to = args
|
||||
.get(1)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| i32::MAX.into())
|
||||
.coerce_to_i32(activation)?;
|
||||
|
||||
if from >= ctr.num_children() as i32 || from < 0 {
|
||||
return Err(format!(
|
||||
"RangeError: Starting position {} does not exist in the child list (valid range is 0 to {})",
|
||||
from,
|
||||
ctr.num_children()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if (to >= ctr.num_children() as i32 || to < 0) && to != i32::MAX {
|
||||
return Err(format!(
|
||||
"RangeError: Ending position {} does not exist in the child list (valid range is 0 to {})",
|
||||
to,
|
||||
ctr.num_children()
|
||||
)
|
||||
.into());
|
||||
}
|
||||
|
||||
if from > to {
|
||||
return Err(format!("RangeError: Range {} to {} is invalid", from, to).into());
|
||||
}
|
||||
|
||||
ctr.remove_range_of_ids(
|
||||
&mut activation.context,
|
||||
from as usize..min(ctr.num_children(), to as usize + 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
}
|
||||
|
||||
/// Construct `DisplayObjectContainer`'s class.
|
||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||
let class = Class::new(
|
||||
|
@ -421,6 +473,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
|
|||
QName::new(Namespace::public_namespace(), "removeChildAt"),
|
||||
Method::from_builtin(remove_child_at),
|
||||
));
|
||||
write.define_instance_trait(Trait::from_method(
|
||||
QName::new(Namespace::public_namespace(), "removeChildren"),
|
||||
Method::from_builtin(remove_children),
|
||||
));
|
||||
|
||||
class
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ use ruffle_macros::enum_trait_object;
|
|||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fmt::Debug;
|
||||
use std::ops::Range;
|
||||
use std::ops::RangeBounds;
|
||||
|
||||
#[enum_trait_object(
|
||||
#[derive(Clone, Collect, Debug, Copy)]
|
||||
|
@ -113,11 +113,9 @@ pub trait TDisplayObjectContainer<'gc>:
|
|||
|
||||
/// Remove a set of children identified by their render list IDs from this
|
||||
/// container's render, depth, and execution lists.
|
||||
fn remove_range_of_ids(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
range: Range<usize>,
|
||||
);
|
||||
fn remove_range_of_ids<R>(&mut self, context: &mut UpdateContext<'_, 'gc, '_>, range: R)
|
||||
where
|
||||
R: RangeBounds<usize>;
|
||||
|
||||
/// Clear all three lists in the container.
|
||||
fn clear(&mut self, context: MutationContext<'gc, '_>);
|
||||
|
@ -238,11 +236,10 @@ macro_rules! impl_display_object_container {
|
|||
.remove_child(context, child)
|
||||
}
|
||||
|
||||
fn remove_range_of_ids(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
range: Range<usize>,
|
||||
) {
|
||||
fn remove_range_of_ids<R>(&mut self, context: &mut UpdateContext<'_, 'gc, '_>, range: R)
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
self.0
|
||||
.write(context.gc_context)
|
||||
.$field
|
||||
|
@ -625,11 +622,10 @@ impl<'gc> ChildContainer<'gc> {
|
|||
|
||||
/// Remove a set of children identified by their render list IDs from this
|
||||
/// container's render, depth, and execution lists.
|
||||
pub fn remove_range_of_ids(
|
||||
&mut self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
range: Range<usize>,
|
||||
) {
|
||||
pub fn remove_range_of_ids<R>(&mut self, context: &mut UpdateContext<'_, 'gc, '_>, range: R)
|
||||
where
|
||||
R: RangeBounds<usize>,
|
||||
{
|
||||
let removed_list: Vec<DisplayObject<'gc>> = self.render_list.drain(range).collect();
|
||||
|
||||
for removed in removed_list {
|
||||
|
|
|
@ -7,7 +7,7 @@ pub use crate::{
|
|||
impl_display_object, impl_display_object_container, impl_display_object_sansbounds,
|
||||
};
|
||||
pub use log::{error, info, trace, warn};
|
||||
pub use std::ops::Range;
|
||||
pub use std::ops::RangeBounds;
|
||||
pub use swf::Matrix;
|
||||
pub use swf::{CharacterId, Color, Twips};
|
||||
|
||||
|
|
|
@ -426,6 +426,7 @@ swf_tests! {
|
|||
(as3_displayobjectcontainer_contains, "avm2/displayobjectcontainer_contains", 5),
|
||||
(as3_displayobjectcontainer_getchildindex, "avm2/displayobjectcontainer_getchildindex", 5),
|
||||
(as3_displayobjectcontainer_removechildat, "avm2/displayobjectcontainer_removechildat", 1),
|
||||
(as3_displayobjectcontainer_removechildren, "avm2/displayobjectcontainer_removechildren", 5),
|
||||
}
|
||||
|
||||
// TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough.
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
//var chx = this.getChildAt(0)
|
||||
//var chy = this.getChildAt(1)
|
||||
//var chz = this.getChildAt(2)
|
||||
//this.removeChildren(0, 1)
|
||||
//this.getChildAt(0) === chz
|
||||
true
|
||||
//this.numChildren
|
||||
1
|
||||
//this.removeChildren(0,0x7fffffff)
|
||||
//this.numChildren
|
||||
0
|
||||
//var chx = this.getChildAt(0)
|
||||
//var chy = this.getChildAt(1)
|
||||
//var chz = this.getChildAt(2)
|
||||
//this.removeChildren(1)
|
||||
//this.getChildAt(0) === chx
|
||||
true
|
||||
//this.numChildren
|
||||
1
|
||||
//this.removeChildren()
|
||||
//this.numChildren
|
||||
0
|
||||
//var chx = this.getChildAt(0)
|
||||
//var chy = this.getChildAt(1)
|
||||
//var chz = this.getChildAt(2)
|
||||
//this.removeChildren(0, 0)
|
||||
//this.getChildAt(0) === chy
|
||||
true
|
||||
//this.getChildAt(1) === chz
|
||||
true
|
||||
//this.numChildren
|
||||
2
|
||||
//this.removeChildren(0, 0)
|
||||
//this.getChildAt(0) === chz
|
||||
true
|
||||
//this.numChildren
|
||||
1
|
||||
//this.removeChildren(0, 0)
|
||||
//this.numChildren
|
||||
0
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue