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.
This commit is contained in:
Kamil Jarosz 2024-05-06 13:03:09 +02:00 committed by Nathan Adams
parent 19b7cc9025
commit 8d50d1fead
6 changed files with 36 additions and 62 deletions

View File

@ -45,7 +45,6 @@ pub struct Avm1ButtonData<'gc> {
tracking: Cell<ButtonTracking>,
object: Lock<Option<Object<'gc>>>,
initialized: Cell<bool>,
has_focus: Cell<bool>,
}
#[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<InteractiveObject<'gc>>,
) {
self.0.has_focus.set(focused);
}
fn tab_enabled_avm1(&self, context: &mut UpdateContext<'_, 'gc>) -> bool {
self.get_avm1_boolean_property(context, "tabEnabled", |_| true)
}

View File

@ -74,7 +74,6 @@ pub struct Avm2ButtonData<'gc> {
/// The AVM2 representation of this button.
object: Lock<Option<Avm2Object<'gc>>>,
has_focus: Cell<bool>,
enabled: Cell<bool>,
use_hand_cursor: Cell<bool>,
@ -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<InteractiveObject<'gc>>,
) {
self.0.has_focus.set(focused);
}
fn tab_enabled_avm2_default(&self, _context: &mut UpdateContext<'_, 'gc>) -> bool {
true
}

View File

@ -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<InteractiveObject<'gc>>,
) {
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;

View File

@ -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>,

View File

@ -162,7 +162,6 @@ pub struct MovieClipData<'gc> {
avm2_class: Option<Avm2ClassObject<'gc>>,
#[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<InteractiveObject<'gc>>,
) {
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)

View File

@ -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);
}