From d26f263e7b66c362a3bb1006e529e04bf00ff13a Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Thu, 27 Jun 2024 21:17:52 +0200 Subject: [PATCH 1/3] avm2: Implement TextField.getLineIndexOfChar() --- core/src/avm2/globals/flash/text/TextField.as | 5 +--- .../src/avm2/globals/flash/text/text_field.rs | 25 +++++++++++++++++++ core/src/display_object/edit_text.rs | 4 +++ core/src/html/layout.rs | 15 ++++++++++- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/core/src/avm2/globals/flash/text/TextField.as b/core/src/avm2/globals/flash/text/TextField.as index b87c0f631c6a..4cd69ff9d639 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 162854910396..4fc0d680aed0 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 8c6c12e54fbf..1f92eda5a099 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 79bac3b64288..ae24d2503fab 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. From 1acb12c5caa81df3b509404122c101c777b45c15 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Thu, 27 Jun 2024 21:18:07 +0200 Subject: [PATCH 2/3] tests: Add avm2/edittext_get_line_index_of_char test Verifies the behavior of TextField.getLineIndexOfChar(). --- .../edittext_get_line_index_of_char/Test.as | 35 ++++++++ .../output.txt | 76 ++++++++++++++++++ .../edittext_get_line_index_of_char/test.swf | Bin 0 -> 1146 bytes .../edittext_get_line_index_of_char/test.toml | 1 + 4 files changed, 112 insertions(+) create mode 100644 tests/tests/swfs/avm2/edittext_get_line_index_of_char/Test.as create mode 100644 tests/tests/swfs/avm2/edittext_get_line_index_of_char/output.txt create mode 100644 tests/tests/swfs/avm2/edittext_get_line_index_of_char/test.swf create mode 100644 tests/tests/swfs/avm2/edittext_get_line_index_of_char/test.toml diff --git a/tests/tests/swfs/avm2/edittext_get_line_index_of_char/Test.as b/tests/tests/swfs/avm2/edittext_get_line_index_of_char/Test.as new file mode 100644 index 000000000000..3f9ae6e9c966 --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_get_line_index_of_char/Test.as @@ -0,0 +1,35 @@ +package { +import flash.display.Sprite; +import flash.text.TextField; + +public class Test extends Sprite { + private var text1: TextField; + private var text2: TextField; + private var text3: TextField; + + public function Test() { + text1 = new TextField(); + text1.text = "line 1\n line2\n\n line 4\n"; + text2 = new TextField(); + text2.htmlText = "

line 1


  • line 2
  • \n\n
  • line 4
  • "; + text3 = new TextField(); + text3.wordWrap = true; + text3.width = 50; + text3.text = "first second\nthird"; + + addChild(text1); + addChild(text2); + addChild(text3); + printValues("text1", text1, 25); + printValues("text2", text2, 25); + printValues("text3", text3, 20); + } + + private function printValues(name:String, text:TextField, len: int):void { + trace("Field: " + name); + for (var i = -1; i < len; ++i) { + trace(" " + name + ".getLineIndexOfChar(" + i + ") = " + text.getLineIndexOfChar(i)); + } + } +} +} diff --git a/tests/tests/swfs/avm2/edittext_get_line_index_of_char/output.txt b/tests/tests/swfs/avm2/edittext_get_line_index_of_char/output.txt new file mode 100644 index 000000000000..369e95d25e9d --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_get_line_index_of_char/output.txt @@ -0,0 +1,76 @@ +Field: text1 + text1.getLineIndexOfChar(-1) = -1 + text1.getLineIndexOfChar(0) = 0 + text1.getLineIndexOfChar(1) = 0 + text1.getLineIndexOfChar(2) = 0 + text1.getLineIndexOfChar(3) = 0 + text1.getLineIndexOfChar(4) = 0 + text1.getLineIndexOfChar(5) = 0 + text1.getLineIndexOfChar(6) = 0 + text1.getLineIndexOfChar(7) = 1 + text1.getLineIndexOfChar(8) = 1 + text1.getLineIndexOfChar(9) = 1 + text1.getLineIndexOfChar(10) = 1 + text1.getLineIndexOfChar(11) = 1 + text1.getLineIndexOfChar(12) = 1 + text1.getLineIndexOfChar(13) = 1 + text1.getLineIndexOfChar(14) = 2 + text1.getLineIndexOfChar(15) = 3 + text1.getLineIndexOfChar(16) = 3 + text1.getLineIndexOfChar(17) = 3 + text1.getLineIndexOfChar(18) = 3 + text1.getLineIndexOfChar(19) = 3 + text1.getLineIndexOfChar(20) = 3 + text1.getLineIndexOfChar(21) = 3 + text1.getLineIndexOfChar(22) = 3 + text1.getLineIndexOfChar(23) = -1 + text1.getLineIndexOfChar(24) = -1 +Field: text2 + text2.getLineIndexOfChar(-1) = -1 + text2.getLineIndexOfChar(0) = 0 + text2.getLineIndexOfChar(1) = 0 + text2.getLineIndexOfChar(2) = 0 + text2.getLineIndexOfChar(3) = 0 + text2.getLineIndexOfChar(4) = 0 + text2.getLineIndexOfChar(5) = 0 + text2.getLineIndexOfChar(6) = 0 + text2.getLineIndexOfChar(7) = 0 + text2.getLineIndexOfChar(8) = 0 + text2.getLineIndexOfChar(9) = 0 + text2.getLineIndexOfChar(10) = 0 + text2.getLineIndexOfChar(11) = 0 + text2.getLineIndexOfChar(12) = 0 + text2.getLineIndexOfChar(13) = 0 + text2.getLineIndexOfChar(14) = 0 + text2.getLineIndexOfChar(15) = 1 + text2.getLineIndexOfChar(16) = 2 + text2.getLineIndexOfChar(17) = 2 + text2.getLineIndexOfChar(18) = 2 + text2.getLineIndexOfChar(19) = 2 + text2.getLineIndexOfChar(20) = 2 + text2.getLineIndexOfChar(21) = 2 + text2.getLineIndexOfChar(22) = 2 + text2.getLineIndexOfChar(23) = -1 + text2.getLineIndexOfChar(24) = -1 +Field: text3 + text3.getLineIndexOfChar(-1) = -1 + text3.getLineIndexOfChar(0) = 0 + text3.getLineIndexOfChar(1) = 0 + text3.getLineIndexOfChar(2) = 0 + text3.getLineIndexOfChar(3) = 0 + text3.getLineIndexOfChar(4) = 0 + text3.getLineIndexOfChar(5) = 0 + text3.getLineIndexOfChar(6) = 1 + text3.getLineIndexOfChar(7) = 1 + text3.getLineIndexOfChar(8) = 1 + text3.getLineIndexOfChar(9) = 1 + text3.getLineIndexOfChar(10) = 1 + text3.getLineIndexOfChar(11) = 1 + text3.getLineIndexOfChar(12) = 1 + text3.getLineIndexOfChar(13) = 2 + text3.getLineIndexOfChar(14) = 2 + text3.getLineIndexOfChar(15) = 2 + text3.getLineIndexOfChar(16) = 2 + text3.getLineIndexOfChar(17) = 2 + text3.getLineIndexOfChar(18) = -1 + text3.getLineIndexOfChar(19) = -1 diff --git a/tests/tests/swfs/avm2/edittext_get_line_index_of_char/test.swf b/tests/tests/swfs/avm2/edittext_get_line_index_of_char/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..affcbfcd95d7c677c5883cabd1260c7729b8926e GIT binary patch literal 1146 zcmV-=1cm!US5qmP1^@tf0fkiEZre5#CPi7Yl|MUioTN)zWlfuAvn^R|(I$0`2Jt+! z88WA|#qo?v@}00Z_KyV}L>+^tWrtKL<6g6v3klNP<00Fs9v zzw@0#@=!x0{)5o$ZwM`8)Gn_A|{hDBA($to5+(tH$lB zYUswzalR?q)Y7AW>nbJ)+-?ia7W*q-?A5;9+24P%_o$6Vb5H{BeC^^Z(nPz$>FMdW z3V|gFnpk}H=M5+mgg$#ppZ*NiAIf7EbM$+;Dk7AwWYZ$5r=*6M&1N*X!Mk@;yH4Z6 zV>aX+mwA?wU0|KVJePKjCC~Ml+ECPVG?ekE-c-ahigBwMw#F>AsfDd(FVb2~&mCh1 zU|=gsiew@x90=HXhaY)q+$_$G{2+*|ClL*E1J~ltyzNFYS7T-dzNK)-jjTMitX;?T ztf??^ef~A|`Yg>n=33f`K0RW@V?K6sJfbF(vkh&lf~uD5hm3y?8+hPbY`ACdIyAa2 zZmRdx`S%x=Gf9YX3|F@ORxI&2a4l({L-a!e87v(f90mtGIIx)Q`fmCb4jkr%Bn)EG zFm%$mb(1vi+#$EOwr0lXYPoUf(dW`W$Z{s_^}c5&pZnw(-dG!!D2EpkO^>-OMdO(i z2bVFm^uXr~Hpks#HbyG%tnUW!>|vkL>YLn5sP6X?G>qHA9YfCHO0FVQt}WxrM`%eF zL>Wsm$;-GPlPMWb%Vb6-voa~lxFnN`Og@mwMVVZZ$z_>fS=^Axby?b!#gApNDvLEJ zHvvj)9+8<@Jh%GeLcW|Ll8V7N2B;)3!k{+A@mNQd?SB~zm=eu$AHB-0~XsLGTiGZRQm zW=FPIRcJ1mn?O=h9NE$}Dkmiv*A1Fa=E11Wfm^v-0EJEgZzhYWDQ0FRHKoj48dW7= ztgmzwvY9N8swrtQTNza$zO-J>)-FJ96quT78Z47`n0m-6FY^T(F4zltw``a7?xMY@cPn;9@0RRR)_sF6W}j084*~*(g|olK MeYnT}07`!1w(D6t`2YX_ literal 0 HcmV?d00001 diff --git a/tests/tests/swfs/avm2/edittext_get_line_index_of_char/test.toml b/tests/tests/swfs/avm2/edittext_get_line_index_of_char/test.toml new file mode 100644 index 000000000000..cf6123969a1d --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_get_line_index_of_char/test.toml @@ -0,0 +1 @@ +num_ticks = 1 From 5d56b9131e2987a99b55954bf94610b85d49e6dd Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Fri, 28 Jun 2024 00:49:16 +0200 Subject: [PATCH 3/3] chore: Remove unnecessary returns --- core/src/avm2/globals/flash/text/text_field.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/avm2/globals/flash/text/text_field.rs b/core/src/avm2/globals/flash/text/text_field.rs index 4fc0d680aed0..b6cf98ec942b 100644 --- a/core/src/avm2/globals/flash/text/text_field.rs +++ b/core/src/avm2/globals/flash/text/text_field.rs @@ -1164,11 +1164,11 @@ pub fn get_line_length<'gc>( return Err(make_error_2006(activation)); } - return if let Some(length) = this.line_length(line_num as usize) { + if let Some(length) = this.line_length(line_num as usize) { Ok(length.into()) } else { Err(make_error_2006(activation)) - }; + } } pub fn get_line_text<'gc>( @@ -1208,11 +1208,11 @@ pub fn get_line_offset<'gc>( return Err(make_error_2006(activation)); } - return if let Some(offset) = this.line_offset(line_num as usize) { + if let Some(offset) = this.line_offset(line_num as usize) { Ok(offset.into()) } else { Err(make_error_2006(activation)) - }; + } } pub fn get_bottom_scroll_v<'gc>(