From 8d50d1feadb9bf2f9b7626f0aebc4db5c8c5087a Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Mon, 6 May 2024 13:03:09 +0200 Subject: [PATCH] core: Move has_focus to InteractiveObject All the interactive objects had the has_focus flag in their concrete implementations (even AVM2 button, which did not use it at all). This patch moves it to InteractiveObject (as a bit flag), making it easier to manage and use through the has_focus, set_has_focus methods. Additionally, the operation of setting the current focus to None when an object was having it was popular enough that it warranted its own method of drop_focus. --- core/src/display_object/avm1_button.rs | 19 ++----------------- core/src/display_object/avm2_button.rs | 11 ----------- core/src/display_object/edit_text.rs | 21 +++++++-------------- core/src/display_object/interactive.rs | 23 +++++++++++++++++++++++ core/src/display_object/movie_clip.rs | 22 ++-------------------- core/src/focus_tracker.rs | 2 ++ 6 files changed, 36 insertions(+), 62 deletions(-) diff --git a/core/src/display_object/avm1_button.rs b/core/src/display_object/avm1_button.rs index 858e41ff5..831fb4aae 100644 --- a/core/src/display_object/avm1_button.rs +++ b/core/src/display_object/avm1_button.rs @@ -45,7 +45,6 @@ pub struct Avm1ButtonData<'gc> { tracking: Cell, object: Lock>>, initialized: Cell, - has_focus: Cell, } #[derive(Clone, Collect)] @@ -98,7 +97,6 @@ impl<'gc> Avm1Button<'gc> { } else { ButtonTracking::Push }), - has_focus: Cell::new(false), }, )) } @@ -393,11 +391,7 @@ impl<'gc> TDisplayObject<'gc> for Avm1Button<'gc> { } fn avm1_unload(&self, context: &mut UpdateContext<'_, 'gc>) { - let had_focus = self.0.has_focus.get(); - if had_focus { - let tracker = context.focus_tracker; - tracker.set(None, context); - } + self.drop_focus(context); if let Some(node) = self.maskee() { node.set_masker(context.gc(), None, true); } else if let Some(node) = self.masker() { @@ -530,7 +524,7 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> { if let Some(name) = event.method_name() { // Keyboard events don't fire their methods // unless the Button has focus (like for MovieClips). - if !event.is_key_event() || self.0.has_focus.get() { + if !event.is_key_event() || self.has_focus() { context.action_queue.queue_action( self_display_object, ActionType::Method { @@ -608,15 +602,6 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> { } } - fn on_focus_changed( - &self, - _context: &mut UpdateContext<'_, 'gc>, - focused: bool, - _other: Option>, - ) { - self.0.has_focus.set(focused); - } - fn tab_enabled_avm1(&self, context: &mut UpdateContext<'_, 'gc>) -> bool { self.get_avm1_boolean_property(context, "tabEnabled", |_| true) } diff --git a/core/src/display_object/avm2_button.rs b/core/src/display_object/avm2_button.rs index 5b5def4ef..f4feedab7 100644 --- a/core/src/display_object/avm2_button.rs +++ b/core/src/display_object/avm2_button.rs @@ -74,7 +74,6 @@ pub struct Avm2ButtonData<'gc> { /// The AVM2 representation of this button. object: Lock>>, - has_focus: Cell, enabled: Cell, use_hand_cursor: Cell, @@ -135,7 +134,6 @@ impl<'gc> Avm2Button<'gc> { } else { ButtonTracking::Push }), - has_focus: Cell::new(false), enabled: Cell::new(true), use_hand_cursor: Cell::new(true), skip_current_frame: Cell::new(false), @@ -823,15 +821,6 @@ impl<'gc> TInteractiveObject<'gc> for Avm2Button<'gc> { } } - fn on_focus_changed( - &self, - _context: &mut UpdateContext<'_, 'gc>, - focused: bool, - _other: Option>, - ) { - self.0.has_focus.set(focused); - } - fn tab_enabled_avm2_default(&self, _context: &mut UpdateContext<'_, 'gc>) -> bool { true } diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 357b5b74b..95a4d244f 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -987,7 +987,7 @@ impl<'gc> EditText<'gc> { ..Default::default() }); - let visible_selection = if edit_text.flags.contains(EditTextFlag::HAS_FOCUS) { + let visible_selection = if self.has_focus() { edit_text.selection } else { None @@ -2121,7 +2121,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { let has_parent = self.parent().is_some(); if self.movie().is_action_script_3() && had_parent && !has_parent { - let had_focus = self.0.read().flags.contains(EditTextFlag::HAS_FOCUS); + let had_focus = self.has_focus(); if had_focus { let tracker = context.focus_tracker; tracker.set(None, context); @@ -2237,7 +2237,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { }); if edit_text.layout.is_empty() && !edit_text.flags.contains(EditTextFlag::READ_ONLY) { - let visible_selection = if edit_text.flags.contains(EditTextFlag::HAS_FOCUS) { + let visible_selection = if self.has_focus() { edit_text.selection } else { None @@ -2275,11 +2275,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { } fn avm1_unload(&self, context: &mut UpdateContext<'_, 'gc>) { - let had_focus = self.0.read().flags.contains(EditTextFlag::HAS_FOCUS); - if had_focus { - let tracker = context.focus_tracker; - tracker.set(None, context); - } + self.drop_focus(context); if let Some(node) = self.maskee() { node.set_masker(context.gc_context, None, true); @@ -2459,11 +2455,9 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> { focused: bool, _other: Option>, ) { - let is_action_script_3 = self.movie().is_action_script_3(); - let mut text = self.0.write(context.gc_context); - text.flags.set(EditTextFlag::HAS_FOCUS, focused); - if !focused && !is_action_script_3 { - text.selection = None; + let is_avm1 = !self.movie().is_action_script_3(); + if !focused && is_avm1 { + self.0.write(context.gc_context).selection = None; } } @@ -2494,7 +2488,6 @@ bitflags::bitflags! { struct EditTextFlag: u16 { const FIRING_VARIABLE_BINDING = 1 << 0; const HAS_BACKGROUND = 1 << 1; - const HAS_FOCUS = 1 << 2; // The following bits need to match `swf::EditTextFlag`. const READ_ONLY = 1 << 3; diff --git a/core/src/display_object/interactive.rs b/core/src/display_object/interactive.rs index e1b170891..de2f38f7c 100644 --- a/core/src/display_object/interactive.rs +++ b/core/src/display_object/interactive.rs @@ -76,6 +76,9 @@ bitflags! { /// Whether this `InteractiveObject` accepts double-clicks. const DOUBLE_CLICK_ENABLED = 1 << 1; + + /// Whether this `InteractiveObject` is currently focused. + const HAS_FOCUS = 1 << 2; } } @@ -167,6 +170,18 @@ pub trait TInteractiveObject<'gc>: .set(InteractiveObjectFlags::DOUBLE_CLICK_ENABLED, value) } + fn has_focus(self) -> bool { + self.raw_interactive() + .flags + .contains(InteractiveObjectFlags::HAS_FOCUS) + } + + fn set_has_focus(self, mc: &Mutation<'gc>, value: bool) { + self.raw_interactive_mut(mc) + .flags + .set(InteractiveObjectFlags::HAS_FOCUS, value) + } + fn context_menu(self) -> Avm2Value<'gc> { self.raw_interactive().context_menu } @@ -536,6 +551,14 @@ pub trait TInteractiveObject<'gc>: ) { } + /// If this object has focus, this method drops it. + fn drop_focus(&self, context: &mut UpdateContext<'_, 'gc>) { + if self.has_focus() { + let tracker = context.focus_tracker; + tracker.set(None, context); + } + } + fn call_focus_handler( &self, context: &mut UpdateContext<'_, 'gc>, diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index f1a09815b..9dc3d4ef3 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -162,7 +162,6 @@ pub struct MovieClipData<'gc> { avm2_class: Option>, #[collect(require_static)] drawing: Drawing, - has_focus: bool, avm2_enabled: bool, /// Show a hand cursor when the clip is in button mode. @@ -212,7 +211,6 @@ impl<'gc> MovieClip<'gc> { flags: MovieClipFlags::empty(), avm2_class: None, drawing: Drawing::new(), - has_focus: false, avm2_enabled: true, avm2_use_hand_cursor: true, button_mode: false, @@ -255,7 +253,6 @@ impl<'gc> MovieClip<'gc> { flags: MovieClipFlags::empty(), avm2_class: Some(class), drawing: Drawing::new(), - has_focus: false, avm2_enabled: true, avm2_use_hand_cursor: true, button_mode: false, @@ -299,7 +296,6 @@ impl<'gc> MovieClip<'gc> { flags: MovieClipFlags::PLAYING, avm2_class: None, drawing: Drawing::new(), - has_focus: false, avm2_enabled: true, avm2_use_hand_cursor: true, button_mode: false, @@ -368,7 +364,6 @@ impl<'gc> MovieClip<'gc> { flags: MovieClipFlags::PLAYING, avm2_class: None, drawing: Drawing::new(), - has_focus: false, avm2_enabled: true, avm2_use_hand_cursor: true, button_mode: false, @@ -2962,11 +2957,7 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { } } - let had_focus = self.0.read().has_focus; - if had_focus { - let tracker = context.focus_tracker; - tracker.set(None, context); - } + self.drop_focus(context); { let mut mc = self.0.write(context.gc_context); @@ -3101,7 +3092,7 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> { if swf_version >= 6 { if let Some(name) = event.method_name() { // Keyboard events don't fire their methods unless the MovieClip has focus (#2120). - if !event.is_key_event() || read.has_focus { + if !event.is_key_event() || self.has_focus() { context.action_queue.queue_action( self.into(), ActionType::Method { @@ -3386,15 +3377,6 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> { } } - fn on_focus_changed( - &self, - context: &mut UpdateContext<'_, 'gc>, - focused: bool, - _other: Option>, - ) { - self.0.write(context.gc_context).has_focus = focused; - } - fn tab_enabled_avm1(&self, context: &mut UpdateContext<'_, 'gc>) -> bool { self.get_avm1_boolean_property(context, "tabEnabled", |context| { self.tab_index().is_some() || self.is_button_mode(context) diff --git a/core/src/focus_tracker.rs b/core/src/focus_tracker.rs index f492d941b..71cf9dc62 100644 --- a/core/src/focus_tracker.rs +++ b/core/src/focus_tracker.rs @@ -98,10 +98,12 @@ impl<'gc> FocusTracker<'gc> { self.update_highlight(context); if let Some(old) = old { + old.set_has_focus(context.gc(), false); old.on_focus_changed(context, false, new); old.call_focus_handler(context, false, new); } if let Some(new) = new { + new.set_has_focus(context.gc(), true); new.on_focus_changed(context, true, old); new.call_focus_handler(context, true, old); }