core: Implement the `doubleClick` event on objects that request it.

This commit is contained in:
David Wendt 2021-12-10 23:39:48 -05:00 committed by Mike Welsh
parent eeea5ffea6
commit 880211c238
3 changed files with 55 additions and 9 deletions

View File

@ -762,9 +762,10 @@ impl<'gc> TInteractiveObject<'gc> for Avm2Button<'gc> {
};
write.play_sound(context, sound);
if write.state != new_state {
let old_state = write.state;
drop(write);
if old_state != new_state {
self.set_state(context, new_state);
}

View File

@ -14,9 +14,11 @@ use crate::display_object::{
use crate::events::{ClipEvent, ClipEventResult};
use bitflags::bitflags;
use gc_arena::{Collect, MutationContext};
use instant::Instant;
use ruffle_macros::enum_trait_object;
use std::cell::{Ref, RefMut};
use std::fmt::Debug;
use std::time::Duration;
use swf::Twips;
/// Find the lowest common ancestor between the display objects in `from` and
@ -77,6 +79,13 @@ pub struct InteractiveObjectBase<'gc> {
pub base: DisplayObjectBase<'gc>,
flags: InteractiveObjectFlags,
context_menu: Avm2Value<'gc>,
/// The time of the last click registered on this object.
///
/// This should be cleared to `None` when the mouse leaves the current
/// display object.
#[collect(require_static)]
last_click: Option<Instant>,
}
impl<'gc> Default for InteractiveObjectBase<'gc> {
@ -85,6 +94,7 @@ impl<'gc> Default for InteractiveObjectBase<'gc> {
base: Default::default(),
flags: InteractiveObjectFlags::MOUSE_ENABLED,
context_menu: Avm2Value::Null,
last_click: None,
}
}
}
@ -237,6 +247,33 @@ pub trait TInteractiveObject<'gc>:
ClipEventResult::Handled
}
ClipEvent::Release => {
let read = self.ibase();
let last_click = read.last_click;
let this_click = Instant::now();
let is_double_click = read
.flags
.contains(InteractiveObjectFlags::DOUBLE_CLICK_ENABLED)
&& last_click
.map(|lc| this_click - lc < Duration::from_secs(1))
.unwrap_or(false);
drop(read);
if is_double_click {
let mut avm2_event = Avm2Event::new(
"doubleClick",
Avm2EventData::mouse_event(context, self.as_displayobject(), None, 0),
);
avm2_event.set_bubbles(true);
if let Err(e) = Avm2::dispatch_event(context, avm2_event, target) {
log::error!("Got error when dispatching {:?} to AVM2: {}", event, e);
}
self.ibase_mut(context.gc_context).last_click = None;
} else {
let mut avm2_event = Avm2Event::new(
"click",
Avm2EventData::mouse_event(context, self.as_displayobject(), None, 0),
@ -248,6 +285,9 @@ pub trait TInteractiveObject<'gc>:
log::error!("Got error when dispatching {:?} to AVM2: {}", event, e);
}
self.ibase_mut(context.gc_context).last_click = Some(this_click);
}
ClipEventResult::Handled
}
ClipEvent::ReleaseOutside => {
@ -262,6 +302,8 @@ pub trait TInteractiveObject<'gc>:
log::error!("Got error when dispatching {:?} to AVM2: {}", event, e);
}
self.ibase_mut(context.gc_context).last_click = None;
ClipEventResult::Handled
}
ClipEvent::RollOut { to } | ClipEvent::DragOut { to } => {
@ -300,6 +342,8 @@ pub trait TInteractiveObject<'gc>:
rollover_target = tgt.parent();
}
self.ibase_mut(context.gc_context).last_click = None;
ClipEventResult::Handled
}
ClipEvent::RollOver { from } | ClipEvent::DragOver { from } => {

View File

@ -2130,6 +2130,7 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> {
}
}
} else {
drop(read);
handled = self.event_dispatch_to_avm2(context, event);
}