diff --git a/core/src/avm2/globals/flash/text/TextField.as b/core/src/avm2/globals/flash/text/TextField.as index b87c0f631..4cd69ff9d 100644 --- a/core/src/avm2/globals/flash/text/TextField.as +++ b/core/src/avm2/globals/flash/text/TextField.as @@ -170,10 +170,7 @@ package flash.text { return 0; } - public function getLineIndexOfChar(charIndex:int):int { - stub_method("flash.text.TextField", "getLineIndexOfChar"); - return 0; - } + public native function getLineIndexOfChar(charIndex:int):int; public function getParagraphLength(charIndex:int):int { stub_method("flash.text.TextField", "getParagraphLength"); diff --git a/core/src/avm2/globals/flash/text/text_field.rs b/core/src/avm2/globals/flash/text/text_field.rs index 162854910..4fc0d680a 100644 --- a/core/src/avm2/globals/flash/text/text_field.rs +++ b/core/src/avm2/globals/flash/text/text_field.rs @@ -1444,3 +1444,28 @@ pub fn get_selected_text<'gc>( } Ok("".into()) } + +pub fn get_line_index_of_char<'gc>( + activation: &mut Activation<'_, 'gc>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let Some(this) = this + .as_display_object() + .and_then(|this| this.as_edit_text()) + else { + return Ok(Value::Undefined); + }; + + let index = args.get_i32(activation, 0)?; + if index < 0 { + // Docs say "throw RangeError", reality says "return -1". + return Ok(Value::Number(-1f64)); + } + + if let Some(line) = this.line_index_of_char(index as usize) { + Ok(line.into()) + } else { + Ok(Value::Number(-1f64)) + } +} diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 8c6c12e54..1f92eda5a 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -1931,6 +1931,10 @@ impl<'gc> EditText<'gc> { Some(first_box.start()) } + pub fn line_index_of_char(self, index: usize) -> Option { + self.0.read().layout.find_line_index_by_position(index) + } + fn execute_avm1_asfunction( self, context: &mut UpdateContext<'_, 'gc>, diff --git a/core/src/html/layout.rs b/core/src/html/layout.rs index 79bac3b64..ae24d2503 100644 --- a/core/src/html/layout.rs +++ b/core/src/html/layout.rs @@ -10,7 +10,7 @@ use crate::tag_utils::SwfMovie; use crate::DefaultFont; use gc_arena::Collect; use ruffle_render::shape_utils::DrawCommand; -use std::cmp::{max, min}; +use std::cmp::{max, min, Ordering}; use std::fmt::{Debug, Formatter}; use std::mem; use std::ops::{Deref, Range}; @@ -773,6 +773,19 @@ impl<'gc> Layout<'gc> { boxes_iter: None, } } + + pub fn find_line_index_by_position(&self, position: usize) -> Option { + let result = self.lines.binary_search_by(|probe| { + if probe.end <= position { + Ordering::Less + } else if position < probe.start { + Ordering::Greater + } else { + Ordering::Equal + } + }); + result.ok() + } } /// A `LayoutLine` represents a single line of text.