core: Move `mouse_pick` and `mouse_cursor` to `InteractiveObject` as no non-interactive object implements them.
This also cascades into other places, ultimately resulting in more things being marked as `InteractiveObject`.
This commit is contained in:
parent
d0ef15503c
commit
353a5a78d6
|
@ -16,7 +16,7 @@ use crate::backend::{
|
|||
video::VideoBackend,
|
||||
};
|
||||
use crate::context_menu::ContextMenuState;
|
||||
use crate::display_object::{EditText, MovieClip, SoundTransform, Stage};
|
||||
use crate::display_object::{EditText, InteractiveObject, MovieClip, SoundTransform, Stage};
|
||||
use crate::external::ExternalInterface;
|
||||
use crate::focus_tracker::FocusTracker;
|
||||
use crate::library::Library;
|
||||
|
@ -98,10 +98,10 @@ pub struct UpdateContext<'a, 'gc, 'gc_context> {
|
|||
pub stage: Stage<'gc>,
|
||||
|
||||
/// The display object that the mouse is currently hovering over.
|
||||
pub mouse_over_object: Option<DisplayObject<'gc>>,
|
||||
pub mouse_over_object: Option<InteractiveObject<'gc>>,
|
||||
|
||||
/// If the mouse is down, the display object that the mouse is currently pressing.
|
||||
pub mouse_down_object: Option<DisplayObject<'gc>>,
|
||||
pub mouse_down_object: Option<InteractiveObject<'gc>>,
|
||||
|
||||
/// The input manager, tracking keys state.
|
||||
pub input: &'a InputManager,
|
||||
|
|
|
@ -37,7 +37,6 @@ mod text;
|
|||
mod video;
|
||||
|
||||
use crate::avm1::activation::Activation;
|
||||
use crate::backend::ui::MouseCursor;
|
||||
pub use crate::display_object::container::{
|
||||
DisplayObjectContainer, Lists, TDisplayObjectContainer,
|
||||
};
|
||||
|
@ -1279,16 +1278,6 @@ pub trait TDisplayObject<'gc>:
|
|||
self.hit_test_bounds(pos)
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
pos: (Twips, Twips),
|
||||
require_button_mode: bool,
|
||||
) -> Option<DisplayObject<'gc>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn post_instantiation(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
|
@ -1323,11 +1312,6 @@ pub trait TDisplayObject<'gc>:
|
|||
true
|
||||
}
|
||||
|
||||
/// The cursor to use when this object is the hovered element under a mouse.
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
MouseCursor::Hand
|
||||
}
|
||||
|
||||
/// Obtain the top-most non-Stage parent of the display tree hierarchy, if
|
||||
/// a suitable object exists. If none such object exists, this function
|
||||
/// yields an AVM1 error (which shouldn't happen in normal usage).
|
||||
|
|
|
@ -358,38 +358,6 @@ impl<'gc> TDisplayObject<'gc> for Avm1Button<'gc> {
|
|||
false
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
require_button_mode: bool,
|
||||
) -> Option<DisplayObject<'gc>> {
|
||||
// The button is hovered if the mouse is over any child nodes.
|
||||
if self.visible() {
|
||||
for child in self.iter_render_list().rev() {
|
||||
let result = child.mouse_pick(context, point, require_button_mode);
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for child in self.0.read().hit_area.values() {
|
||||
if child.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK) {
|
||||
return Some((*self).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
if self.use_hand_cursor() {
|
||||
MouseCursor::Hand
|
||||
} else {
|
||||
MouseCursor::Arrow
|
||||
}
|
||||
}
|
||||
|
||||
fn object(&self) -> Value<'gc> {
|
||||
self.0
|
||||
.read()
|
||||
|
@ -549,6 +517,40 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> {
|
|||
|
||||
ClipEventResult::NotHandled
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
require_button_mode: bool,
|
||||
) -> Option<InteractiveObject<'gc>> {
|
||||
// The button is hovered if the mouse is over any child nodes.
|
||||
if self.visible() {
|
||||
for child in self.iter_render_list().rev() {
|
||||
let result = child
|
||||
.as_interactive()
|
||||
.and_then(|c| c.mouse_pick(context, point, require_button_mode));
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for child in self.0.read().hit_area.values() {
|
||||
if child.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK) {
|
||||
return Some((*self).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
if self.use_hand_cursor() {
|
||||
MouseCursor::Hand
|
||||
} else {
|
||||
MouseCursor::Arrow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Avm1ButtonData<'gc> {
|
||||
|
|
|
@ -640,44 +640,6 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
|
|||
false
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
require_button_mode: bool,
|
||||
) -> Option<DisplayObject<'gc>> {
|
||||
// The button is hovered if the mouse is over any child nodes.
|
||||
if self.visible() {
|
||||
let state = self.0.read().state;
|
||||
let state_child = self.get_state_child(state.into());
|
||||
|
||||
if let Some(state_child) = state_child {
|
||||
let mouse_pick = state_child.mouse_pick(context, point, require_button_mode);
|
||||
if mouse_pick.is_some() {
|
||||
return mouse_pick;
|
||||
}
|
||||
}
|
||||
|
||||
let hit_area = self.0.read().hit_area;
|
||||
if let Some(hit_area) = hit_area {
|
||||
// hit_area is not actually a child, so transform point into local space before passing it down.
|
||||
let point = self.global_to_local(point);
|
||||
if hit_area.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK) {
|
||||
return Some((*self).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
if self.use_hand_cursor() {
|
||||
MouseCursor::Hand
|
||||
} else {
|
||||
MouseCursor::Arrow
|
||||
}
|
||||
}
|
||||
|
||||
fn object2(&self) -> Avm2Value<'gc> {
|
||||
self.0
|
||||
.read()
|
||||
|
@ -808,6 +770,46 @@ impl<'gc> TInteractiveObject<'gc> for Avm2Button<'gc> {
|
|||
|
||||
self.event_dispatch_to_avm2(context, event)
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
require_button_mode: bool,
|
||||
) -> Option<InteractiveObject<'gc>> {
|
||||
// The button is hovered if the mouse is over any child nodes.
|
||||
if self.visible() {
|
||||
let state = self.0.read().state;
|
||||
let state_child = self.get_state_child(state.into());
|
||||
|
||||
if let Some(state_child) = state_child {
|
||||
let mouse_pick = state_child
|
||||
.as_interactive()
|
||||
.and_then(|c| c.mouse_pick(context, point, require_button_mode));
|
||||
if mouse_pick.is_some() {
|
||||
return mouse_pick;
|
||||
}
|
||||
}
|
||||
|
||||
let hit_area = self.0.read().hit_area;
|
||||
if let Some(hit_area) = hit_area {
|
||||
// hit_area is not actually a child, so transform point into local space before passing it down.
|
||||
let point = self.global_to_local(point);
|
||||
if hit_area.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK) {
|
||||
return Some((*self).into());
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
if self.use_hand_cursor() {
|
||||
MouseCursor::Hand
|
||||
} else {
|
||||
MouseCursor::Arrow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> Avm2ButtonData<'gc> {
|
||||
|
|
|
@ -1762,27 +1762,6 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
self.set_removed(context.gc_context, true);
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
_require_button_mode: bool,
|
||||
) -> Option<DisplayObject<'gc>> {
|
||||
// The button is hovered if the mouse is over any child nodes.
|
||||
if self.visible()
|
||||
&& self.is_selectable()
|
||||
&& self.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK)
|
||||
{
|
||||
Some((*self).into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
MouseCursor::IBeam
|
||||
}
|
||||
|
||||
fn on_focus_changed(&self, gc_context: MutationContext<'gc, '_>, focused: bool) {
|
||||
let mut text = self.0.write(gc_context);
|
||||
text.has_focus = focused;
|
||||
|
@ -1839,6 +1818,27 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> {
|
|||
|
||||
ClipEventResult::Handled
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
_require_button_mode: bool,
|
||||
) -> Option<InteractiveObject<'gc>> {
|
||||
// The button is hovered if the mouse is over any child nodes.
|
||||
if self.visible()
|
||||
&& self.is_selectable()
|
||||
&& self.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK)
|
||||
{
|
||||
Some((*self).into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
MouseCursor::IBeam
|
||||
}
|
||||
}
|
||||
|
||||
/// Static data shared between all instances of a text object.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! Interactive object enumtrait
|
||||
|
||||
use crate::avm2::{Avm2, Event as Avm2Event, EventData as Avm2EventData, Value as Avm2Value};
|
||||
use crate::backend::ui::MouseCursor;
|
||||
use crate::context::UpdateContext;
|
||||
use crate::display_object::avm1_button::Avm1Button;
|
||||
use crate::display_object::avm2_button::Avm2Button;
|
||||
|
@ -16,6 +17,7 @@ use gc_arena::{Collect, MutationContext};
|
|||
use ruffle_macros::enum_trait_object;
|
||||
use std::cell::{Ref, RefMut};
|
||||
use std::fmt::Debug;
|
||||
use swf::Twips;
|
||||
|
||||
bitflags! {
|
||||
/// Boolean state flags used by `InteractiveObject`.
|
||||
|
@ -250,12 +252,40 @@ pub trait TInteractiveObject<'gc>:
|
|||
|
||||
self.event_dispatch(context, event)
|
||||
}
|
||||
|
||||
/// Determine the bottom-most interactive display object under the given
|
||||
/// mouse cursor.
|
||||
///
|
||||
/// Only objects capable of handling mouse input should flag themselves as
|
||||
/// mouse-pickable, as doing so will make them eligible to recieve targeted
|
||||
/// mouse events. As a result of this, the returned object will always be
|
||||
/// an `InteractiveObject`.
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_pos: (Twips, Twips),
|
||||
_require_button_mode: bool,
|
||||
) -> Option<InteractiveObject<'gc>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// The cursor to use when this object is the hovered element under a mouse.
|
||||
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
MouseCursor::Hand
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> InteractiveObject<'gc> {
|
||||
pub fn ptr_eq<T: TInteractiveObject<'gc>>(a: T, b: T) -> bool {
|
||||
a.as_displayobject().as_ptr() == b.as_displayobject().as_ptr()
|
||||
}
|
||||
|
||||
pub fn option_ptr_eq(
|
||||
a: Option<InteractiveObject<'gc>>,
|
||||
b: Option<InteractiveObject<'gc>>,
|
||||
) -> bool {
|
||||
a.map(|o| o.as_displayobject().as_ptr()) == b.map(|o| o.as_displayobject().as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> PartialEq for InteractiveObject<'gc> {
|
||||
|
|
|
@ -1924,84 +1924,6 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
false
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
require_button_mode: bool,
|
||||
) -> Option<DisplayObject<'gc>> {
|
||||
if self.visible() {
|
||||
let this: DisplayObject<'gc> = (*self).into();
|
||||
|
||||
if let Some(masker) = self.masker() {
|
||||
if !masker.hit_test_shape(context, point, HitTestOptions::SKIP_INVISIBLE) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if self.world_bounds().contains(point) {
|
||||
// This MovieClip operates in "button mode" if it has a mouse handler,
|
||||
// either via on(..) or via property mc.onRelease, etc.
|
||||
let is_button_mode = self.is_button_mode(context);
|
||||
|
||||
if is_button_mode {
|
||||
let mut options = HitTestOptions::SKIP_INVISIBLE;
|
||||
options.set(HitTestOptions::SKIP_MASK, self.maskee().is_none());
|
||||
if self.hit_test_shape(context, point, options) {
|
||||
return Some(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe we could skip recursing down at all if !world_bounds.contains(point),
|
||||
// but a child button can have an invisible hit area outside the parent's bounds.
|
||||
let mut hit_depth = 0;
|
||||
let mut result = None;
|
||||
|
||||
for child in self.iter_render_list().rev() {
|
||||
if child.clip_depth() > 0 {
|
||||
if result.is_some() && child.clip_depth() >= hit_depth {
|
||||
if child.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK) {
|
||||
return result;
|
||||
} else {
|
||||
result = None;
|
||||
}
|
||||
}
|
||||
} else if result.is_none() {
|
||||
result = child.mouse_pick(context, point, require_button_mode);
|
||||
|
||||
if result.is_some() {
|
||||
hit_depth = child.depth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// AVM2 allows movie clips to recieve mouse events without
|
||||
// explicitly enabling button mode.
|
||||
if !require_button_mode || matches!(self.object2(), Avm2Value::Object(_)) {
|
||||
let mut options = HitTestOptions::SKIP_INVISIBLE;
|
||||
options.set(HitTestOptions::SKIP_MASK, self.maskee().is_none());
|
||||
if self.hit_test_shape(context, point, options) {
|
||||
return Some(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
if self.use_hand_cursor() && self.is_button_mode(context) {
|
||||
MouseCursor::Hand
|
||||
} else {
|
||||
MouseCursor::Arrow
|
||||
}
|
||||
}
|
||||
|
||||
fn as_movie_clip(&self) -> Option<MovieClip<'gc>> {
|
||||
Some(*self)
|
||||
}
|
||||
|
@ -2210,6 +2132,86 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> {
|
|||
|
||||
handled
|
||||
}
|
||||
|
||||
fn mouse_pick(
|
||||
&self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
point: (Twips, Twips),
|
||||
require_button_mode: bool,
|
||||
) -> Option<InteractiveObject<'gc>> {
|
||||
if self.visible() {
|
||||
let this: InteractiveObject<'gc> = (*self).into();
|
||||
|
||||
if let Some(masker) = self.masker() {
|
||||
if !masker.hit_test_shape(context, point, HitTestOptions::SKIP_INVISIBLE) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
if self.world_bounds().contains(point) {
|
||||
// This MovieClip operates in "button mode" if it has a mouse handler,
|
||||
// either via on(..) or via property mc.onRelease, etc.
|
||||
let is_button_mode = self.is_button_mode(context);
|
||||
|
||||
if is_button_mode {
|
||||
let mut options = HitTestOptions::SKIP_INVISIBLE;
|
||||
options.set(HitTestOptions::SKIP_MASK, self.maskee().is_none());
|
||||
if self.hit_test_shape(context, point, options) {
|
||||
return Some(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Maybe we could skip recursing down at all if !world_bounds.contains(point),
|
||||
// but a child button can have an invisible hit area outside the parent's bounds.
|
||||
let mut hit_depth = 0;
|
||||
let mut result = None;
|
||||
|
||||
for child in self.iter_render_list().rev() {
|
||||
if child.clip_depth() > 0 {
|
||||
if result.is_some() && child.clip_depth() >= hit_depth {
|
||||
if child.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK) {
|
||||
return result;
|
||||
} else {
|
||||
result = None;
|
||||
}
|
||||
}
|
||||
} else if result.is_none() {
|
||||
result = child
|
||||
.as_interactive()
|
||||
.and_then(|c| c.mouse_pick(context, point, require_button_mode));
|
||||
|
||||
if result.is_some() {
|
||||
hit_depth = child.depth();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if result.is_some() {
|
||||
return result;
|
||||
}
|
||||
|
||||
// AVM2 allows movie clips to recieve mouse events without
|
||||
// explicitly enabling button mode.
|
||||
if !require_button_mode || matches!(self.object2(), Avm2Value::Object(_)) {
|
||||
let mut options = HitTestOptions::SKIP_INVISIBLE;
|
||||
options.set(HitTestOptions::SKIP_MASK, self.maskee().is_none());
|
||||
if self.hit_test_shape(context, point, options) {
|
||||
return Some(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn mouse_cursor(self, context: &mut UpdateContext<'_, 'gc, '_>) -> MouseCursor {
|
||||
if self.use_hand_cursor() && self.is_button_mode(context) {
|
||||
MouseCursor::Hand
|
||||
} else {
|
||||
MouseCursor::Arrow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> MovieClipData<'gc> {
|
||||
|
|
|
@ -19,8 +19,8 @@ use crate::config::Letterbox;
|
|||
use crate::context::{ActionQueue, ActionType, RenderContext, UpdateContext};
|
||||
use crate::context_menu::{ContextMenuCallback, ContextMenuItem, ContextMenuState};
|
||||
use crate::display_object::{
|
||||
EditText, MorphShape, MovieClip, Stage, StageAlign, StageDisplayState, StageQuality,
|
||||
StageScaleMode, TInteractiveObject,
|
||||
EditText, InteractiveObject, MorphShape, MovieClip, Stage, StageAlign, StageDisplayState,
|
||||
StageQuality, StageScaleMode, TInteractiveObject,
|
||||
};
|
||||
use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult, KeyCode, MouseButton, PlayerEvent};
|
||||
use crate::external::Value as ExternalValue;
|
||||
|
@ -65,10 +65,10 @@ struct GcRootData<'gc> {
|
|||
stage: Stage<'gc>,
|
||||
|
||||
/// The display object that the mouse is currently hovering over.
|
||||
mouse_hovered_object: Option<DisplayObject<'gc>>,
|
||||
mouse_hovered_object: Option<InteractiveObject<'gc>>,
|
||||
|
||||
/// If the mouse is down, the display object that the mouse is currently pressing.
|
||||
mouse_pressed_object: Option<DisplayObject<'gc>>,
|
||||
mouse_pressed_object: Option<InteractiveObject<'gc>>,
|
||||
|
||||
/// The object being dragged via a `startDrag` action.
|
||||
drag_object: Option<DragObject<'gc>>,
|
||||
|
@ -1087,9 +1087,14 @@ impl Player {
|
|||
.iter_depth_list()
|
||||
.rev()
|
||||
.find_map(|(_depth, level)| {
|
||||
level.mouse_pick(context, *context.mouse_position, false)
|
||||
level.as_interactive().and_then(|l| {
|
||||
l.mouse_pick(context, *context.mouse_position, false)
|
||||
})
|
||||
});
|
||||
movie_clip.set_drop_target(context.gc_context, drop_target_object);
|
||||
movie_clip.set_drop_target(
|
||||
context.gc_context,
|
||||
drop_target_object.map(|d| d.as_displayobject()),
|
||||
);
|
||||
display_object.set_visible(context.gc_context, was_visible);
|
||||
}
|
||||
}
|
||||
|
@ -1110,37 +1115,41 @@ impl Player {
|
|||
.iter_depth_list()
|
||||
.rev()
|
||||
.find_map(|(_depth, level)| {
|
||||
level.mouse_pick(context, *context.mouse_position, true)
|
||||
level
|
||||
.as_interactive()
|
||||
.and_then(|l| l.mouse_pick(context, *context.mouse_position, true))
|
||||
});
|
||||
|
||||
let mut events: smallvec::SmallVec<[(DisplayObject<'_>, ClipEvent); 2]> =
|
||||
let mut events: smallvec::SmallVec<[(InteractiveObject<'_>, ClipEvent); 2]> =
|
||||
Default::default();
|
||||
|
||||
// Cancel hover if an object is removed from the stage.
|
||||
if let Some(hovered) = context.mouse_over_object {
|
||||
if hovered.removed() {
|
||||
if hovered.as_displayobject().removed() {
|
||||
context.mouse_over_object = None;
|
||||
}
|
||||
}
|
||||
if let Some(pressed) = context.mouse_down_object {
|
||||
if pressed.removed() {
|
||||
if pressed.as_displayobject().removed() {
|
||||
context.mouse_down_object = None;
|
||||
}
|
||||
}
|
||||
|
||||
let cur_over_object = context.mouse_over_object;
|
||||
// Check if a new object has been hovered over.
|
||||
if !DisplayObject::option_ptr_eq(cur_over_object, new_over_object) {
|
||||
if !InteractiveObject::option_ptr_eq(cur_over_object, new_over_object) {
|
||||
// If the mouse button is down, the object the user clicked on grabs the focus
|
||||
// and fires "drag" events. Other objects are ignroed.
|
||||
if context.input.is_mouse_down() {
|
||||
context.mouse_over_object = new_over_object;
|
||||
if let Some(down_object) = context.mouse_down_object {
|
||||
if DisplayObject::option_ptr_eq(context.mouse_down_object, cur_over_object)
|
||||
{
|
||||
if InteractiveObject::option_ptr_eq(
|
||||
context.mouse_down_object,
|
||||
cur_over_object,
|
||||
) {
|
||||
// Dragged from outside the clicked object to the inside.
|
||||
events.push((down_object, ClipEvent::DragOut));
|
||||
} else if DisplayObject::option_ptr_eq(
|
||||
} else if InteractiveObject::option_ptr_eq(
|
||||
context.mouse_down_object,
|
||||
new_over_object,
|
||||
) {
|
||||
|
@ -1155,7 +1164,7 @@ impl Player {
|
|||
events.push((
|
||||
cur_over_object,
|
||||
ClipEvent::RollOut {
|
||||
to: new_over_object.and_then(|d| d.as_interactive()),
|
||||
to: new_over_object,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
@ -1165,7 +1174,7 @@ impl Player {
|
|||
events.push((
|
||||
new_over_object,
|
||||
ClipEvent::RollOver {
|
||||
from: cur_over_object.and_then(|d| d.as_interactive()),
|
||||
from: cur_over_object,
|
||||
},
|
||||
));
|
||||
} else {
|
||||
|
@ -1192,7 +1201,7 @@ impl Player {
|
|||
events.push((context.stage.into(), ClipEvent::MouseUpInside));
|
||||
}
|
||||
|
||||
let released_inside = DisplayObject::option_ptr_eq(
|
||||
let released_inside = InteractiveObject::option_ptr_eq(
|
||||
context.mouse_down_object,
|
||||
context.mouse_over_object,
|
||||
);
|
||||
|
@ -1216,7 +1225,7 @@ impl Player {
|
|||
events.push((
|
||||
over_object,
|
||||
ClipEvent::RollOver {
|
||||
from: cur_over_object.and_then(|d| d.as_interactive()),
|
||||
from: cur_over_object,
|
||||
},
|
||||
));
|
||||
} else {
|
||||
|
@ -1232,10 +1241,8 @@ impl Player {
|
|||
false
|
||||
} else {
|
||||
for (object, event) in events {
|
||||
if !object.removed() {
|
||||
if let Some(interactive) = object.as_interactive() {
|
||||
interactive.handle_clip_event(context, event);
|
||||
}
|
||||
if !object.as_displayobject().removed() {
|
||||
object.handle_clip_event(context, event);
|
||||
}
|
||||
}
|
||||
true
|
||||
|
|
Loading…
Reference in New Issue