Skip to content

Commit

Permalink
avm1: Support MovieClip.tabChildren
Browse files Browse the repository at this point in the history
The property `MovieClip.tabChildren` allows changing the behavior of
tab ordering hierarchically. When set to `false`, it excludes the whole
subtree represented by the movie clip from tab ordering.
  • Loading branch information
kjarosh committed Mar 14, 2024
1 parent 1ac611b commit 339bc07
Show file tree
Hide file tree
Showing 9 changed files with 408 additions and 2 deletions.
1 change: 1 addition & 0 deletions core/src/avm1/globals/movie_clip.rs
Expand Up @@ -121,6 +121,7 @@ const PROTO_DECLS: &[Declaration] = declare_properties! {
// NOTE: `tabEnabled` is not a built-in property of MovieClip.
// NOTE: `tabIndex` is not enumerable in MovieClip, contrary to Button and TextField
"tabIndex" => property(mc_getter!(tab_index), mc_setter!(set_tab_index); DONT_ENUM | VERSION_6);
// NOTE: `tabChildren` is not a built-in property of MovieClip.
};

/// Implements `MovieClip`
Expand Down
9 changes: 8 additions & 1 deletion core/src/display_object/container.rs
Expand Up @@ -475,12 +475,19 @@ pub trait TDisplayObjectContainer<'gc>:
RenderIter::from_container(self.into())
}

fn is_tab_children(&self, _context: &mut UpdateContext<'_, 'gc>) -> bool {
true
}

fn fill_tab_order(
&self,
tab_order: &mut Vec<DisplayObject<'gc>>,
context: &mut UpdateContext<'_, 'gc>,
) {
// TODO Add support for `tabChildren`
if !self.is_tab_children(context) {
return;
}

for child in self.iter_render_list() {
if child.is_tab_enabled(context) {
tab_order.push(child);
Expand Down
12 changes: 12 additions & 0 deletions core/src/display_object/movie_clip.rs
Expand Up @@ -3047,6 +3047,18 @@ impl<'gc> TDisplayObjectContainer<'gc> for MovieClip<'gc> {
fn raw_container_mut(&self, gc_context: &Mutation<'gc>) -> RefMut<'_, ChildContainer<'gc>> {
RefMut::map(self.0.write(gc_context), |this| &mut this.container)
}

/// The property `MovieClip.tabChildren` allows changing the behavior of
/// tab ordering hierarchically.
/// When set to `false`, it excludes the whole subtree represented by
/// the movie clip from tab ordering.
///
/// _NOTE:_
/// According to the AS2 documentation, it should affect only automatic tab ordering.
/// However, that does not seem to be the case, as it also affects custom ordering.
fn is_tab_children(&self, context: &mut UpdateContext<'_, 'gc>) -> bool {
self.get_avm1_boolean_property(context, "tabChildren", true)
}
}

impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> {
Expand Down
4 changes: 3 additions & 1 deletion core/src/focus_tracker.rs
Expand Up @@ -100,6 +100,8 @@ impl<'gc> FocusTracker<'gc> {
tab_order.first()
};

self.set(next.copied(), context);
if next.is_some() {
self.set(next.copied(), context);
}
}
}
78 changes: 78 additions & 0 deletions tests/tests/swfs/avm1/tab_ordering_children/input.json
@@ -0,0 +1,78 @@
[
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 27 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 },
{ "type": "KeyDown", "key_code": 9 }
]
208 changes: 208 additions & 0 deletions tests/tests/swfs/avm1/tab_ordering_children/output.txt
@@ -0,0 +1,208 @@
Focus changed
old: null
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.clipOuter.text3
Tab pressed
Focus changed
old: _level0.clipOuter.text3
new: _level0.clipOuter.text4
Tab pressed
Focus changed
old: _level0.clipOuter.text4
new: _level0.clipOuter.clipInner.text5
Tab pressed
Focus changed
old: _level0.clipOuter.clipInner.text5
new: _level0.clipOuter.clipInner.text6
Tab pressed
Focus changed
old: _level0.clipOuter.clipInner.text6
new: _level0.text1
Escape pressed, moving to stage 1
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Escape pressed, moving to stage 2
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.text1
Escape pressed, moving to stage 3
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.clipOuter.text3
Tab pressed
Focus changed
old: _level0.clipOuter.text3
new: _level0.clipOuter.text4
Tab pressed
Focus changed
old: _level0.clipOuter.text4
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.clipOuter.text3
Escape pressed, moving to stage 4
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Escape pressed, moving to stage 5
Tab pressed
Focus changed
old: _level0.clipOuter.text3
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Escape pressed, moving to stage 6
Tab pressed
Focus changed
old: _level0.text2
new: _level0.clipOuter
Tab pressed
Focus changed
old: _level0.clipOuter
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.clipOuter
Tab pressed
Focus changed
old: _level0.clipOuter
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Escape pressed, moving to stage 7
Tab pressed
Focus changed
old: _level0.text2
new: _level0.clipOuter
Tab pressed
Focus changed
old: _level0.clipOuter
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Tab pressed
Focus changed
old: _level0.text2
new: _level0.clipOuter
Tab pressed
Focus changed
old: _level0.clipOuter
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.text2
Escape pressed, moving to stage 8
Tab pressed
Focus changed
old: _level0.text2
new: _level0.text1
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Escape pressed, moving to stage 9
Tab pressed
Focus changed
old: _level0.text1
new: _level0.clipOuter.text3
Tab pressed
Focus changed
old: _level0.clipOuter.text3
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.clipOuter.text3
Tab pressed
Focus changed
old: _level0.clipOuter.text3
new: _level0.text1
Tab pressed
Focus changed
old: _level0.text1
new: _level0.clipOuter.text3
Tab pressed
Focus changed
old: _level0.clipOuter.text3
new: _level0.text1
Escape pressed, moving to stage 10
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Tab pressed
Tab pressed

0 comments on commit 339bc07

Please sign in to comment.