core: Flatten ClipActions into a single event per action

Previously a MovieClip's clip action would have a set of events
that would trigger it. Now we flatten these out into a single
event per action, because this is by far the common case. If an
action does happen to have >1 event, it will be duplicated for each.
This commit is contained in:
Mike Welsh 2020-05-23 17:47:43 -07:00
parent c427d55dbb
commit 932ad30570
2 changed files with 46 additions and 51 deletions

View File

@ -781,6 +781,7 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
.iter()
.cloned()
.map(|a| ClipAction::from_action_and_movie(a, clip.movie().unwrap()))
.flatten()
.collect(),
);
}

View File

@ -43,7 +43,7 @@ pub struct MovieClipData<'gc> {
audio_stream: Option<AudioStreamHandle>,
children: BTreeMap<Depth, DisplayObject<'gc>>,
object: Option<Object<'gc>>,
clip_actions: SmallVec<[ClipAction; 2]>,
clip_actions: Vec<ClipAction>,
has_button_clip_event: bool,
flags: EnumSet<MovieClipFlags>,
avm1_constructor: Option<Object<'gc>>,
@ -63,7 +63,7 @@ impl<'gc> MovieClip<'gc> {
audio_stream: None,
children: BTreeMap::new(),
object: None,
clip_actions: SmallVec::new(),
clip_actions: Vec::new(),
has_button_clip_event: false,
flags: EnumSet::empty(),
avm1_constructor: None,
@ -97,7 +97,7 @@ impl<'gc> MovieClip<'gc> {
audio_stream: None,
children: BTreeMap::new(),
object: None,
clip_actions: SmallVec::new(),
clip_actions: Vec::new(),
has_button_clip_event: false,
flags: MovieClipFlags::Playing.into(),
avm1_constructor: None,
@ -457,15 +457,9 @@ impl<'gc> MovieClip<'gc> {
/// Sets the clip actions (a.k.a. clip events) for this movieclip.
/// Clip actions are created in the Flash IDE by using the `onEnterFrame`
/// tag on a movieclip instance.
pub fn set_clip_actions(
self,
gc_context: MutationContext<'gc, '_>,
actions: SmallVec<[ClipAction; 2]>,
) {
pub fn set_clip_actions(self, gc_context: MutationContext<'gc, '_>, actions: Vec<ClipAction>) {
let mut mc = self.0.write(gc_context);
mc.has_button_clip_event = actions
.iter()
.any(|a| a.events.iter().copied().any(ClipEvent::is_button_event));
mc.has_button_clip_event = actions.iter().any(|a| a.event.is_button_event());
mc.set_clip_actions(actions);
}
@ -778,7 +772,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
for clip_action in mc
.clip_actions()
.iter()
.filter(|action| action.events.contains(&ClipEvent::Construct))
.filter(|action| action.event == ClipEvent::Construct)
{
events.push(clip_action.action_data.clone());
}
@ -1333,7 +1327,7 @@ impl<'gc> MovieClipData<'gc> {
for clip_action in self
.clip_actions
.iter()
.filter(|action| action.events.contains(&event))
.filter(|action| action.event == event)
{
// KeyPress events are consumed by a single instance.
if matches!(clip_action.event, ClipEvent::KeyPress { .. }) {
@ -1397,7 +1391,7 @@ impl<'gc> MovieClipData<'gc> {
&self.clip_actions
}
pub fn set_clip_actions(&mut self, actions: SmallVec<[ClipAction; 2]>) {
pub fn set_clip_actions(&mut self, actions: Vec<ClipAction>) {
self.clip_actions = actions;
}
@ -2386,29 +2380,31 @@ enum MovieClipFlags {
/// an `onClipEvent`/`on` handler.
#[derive(Debug, Clone)]
pub struct ClipAction {
/// The events that trigger this handler.
events: SmallVec<[ClipEvent; 1]>,
/// The event that triggers this handler.
event: ClipEvent,
/// The actions to run.
action_data: SwfSlice,
}
impl ClipAction {
/// Build a clip action from a SWF movie and a parsed ClipAction.
/// Build a set of clip actions from a SWF movie and a parsed ClipAction.
///
/// TODO: Our underlying SWF parser currently does not yield slices of the
/// underlying movie, so we cannot convert those slices into a `SwfSlice`.
/// Instead, we have to construct a fake `SwfMovie` just to hold one clip
/// action.
pub fn from_action_and_movie(other: swf::ClipAction, movie: Arc<SwfMovie>) -> Self {
pub fn from_action_and_movie(
other: swf::ClipAction,
movie: Arc<SwfMovie>,
) -> impl Iterator<Item = Self> {
use swf::ClipEventFlag;
let len = other.action_data.len();
Self {
events: other
.events
.into_iter()
.map(|event| match event {
let key_code = other.key_code;
let movie = Arc::new(movie.from_movie_and_subdata(other.action_data));
other.events.into_iter().map(move |event| Self {
event: match event {
ClipEventFlag::Construct => ClipEvent::Construct,
ClipEventFlag::Data => ClipEvent::Data,
ClipEventFlag::DragOut => ClipEvent::DragOut,
@ -2418,8 +2414,7 @@ impl ClipAction {
ClipEventFlag::KeyUp => ClipEvent::KeyUp,
ClipEventFlag::KeyDown => ClipEvent::KeyDown,
ClipEventFlag::KeyPress => ClipEvent::KeyPress {
key_code: other
.key_code
key_code: key_code
.and_then(|k| ButtonKeyCode::try_from(k).ok())
.unwrap_or(ButtonKeyCode::Unknown),
},
@ -2433,13 +2428,12 @@ impl ClipAction {
ClipEventFlag::Release => ClipEvent::Release,
ClipEventFlag::ReleaseOutside => ClipEvent::ReleaseOutside,
ClipEventFlag::Unload => ClipEvent::Unload,
})
.collect(),
},
action_data: SwfSlice {
movie: Arc::new(movie.from_movie_and_subdata(other.action_data)),
movie: Arc::clone(&movie),
start: 0,
end: len,
},
}
})
}
}