avm2: Roughly implement `TextField.*IndexAtPoint`

Both implementations currently use `screen_position_to_index`, which
is inaccurate, and that's why these methods still produce a stub warning.
However, this implementation makes some games work, is a lot better than
a perfect implementation which would require a lot of work and time,
and infinitely better than no implementation at all.
This commit is contained in:
Kamil Jarosz 2024-06-28 01:32:08 +02:00 committed by Nathan Adams
parent a9708a8017
commit a3dddbd80d
2 changed files with 75 additions and 10 deletions

View File

@ -139,10 +139,7 @@ package flash.text {
stub_method("flash.text.TextField", "insertXMLText");
}
public function getCharIndexAtPoint(x:Number, y:Number):int {
stub_method("flash.text.TextField", "getCharIndexAtPoint");
return 0;
}
public native function getCharIndexAtPoint(x:Number, y:Number):int;
public native function getLineLength(lineIndex:int):int;
@ -165,10 +162,7 @@ package flash.text {
return null;
}
public function getLineIndexAtPoint(x:Number, y:Number):int {
stub_method("flash.text.TextField", "getLineIndexAtPoint");
return 0;
}
public native function getLineIndexAtPoint(x:Number, y:Number):int;
public native function getLineIndexOfChar(charIndex:int):int;

View File

@ -10,8 +10,8 @@ use crate::avm2::Error;
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject, TextSelection};
use crate::html::TextFormat;
use crate::string::AvmString;
use crate::{avm2_stub_getter, avm2_stub_setter};
use swf::Color;
use crate::{avm2_stub_getter, avm2_stub_method, avm2_stub_setter};
use swf::{Color, Point};
pub fn text_field_allocator<'gc>(
class: ClassObject<'gc>,
@ -1469,3 +1469,74 @@ pub fn get_line_index_of_char<'gc>(
Ok(Value::Number(-1f64))
}
}
pub fn get_char_index_at_point<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
// TODO This currently uses screen_position_to_index, which is inaccurate, because:
// 1. getCharIndexAtPoint should return -1 when clicked outside of a character,
// 2. screen_position_to_index returns caret index, not clicked character index.
// Currently, it is difficult to prove accuracy of this method, as at the time
// of writing this comment, text layout behaves differently compared to Flash.
// However, the current implementation is good enough to make some SWFs work.
avm2_stub_method!(
activation,
"flash.text.TextField",
"getCharIndexAtPoint",
"inaccurate char index detection"
);
let Some(this) = this
.as_display_object()
.and_then(|this| this.as_edit_text())
else {
return Ok(Value::Undefined);
};
let x = args.get_f64(activation, 0)?;
let y = args.get_f64(activation, 1)?;
if let Some(index) = this.screen_position_to_index(Point::from_pixels(x, y)) {
Ok(index.into())
} else {
Ok(Value::Number(-1f64))
}
}
pub fn get_line_index_at_point<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
// TODO This currently uses screen_position_to_index, but it should calculate
// the line index using only line data, without taking into account characters.
// Currently, it is difficult to prove accuracy of this method, as at the time
// of writing this comment, text layout behaves differently compared to Flash.
avm2_stub_method!(
activation,
"flash.text.TextField",
"getLineIndexAtPoint",
"inaccurate line index detection"
);
let Some(this) = this
.as_display_object()
.and_then(|this| this.as_edit_text())
else {
return Ok(Value::Undefined);
};
let x = args.get_f64(activation, 0)?;
let y = args.get_f64(activation, 1)?;
if let Some(index) = this
.screen_position_to_index(Point::from_pixels(x, y))
.and_then(|index| this.line_index_of_char(index))
{
Ok(index.into())
} else {
Ok(Value::Number(-1f64))
}
}