avm1: Fix some issues with `Selection.getFocus()` and `setFocus()`

This commit is contained in:
Toad06 2023-04-04 15:51:15 +02:00 committed by Mike Welsh
parent 7a6e6cf214
commit 5e165a0682
3 changed files with 62 additions and 66 deletions

View File

@ -2,7 +2,7 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::globals::as_broadcaster::BroadcasterFunctions;
use crate::avm1::property_decl::{define_properties_on, Declaration};
use crate::avm1::{Object, ScriptObject, TObject, Value};
use crate::avm1::{Object, ScriptObject, Value};
use crate::display_object::{EditText, TDisplayObject, TextSelection};
use gc_arena::MutationContext;
@ -108,10 +108,14 @@ pub fn get_focus<'gc>(
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let focus = activation.context.focus_tracker.get();
match focus {
Some(focus) => Ok(focus.object()),
None => Ok(Value::Null),
}
Ok(match focus {
Some(focus) => focus
.object()
.coerce_to_string(activation)
.unwrap_or_default()
.into(),
None => Value::Null,
})
}
pub fn set_focus<'gc>(
@ -121,38 +125,23 @@ pub fn set_focus<'gc>(
) -> Result<Value<'gc>, Error<'gc>> {
let tracker = activation.context.focus_tracker;
match args.get(0) {
None => Ok(false.into()),
Some(Value::Undefined | Value::Null) => {
tracker.set(None, &mut activation.context);
Ok(true.into())
}
Some(Value::Object(obj)) => {
if let Some(display_object) = obj.as_display_object() {
Some(focus) => {
let start_clip = activation.target_clip_or_root();
let object = activation.resolve_target_display_object(start_clip, *focus, false)?;
if let Some(display_object) = object {
if display_object.is_focusable() {
tracker.set(Some(display_object), &mut activation.context);
tracker.set(object, &mut activation.context);
return Ok(true.into());
}
}
// [NA] Note: The documentation says true is success and false is failure,
// but from testing this seems to be opposite.
Ok(false.into())
} else {
Ok(true.into())
}
}
Some(Value::MovieClip(_)) => {
let obj = args.get(0).unwrap().coerce_to_object(activation);
if let Some(display_object) = obj.as_display_object() {
if display_object.is_focusable() {
tracker.set(Some(display_object), &mut activation.context);
}
// [NA] Note: The documentation says true is success and false is failure,
// but from testing this seems to be opposite.
Ok(false.into())
} else {
Ok(true.into())
}
}
_ => Ok(false.into()),
}
}
pub fn create_selection_object<'gc>(

View File

@ -1,7 +1,9 @@
use crate::avm1::Avm1;
use crate::avm1::Value;
use crate::context::UpdateContext;
pub use crate::display_object::{DisplayObject, TDisplayObject, TDisplayObjectContainer};
pub use crate::display_object::{
DisplayObject, TDisplayObject, TDisplayObjectContainer, TextSelection,
};
use gc_arena::{Collect, GcCell, MutationContext};
#[derive(Clone, Copy, Collect)]
@ -22,24 +24,15 @@ impl<'gc> FocusTracker<'gc> {
focused_element: Option<DisplayObject<'gc>>,
context: &mut UpdateContext<'_, 'gc>,
) {
if let Some(text_field) = focused_element.and_then(|e| e.as_edit_text()) {
if text_field.is_editable() {
context.ui.open_virtual_keyboard();
}
}
let old = std::mem::replace(&mut *self.0.write(context.gc_context), focused_element);
if old.is_none() && focused_element.is_none() {
// We didn't have anything, we still don't, no change.
return;
}
if old.is_some() == focused_element.is_some()
&& old.unwrap().as_ptr() == focused_element.unwrap().as_ptr()
if !(old.is_some() == focused_element.is_some()
&& old.unwrap().as_ptr() == focused_element.unwrap().as_ptr())
{
// We're setting it to the same object as before, no change.
return;
}
if let Some(old) = old {
old.on_focus_changed(context.gc_context, false);
}
@ -62,4 +55,18 @@ impl<'gc> FocusTracker<'gc> {
);
}
}
// This applies even if the focused element hasn't changed.
if let Some(text_field) = focused_element.and_then(|e| e.as_edit_text()) {
if text_field.is_editable() {
let length = text_field.text_length();
text_field.set_selection(
Some(TextSelection::for_range(0, length)),
context.gc_context,
);
context.ui.open_virtual_keyboard();
}
}
}
}

View File

@ -26,7 +26,7 @@ _level0.a
false
true
// Selection.setFocus(b)
! - onSetFocus
@ -50,7 +50,7 @@ _level0.b
false
true
// Selection.setSelection(0)
undefined
@ -133,7 +133,7 @@ _level0.b
// Selection.setFocus(b)
false
true
// b.focusEnabled = false;
// Selection.getBeginIndex()
@ -195,7 +195,7 @@ _level0.c
false
true
// Selection.setFocus(d)
false
@ -246,7 +246,7 @@ _level0.button
false
true
// Selection.setFocus(text_input
! - onSetFocus
@ -270,7 +270,7 @@ _level0.text_input
false
true
// Selection.setFocus(text_selectable)
! - onSetFocus
@ -294,7 +294,7 @@ _level0.text_selectable
false
true
// Selection.setSelection(0)
undefined
@ -414,7 +414,7 @@ _level0.text_non_selectable
false
true
// Selection.setFocus(text_input)
! - onSetFocus
@ -438,7 +438,7 @@ _level0.text_input
false
true
// Selection.setSelection(1, 3)
// text_input.text