core: Replace direct container manipulation with `TDisplayObjectContainer` methods wherever possible.

The purpose of this refactor is twofold:

1. Ensure `TDisplayObjectContainer` holds all the container methods we need
2. Ensure that future adjustments to trait methods automatically apply to the display object's own use of the container, in case we want to do things in those methods that can't be done in a borrowed container

There are two places where we cannot use the new trait methods:

1. `Button.set_state` as it holds a borrow at the point we want to clear the container
2. `MovieClipData.goto_remove_object`, since it's a method on the data and thus cannot access trait methods

This particular commit generates a lot of noise as several `DisplayObject` methods were incorrectly marked as non-mutating.
This commit is contained in:
David Wendt 2020-11-14 15:59:18 -05:00 committed by Mike Welsh
parent 9dde91e0fd
commit fec4e3c0a9
14 changed files with 69 additions and 79 deletions

View File

@ -2769,7 +2769,7 @@ impl<'a, 'gc, 'gc_context> Activation<'a, 'gc, 'gc_context> {
if let Some(level) = self.context.levels.get(&level_id) { if let Some(level) = self.context.levels.get(&level_id) {
*level *level
} else { } else {
let level: DisplayObject<'_> = MovieClip::new( let mut level: DisplayObject<'_> = MovieClip::new(
SwfSlice::empty(self.base_clip().movie().unwrap()), SwfSlice::empty(self.base_clip().movie().unwrap()),
self.context.gc_context, self.context.gc_context,
) )

View File

@ -512,7 +512,7 @@ fn attach_movie<'gc>(
return Ok(Value::Undefined); return Ok(Value::Undefined);
} }
if let Ok(new_clip) = activation if let Ok(mut new_clip) = activation
.context .context
.library .library
.library_for_movie(movie_clip.movie().unwrap()) .library_for_movie(movie_clip.movie().unwrap())
@ -568,7 +568,7 @@ fn create_empty_movie_clip<'gc>(
.movie() .movie()
.or_else(|| activation.base_clip().movie()) .or_else(|| activation.base_clip().movie())
.unwrap(); .unwrap();
let new_clip = MovieClip::new(SwfSlice::empty(swf_movie), activation.context.gc_context); let mut new_clip = MovieClip::new(SwfSlice::empty(swf_movie), activation.context.gc_context);
// Set name and attach to parent. // Set name and attach to parent.
new_clip.set_name(activation.context.gc_context, &new_instance_name); new_clip.set_name(activation.context.gc_context, &new_instance_name);
@ -617,7 +617,7 @@ fn create_text_field<'gc>(
.unwrap_or(Value::Undefined) .unwrap_or(Value::Undefined)
.coerce_to_f64(activation)?; .coerce_to_f64(activation)?;
let text_field: DisplayObject<'gc> = let mut text_field: DisplayObject<'gc> =
EditText::new(&mut activation.context, movie, x, y, width, height).into(); EditText::new(&mut activation.context, movie, x, y, width, height).into();
text_field.set_name( text_field.set_name(
activation.context.gc_context, activation.context.gc_context,
@ -687,7 +687,7 @@ pub fn duplicate_movie_clip_with_bias<'gc>(
return Ok(Value::Undefined); return Ok(Value::Undefined);
} }
if let Ok(new_clip) = activation if let Ok(mut new_clip) = activation
.context .context
.library .library
.library_for_movie(movie_clip.movie().unwrap()) .library_for_movie(movie_clip.movie().unwrap())

View File

@ -874,7 +874,7 @@ mod tests {
let mut avm1 = Avm1::new(gc_context, swf_version); let mut avm1 = Avm1::new(gc_context, swf_version);
let mut avm2 = Avm2::new(gc_context); let mut avm2 = Avm2::new(gc_context);
let swf = Arc::new(SwfMovie::empty(swf_version)); let swf = Arc::new(SwfMovie::empty(swf_version));
let root: DisplayObject<'_> = let mut root: DisplayObject<'_> =
MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into(); MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into();
root.set_depth(gc_context, 0); root.set_depth(gc_context, 0);
let mut levels = BTreeMap::new(); let mut levels = BTreeMap::new();

View File

@ -36,7 +36,7 @@ where
let mut avm1 = Avm1::new(gc_context, swf_version); let mut avm1 = Avm1::new(gc_context, swf_version);
let mut avm2 = Avm2::new(gc_context); let mut avm2 = Avm2::new(gc_context);
let swf = Arc::new(SwfMovie::empty(swf_version)); let swf = Arc::new(SwfMovie::empty(swf_version));
let root: DisplayObject<'gc> = let mut root: DisplayObject<'gc> =
MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into(); MovieClip::new(SwfSlice::empty(swf.clone()), gc_context).into();
root.set_depth(gc_context, 0); root.set_depth(gc_context, 0);
let mut levels = BTreeMap::new(); let mut levels = BTreeMap::new();

View File

@ -730,14 +730,14 @@ pub trait TDisplayObject<'gc>:
/// Events execute inside-out; the deepest child will react first, followed by its parent, and /// Events execute inside-out; the deepest child will react first, followed by its parent, and
/// so forth. /// so forth.
fn handle_clip_event( fn handle_clip_event(
&self, &mut self,
_context: &mut UpdateContext<'_, 'gc, '_>, _context: &mut UpdateContext<'_, 'gc, '_>,
_event: ClipEvent, _event: ClipEvent,
) -> ClipEventResult { ) -> ClipEventResult {
ClipEventResult::NotHandled ClipEventResult::NotHandled
} }
fn run_frame(&self, _context: &mut UpdateContext<'_, 'gc, '_>) {} fn run_frame(&mut self, _context: &mut UpdateContext<'_, 'gc, '_>) {}
fn render(&self, _context: &mut RenderContext<'_, 'gc>) {} fn render(&self, _context: &mut RenderContext<'_, 'gc>) {}
fn unload(&self, context: &mut UpdateContext<'_, 'gc, '_>) { fn unload(&self, context: &mut UpdateContext<'_, 'gc, '_>) {
@ -870,7 +870,7 @@ pub trait TDisplayObject<'gc>:
} }
fn post_instantiation( fn post_instantiation(
&self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
_display_object: DisplayObject<'gc>, _display_object: DisplayObject<'gc>,
_init_object: Option<Avm1Object<'gc>>, _init_object: Option<Avm1Object<'gc>>,

View File

@ -80,7 +80,7 @@ impl<'gc> TDisplayObject<'gc> for Bitmap<'gc> {
} }
} }
fn run_frame(&self, _context: &mut UpdateContext) { fn run_frame(&mut self, _context: &mut UpdateContext) {
// Noop // Noop
} }

View File

@ -115,7 +115,7 @@ impl<'gc> Button<'gc> {
/// This function instantiates children and thus must not be called whilst /// This function instantiates children and thus must not be called whilst
/// the caller is holding a write lock on the button data. /// the caller is holding a write lock on the button data.
fn set_state( fn set_state(
&self, &mut self,
self_display_object: DisplayObject<'gc>, self_display_object: DisplayObject<'gc>,
context: &mut crate::context::UpdateContext<'_, 'gc, '_>, context: &mut crate::context::UpdateContext<'_, 'gc, '_>,
state: ButtonState, state: ButtonState,
@ -153,18 +153,11 @@ impl<'gc> Button<'gc> {
drop(write); drop(write);
//TODO: This code originally reinserted children onto the execution list for (mut child, depth) in new_children {
//"backwards" (compared to standard behavior) - do we still want that?
for (child, depth) in new_children {
// Initialize child. // Initialize child.
child.post_instantiation(context, child, None, Instantiator::Movie, false); child.post_instantiation(context, child, None, Instantiator::Movie, false);
child.run_frame(context); child.run_frame(context);
self.0.write(context.gc_context).container.replace_at_depth( self.replace_at_depth(context, child, depth.into());
context,
(*self).into(),
child,
depth.into(),
);
} }
} }
} }
@ -181,7 +174,7 @@ impl<'gc> TDisplayObject<'gc> for Button<'gc> {
} }
fn post_instantiation( fn post_instantiation(
&self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
display_object: DisplayObject<'gc>, display_object: DisplayObject<'gc>,
_init_object: Option<Object<'gc>>, _init_object: Option<Object<'gc>>,
@ -207,7 +200,7 @@ impl<'gc> TDisplayObject<'gc> for Button<'gc> {
} }
} }
fn run_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) { fn run_frame(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) {
let self_display_object = (*self).into(); let self_display_object = (*self).into();
let initialized = self.0.read().initialized; let initialized = self.0.read().initialized;
@ -247,7 +240,7 @@ impl<'gc> TDisplayObject<'gc> for Button<'gc> {
drop(read); drop(read);
for (child, depth) in new_children { for (mut child, depth) in new_children {
child.post_instantiation(context, child, None, Instantiator::Movie, false); child.post_instantiation(context, child, None, Instantiator::Movie, false);
self.0 self.0
.write(context.gc_context) .write(context.gc_context)
@ -256,7 +249,7 @@ impl<'gc> TDisplayObject<'gc> for Button<'gc> {
} }
} }
for child in self.iter_execution_list() { for mut child in self.iter_execution_list() {
child.run_frame(context); child.run_frame(context);
} }
} }
@ -322,19 +315,19 @@ impl<'gc> TDisplayObject<'gc> for Button<'gc> {
} }
fn allow_as_mask(&self) -> bool { fn allow_as_mask(&self) -> bool {
!self.0.read().container.is_empty() !self.is_empty()
} }
/// Executes and propagates the given clip event. /// Executes and propagates the given clip event.
/// Events execute inside-out; the deepest child will react first, followed by its parent, and /// Events execute inside-out; the deepest child will react first, followed by its parent, and
/// so forth. /// so forth.
fn handle_clip_event( fn handle_clip_event(
&self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
event: ClipEvent, event: ClipEvent,
) -> ClipEventResult { ) -> ClipEventResult {
if event.propagates() { if event.propagates() {
for child in self.iter_execution_list() { for mut child in self.iter_execution_list() {
if child.handle_clip_event(context, event) == ClipEventResult::Handled { if child.handle_clip_event(context, event) == ClipEventResult::Handled {
return ClipEventResult::Handled; return ClipEventResult::Handled;
} }

View File

@ -51,6 +51,10 @@ pub trait TDisplayObjectContainer<'gc>:
/// Returns the number of children on the render list. /// Returns the number of children on the render list.
fn num_children(self) -> usize; 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>;
/// 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.
/// ///
@ -125,6 +129,9 @@ pub trait TDisplayObjectContainer<'gc>:
/// 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, '_>);
/// Determine if the container is empty.
fn is_empty(self) -> bool;
/// Iterates over the children of this display object in execution order. /// Iterates over the children of this display object in execution order.
/// This is different than render or depth order. /// This is different than render or depth order.
/// ///
@ -175,6 +182,10 @@ macro_rules! impl_display_object_container {
self.0.read().$field.num_children() self.0.read().$field.num_children()
} }
fn highest_depth(self) -> Option<Depth> {
self.0.read().$field.highest_depth()
}
fn replace_at_depth( fn replace_at_depth(
&mut self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
@ -258,6 +269,10 @@ macro_rules! impl_display_object_container {
fn clear(&mut self, gc_context: MutationContext<'gc, '_>) { fn clear(&mut self, gc_context: MutationContext<'gc, '_>) {
self.0.write(gc_context).$field.clear(gc_context) self.0.write(gc_context).$field.clear(gc_context)
} }
fn is_empty(self) -> bool {
self.0.read().$field.is_empty()
}
}; };
} }

View File

@ -1093,7 +1093,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
Some(self.0.read().static_data.swf.clone()) Some(self.0.read().static_data.swf.clone())
} }
fn run_frame(&self, _context: &mut UpdateContext) { fn run_frame(&mut self, _context: &mut UpdateContext) {
// Noop // Noop
} }
@ -1102,7 +1102,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
} }
fn post_instantiation( fn post_instantiation(
&self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
display_object: DisplayObject<'gc>, display_object: DisplayObject<'gc>,
_init_object: Option<Object<'gc>>, _init_object: Option<Object<'gc>>,
@ -1387,7 +1387,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
} }
fn handle_clip_event( fn handle_clip_event(
&self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
event: ClipEvent, event: ClipEvent,
) -> ClipEventResult { ) -> ClipEventResult {

View File

@ -55,7 +55,7 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
bounds bounds
} }
fn run_frame(&self, _context: &mut UpdateContext) { fn run_frame(&mut self, _context: &mut UpdateContext) {
// Noop // Noop
} }

View File

@ -52,7 +52,7 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
Some(*self) Some(*self)
} }
fn run_frame(&self, _context: &mut UpdateContext) { fn run_frame(&mut self, _context: &mut UpdateContext) {
// Noop // Noop
} }

View File

@ -921,17 +921,6 @@ impl<'gc> MovieClip<'gc> {
} }
} }
/// Returns the highest depth in use by this movie clip, or `None` if there
/// are no children.
pub fn highest_depth(self) -> Option<Depth> {
self.0.read().container.highest_depth()
}
/// Returns the number of children on the render list.
pub fn num_children(self) -> usize {
self.0.read().container.num_children()
}
/// Gets the clip events for this movieclip. /// Gets the clip events for this movieclip.
pub fn clip_actions(&self) -> Ref<[ClipAction]> { pub fn clip_actions(&self) -> Ref<[ClipAction]> {
Ref::map(self.0.read(), |mc| mc.clip_actions()) Ref::map(self.0.read(), |mc| mc.clip_actions())
@ -1115,7 +1104,7 @@ impl<'gc> MovieClip<'gc> {
/// Instantiate a given child object on the timeline at a given depth. /// Instantiate a given child object on the timeline at a given depth.
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
fn instantiate_child( fn instantiate_child(
self, mut self,
self_display_object: DisplayObject<'gc>, self_display_object: DisplayObject<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
id: CharacterId, id: CharacterId,
@ -1123,18 +1112,14 @@ impl<'gc> MovieClip<'gc> {
place_object: &swf::PlaceObject, place_object: &swf::PlaceObject,
copy_previous_properties: bool, copy_previous_properties: bool,
) -> Option<DisplayObject<'gc>> { ) -> Option<DisplayObject<'gc>> {
if let Ok(child) = context if let Ok(mut child) = context
.library .library
.library_for_movie_mut(self.movie().unwrap()) //TODO .library_for_movie_mut(self.movie().unwrap()) //TODO
.instantiate_by_id(id, context.gc_context) .instantiate_by_id(id, context.gc_context)
{ {
// Remove previous child from children list, // Remove previous child from children list,
// and add new child onto front of the list. // and add new child onto front of the list.
let prev_child = { let prev_child = self.replace_at_depth(context, child, depth);
let mut mc = self.0.write(context.gc_context);
mc.container
.replace_at_depth(context, self.into(), child, depth)
};
{ {
// Set initial properties for child. // Set initial properties for child.
child.set_depth(context.gc_context, depth); child.set_depth(context.gc_context, depth);
@ -1176,7 +1161,7 @@ impl<'gc> MovieClip<'gc> {
} }
pub fn run_goto( pub fn run_goto(
self, mut self,
self_display_object: DisplayObject<'gc>, self_display_object: DisplayObject<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
frame: FrameNumber, frame: FrameNumber,
@ -1226,8 +1211,7 @@ impl<'gc> MovieClip<'gc> {
.collect(); .collect();
for (_depth, child) in children { for (_depth, child) in children {
if !child.placed_by_script() { if !child.placed_by_script() {
let mut mc = self.0.write(context.gc_context); self.remove_child(context, child);
mc.container.remove_child(context, child);
} }
} }
true true
@ -1333,7 +1317,7 @@ impl<'gc> MovieClip<'gc> {
let run_goto_command = |clip: MovieClip<'gc>, let run_goto_command = |clip: MovieClip<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
params: &GotoPlaceObject| { params: &GotoPlaceObject| {
let child_entry = clip.0.read().container.get_depth(params.depth()); let child_entry = clip.child_by_depth(params.depth());
match child_entry { match child_entry {
// Apply final delta to display parameters. // Apply final delta to display parameters.
// For rewinds, if an object was created before the final frame, // For rewinds, if an object was created before the final frame,
@ -1395,7 +1379,7 @@ impl<'gc> MovieClip<'gc> {
} }
fn construct_as_avm1_object( fn construct_as_avm1_object(
self, mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
display_object: DisplayObject<'gc>, display_object: DisplayObject<'gc>,
init_object: Option<Avm1Object<'gc>>, init_object: Option<Avm1Object<'gc>>,
@ -1668,9 +1652,9 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
self.0.read().movie().version() self.0.read().movie().version()
} }
fn run_frame(&self, context: &mut UpdateContext<'_, 'gc, '_>) { fn run_frame(&mut self, context: &mut UpdateContext<'_, 'gc, '_>) {
// Children must run first. // Children must run first.
for child in self.iter_execution_list() { for mut child in self.iter_execution_list() {
child.run_frame(context); child.run_frame(context);
} }
@ -1781,12 +1765,12 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
} }
fn handle_clip_event( fn handle_clip_event(
&self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
event: ClipEvent, event: ClipEvent,
) -> ClipEventResult { ) -> ClipEventResult {
if event.propagates() { if event.propagates() {
for child in self.iter_execution_list() { for mut child in self.iter_execution_list() {
if child.handle_clip_event(context, event) == ClipEventResult::Handled { if child.handle_clip_event(context, event) == ClipEventResult::Handled {
return ClipEventResult::Handled; return ClipEventResult::Handled;
} }
@ -1805,7 +1789,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
} }
fn post_instantiation( fn post_instantiation(
&self, &mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
display_object: DisplayObject<'gc>, display_object: DisplayObject<'gc>,
init_object: Option<Avm1Object<'gc>>, init_object: Option<Avm1Object<'gc>>,
@ -1880,7 +1864,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
} }
fn allow_as_mask(&self) -> bool { fn allow_as_mask(&self) -> bool {
!self.0.read().container.is_empty() !self.is_empty()
} }
fn is_focusable(&self) -> bool { fn is_focusable(&self) -> bool {
@ -2829,7 +2813,7 @@ impl<'gc, 'a> MovieClip<'gc> {
} }
} }
PlaceObjectAction::Modify => { PlaceObjectAction::Modify => {
if let Some(child) = self.0.read().container.get_depth(place_object.depth.into()) { if let Some(child) = self.child_by_depth(place_object.depth.into()) {
child.apply_place_object(context.gc_context, &place_object); child.apply_place_object(context.gc_context, &place_object);
child child
} else { } else {
@ -2843,7 +2827,7 @@ impl<'gc, 'a> MovieClip<'gc> {
#[inline] #[inline]
fn remove_object( fn remove_object(
self, mut self,
context: &mut UpdateContext<'_, 'gc, '_>, context: &mut UpdateContext<'_, 'gc, '_>,
reader: &mut SwfStream<&'a [u8]>, reader: &mut SwfStream<&'a [u8]>,
version: u8, version: u8,
@ -2853,12 +2837,10 @@ impl<'gc, 'a> MovieClip<'gc> {
} else { } else {
reader.read_remove_object_2() reader.read_remove_object_2()
}?; }?;
let read = self.0.read(); if let Some(child) = self.child_by_depth(remove_object.depth.into()) {
if let Some(child) = read.container.get_depth(remove_object.depth.into()) {
if !child.placed_by_script() { if !child.placed_by_script() {
drop(read); self.remove_child(context, child);
self.0.write(context.gc_context).container.remove_child(context, child);
} }
} }

View File

@ -53,7 +53,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
Some(self.0.read().static_data.swf.clone()) Some(self.0.read().static_data.swf.clone())
} }
fn run_frame(&self, _context: &mut UpdateContext) { fn run_frame(&mut self, _context: &mut UpdateContext) {
// Noop // Noop
} }

View File

@ -290,7 +290,7 @@ impl Player {
player.mutate_with_update_context(|context| { player.mutate_with_update_context(|context| {
// Instantiate an empty root before the main movie loads. // Instantiate an empty root before the main movie loads.
let fake_root = MovieClip::from_movie(context.gc_context, fake_movie); let mut fake_root = MovieClip::from_movie(context.gc_context, fake_movie);
fake_root.post_instantiation( fake_root.post_instantiation(
context, context,
fake_root.into(), fake_root.into(),
@ -358,7 +358,7 @@ impl Player {
.library_for_movie_mut(context.swf.clone()) .library_for_movie_mut(context.swf.clone())
.set_avm2_domain(domain); .set_avm2_domain(domain);
let root: DisplayObject = let mut root: DisplayObject =
MovieClip::from_movie(context.gc_context, context.swf.clone()).into(); MovieClip::from_movie(context.gc_context, context.swf.clone()).into();
root.set_depth(context.gc_context, 0); root.set_depth(context.gc_context, 0);
@ -608,7 +608,7 @@ impl Player {
if button_event.is_some() { if button_event.is_some() {
self.mutate_with_update_context(|context| { self.mutate_with_update_context(|context| {
let levels: Vec<DisplayObject<'_>> = context.levels.values().copied().collect(); let levels: Vec<DisplayObject<'_>> = context.levels.values().copied().collect();
for level in levels { for mut level in levels {
if let Some(button_event) = button_event { if let Some(button_event) = button_event {
let state = level.handle_clip_event(context, button_event); let state = level.handle_clip_event(context, button_event);
if state == ClipEventResult::Handled { if state == ClipEventResult::Handled {
@ -658,7 +658,7 @@ impl Player {
// Fire clip event on all clips. // Fire clip event on all clips.
if let Some(clip_event) = clip_event { if let Some(clip_event) = clip_event {
let levels: Vec<DisplayObject<'_>> = context.levels.values().copied().collect(); let levels: Vec<DisplayObject<'_>> = context.levels.values().copied().collect();
for level in levels { for mut level in levels {
level.handle_clip_event(context, clip_event); level.handle_clip_event(context, clip_event);
} }
} }
@ -689,7 +689,7 @@ impl Player {
PlayerEvent::MouseDown { .. } => { PlayerEvent::MouseDown { .. } => {
is_mouse_down = true; is_mouse_down = true;
needs_render = true; needs_render = true;
if let Some(node) = context.mouse_hovered_object { if let Some(mut node) = context.mouse_hovered_object {
node.handle_clip_event(context, ClipEvent::Press); node.handle_clip_event(context, ClipEvent::Press);
} }
} }
@ -697,7 +697,7 @@ impl Player {
PlayerEvent::MouseUp { .. } => { PlayerEvent::MouseUp { .. } => {
is_mouse_down = false; is_mouse_down = false;
needs_render = true; needs_render = true;
if let Some(node) = context.mouse_hovered_object { if let Some(mut node) = context.mouse_hovered_object {
node.handle_clip_event(context, ClipEvent::Release); node.handle_clip_event(context, ClipEvent::Release);
} }
} }
@ -766,7 +766,7 @@ impl Player {
if cur_hovered.map(|d| d.as_ptr()) != new_hovered.map(|d| d.as_ptr()) { if cur_hovered.map(|d| d.as_ptr()) != new_hovered.map(|d| d.as_ptr()) {
// RollOut of previous node. // RollOut of previous node.
if let Some(node) = cur_hovered { if let Some(mut node) = cur_hovered {
if !node.removed() { if !node.removed() {
node.handle_clip_event(context, ClipEvent::RollOut); node.handle_clip_event(context, ClipEvent::RollOut);
} }
@ -774,7 +774,7 @@ impl Player {
// RollOver on new node.I still // RollOver on new node.I still
new_cursor = MouseCursor::Arrow; new_cursor = MouseCursor::Arrow;
if let Some(node) = new_hovered { if let Some(mut node) = new_hovered {
new_cursor = node.mouse_cursor(); new_cursor = node.mouse_cursor();
node.handle_clip_event(context, ClipEvent::RollOver); node.handle_clip_event(context, ClipEvent::RollOver);
} }
@ -828,7 +828,7 @@ impl Player {
// want to run frames on // want to run frames on
let levels: Vec<_> = update_context.levels.values().copied().collect(); let levels: Vec<_> = update_context.levels.values().copied().collect();
for level in levels { for mut level in levels {
level.run_frame(update_context); level.run_frame(update_context);
} }
}); });