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::context::UpdateContext;
|
||||||
use crate::display_object::{DisplayObject, TDisplayObject, TDisplayObjectContainer};
|
use crate::display_object::{DisplayObject, TDisplayObject, TDisplayObjectContainer};
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
use std::cmp::min;
|
||||||
|
|
||||||
/// Implements `flash.display.DisplayObjectContainer`'s instance constructor.
|
/// Implements `flash.display.DisplayObjectContainer`'s instance constructor.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
|
@ -370,6 +371,57 @@ pub fn remove_child_at<'gc>(
|
||||||
Ok(Value::Undefined)
|
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.
|
/// Construct `DisplayObjectContainer`'s class.
|
||||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||||
let class = Class::new(
|
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"),
|
QName::new(Namespace::public_namespace(), "removeChildAt"),
|
||||||
Method::from_builtin(remove_child_at),
|
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
|
class
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,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::Range;
|
use std::ops::RangeBounds;
|
||||||
|
|
||||||
#[enum_trait_object(
|
#[enum_trait_object(
|
||||||
#[derive(Clone, Collect, Debug, Copy)]
|
#[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
|
/// Remove a set of children identified by their render list IDs from this
|
||||||
/// container's render, depth, and execution lists.
|
/// container's render, depth, and execution lists.
|
||||||
fn remove_range_of_ids(
|
fn remove_range_of_ids<R>(&mut self, context: &mut UpdateContext<'_, 'gc, '_>, range: R)
|
||||||
&mut self,
|
where
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
R: RangeBounds<usize>;
|
||||||
range: Range<usize>,
|
|
||||||
);
|
|
||||||
|
|
||||||
/// Clear all three lists in the container.
|
/// Clear all three lists in the container.
|
||||||
fn clear(&mut self, context: MutationContext<'gc, '_>);
|
fn clear(&mut self, context: MutationContext<'gc, '_>);
|
||||||
|
@ -238,11 +236,10 @@ macro_rules! impl_display_object_container {
|
||||||
.remove_child(context, child)
|
.remove_child(context, child)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_range_of_ids(
|
fn remove_range_of_ids<R>(&mut self, context: &mut UpdateContext<'_, 'gc, '_>, range: R)
|
||||||
&mut self,
|
where
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
R: RangeBounds<usize>,
|
||||||
range: Range<usize>,
|
{
|
||||||
) {
|
|
||||||
self.0
|
self.0
|
||||||
.write(context.gc_context)
|
.write(context.gc_context)
|
||||||
.$field
|
.$field
|
||||||
|
@ -625,11 +622,10 @@ impl<'gc> ChildContainer<'gc> {
|
||||||
|
|
||||||
/// Remove a set of children identified by their render list IDs from this
|
/// Remove a set of children identified by their render list IDs from this
|
||||||
/// container's render, depth, and execution lists.
|
/// container's render, depth, and execution lists.
|
||||||
pub fn remove_range_of_ids(
|
pub fn remove_range_of_ids<R>(&mut self, context: &mut UpdateContext<'_, 'gc, '_>, range: R)
|
||||||
&mut self,
|
where
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
R: RangeBounds<usize>,
|
||||||
range: Range<usize>,
|
{
|
||||||
) {
|
|
||||||
let removed_list: Vec<DisplayObject<'gc>> = self.render_list.drain(range).collect();
|
let removed_list: Vec<DisplayObject<'gc>> = self.render_list.drain(range).collect();
|
||||||
|
|
||||||
for removed in removed_list {
|
for removed in removed_list {
|
||||||
|
|
|
@ -7,7 +7,7 @@ pub use crate::{
|
||||||
impl_display_object, impl_display_object_container, impl_display_object_sansbounds,
|
impl_display_object, impl_display_object_container, impl_display_object_sansbounds,
|
||||||
};
|
};
|
||||||
pub use log::{error, info, trace, warn};
|
pub use log::{error, info, trace, warn};
|
||||||
pub use std::ops::Range;
|
pub use std::ops::RangeBounds;
|
||||||
pub use swf::Matrix;
|
pub use swf::Matrix;
|
||||||
pub use swf::{CharacterId, Color, Twips};
|
pub use swf::{CharacterId, Color, Twips};
|
||||||
|
|
||||||
|
|
|
@ -426,6 +426,7 @@ swf_tests! {
|
||||||
(as3_displayobjectcontainer_contains, "avm2/displayobjectcontainer_contains", 5),
|
(as3_displayobjectcontainer_contains, "avm2/displayobjectcontainer_contains", 5),
|
||||||
(as3_displayobjectcontainer_getchildindex, "avm2/displayobjectcontainer_getchildindex", 5),
|
(as3_displayobjectcontainer_getchildindex, "avm2/displayobjectcontainer_getchildindex", 5),
|
||||||
(as3_displayobjectcontainer_removechildat, "avm2/displayobjectcontainer_removechildat", 1),
|
(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.
|
// 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