RFR: 8319844 : Text/TextFlow.hitTest() is incorrect in RTL orientation [v11]

Karthik P K kpk at openjdk.org
Mon Feb 19 10:25:04 UTC 2024


On Wed, 14 Feb 2024 04:39:04 GMT, John Hendrikx <jhendrikx at openjdk.org> wrote:

>> Karthik P K has updated the pull request incrementally with one additional commit since the last revision:
>> 
>>   Review comments
>
> modules/javafx.graphics/src/main/java/com/sun/javafx/text/PrismTextLayout.java line 432:
> 
>> 430:         int ltrIndex = 0;
>> 431:         int textWidthPrevLine = 0;
>> 432:         float xHitPos = x;
> 
> This variable seems important, yet is only used in the RTL+Text branches.
> 
> It seems that this variable can also be easily calculated as:
> 
>     originalX - (getMirroringWidth() - x) + bounds.getMinX
> 
> This calculation can be done in the final RTL+Text branch near the end, no need to precalculate it IMHO

The calculation done ln.no.486 calculates the effective value of x relative to the Text node on which hit test is requested. As far as I understood, the above suggested calculation doesn't perform the same calculation. So I did not make any changes.
Am I missing something here. Please let me know if you have any suggestions.

> modules/javafx.graphics/src/main/java/com/sun/javafx/text/PrismTextLayout.java line 440:
> 
>> 438:             insertionIndex = charIndex + 1;
>> 439:         } else {
>> 440:             if (isMirrored && (forTextFlow || spans == null)) {
> 
> The extra condition here adds nothing of value, the original `if` was correct.
> 
> Because `spans == null` means "for Text", and `forTextFlow == true` means "for TextFlow".  Since it always is either a `TextFlow` or a `Text` the `||` always evaluates to `true` -- unless of course you pass in this flag incorrectly (again, I think the flag should be removed).

The additional conditions are added to differentiate between the three cases: Text, TexFlow and Text embedded in TextFlow. Hence not make changes in this.

> modules/javafx.graphics/src/main/java/com/sun/javafx/text/PrismTextLayout.java line 448:
> 
>> 446:             TextRun run = null;
>> 447:             //TODO binary search
>> 448:             if (text == null || spans == null) {
> 
> `text` now refers to the `text` parameter here, and not to the original `text` field -- as discussed earlier, I think you shouldn't be passing in `text` as argument at all.
> 
> Furthermore, I think the original code was also flawed; `text` is only `null` for a short while, and it is never `null` when `spans == null`.  In other words, this `if` should IMHO be:
> 
> Suggestion:
> 
>             if (spans == null) {  // not a TextFlow, must be Text
> 
> 
> Use `getText()` function to get the value of the text (it will just use the one from `Text` set via `setContent(String text, Object font)` or it will calculate it for `TextFlow` from the spans set via `setContent(TextSpan[] spans)`;

As I mentioned earlier, the condition is required to differentiate between Text, TexFlow and Text embedded in TextFlow. Hence not removing condition, made modification to use `forTextFlow` instead of `text == null`

> modules/javafx.graphics/src/main/java/com/sun/javafx/text/PrismTextLayout.java line 567:
> 
>> 565:                 int[] trailing = new int[1];
>> 566:                 if (text != null && spans != null) {
>> 567:                     charIndex = run.getOffsetAtX(x, trailing);
> 
> Here also: `text` is no longer referring to the field `text` but to the argument `text`.  I also again think the check is wrong.  It should just be:
> 
>      if (spans != null) {   // must be a TextFlow
> 
> There are more oddities in the code below (which you didn't change this round -- see my comments inline:
> 
>                 // !! getText() ***never*** returns `null`, the check is not needed
>                 if (getText() != null && insertionIndex < getText().length) {
>                     if (!leading) {
>                         BreakIterator charIterator = BreakIterator.getCharacterInstance();
> 
>                         // before, when `text` was a field, this could never be null here
>                         if (text != null) {
>                             charIterator.setText(text);
>                         } else {
>                             charIterator.setText(new String(getText()));
>                         }
>                         int next = charIterator.following(insertionIndex);
>                         if (next == BreakIterator.DONE) {
>                             insertionIndex += 1;
>                         } else {
>                             insertionIndex = next;
>                         }
>                     }
>                 } else if (!leading) {
>                     insertionIndex += 1;
>                 }

Made changes to use `forTextFlow` instead of condition `text != null` so that it is easier to understand

> modules/javafx.graphics/src/main/java/com/sun/javafx/text/PrismTextLayout.java line 576:
> 
>> 574:                     int indexOffset;
>> 575:                     if (isMirrored) {
>> 576:                         indexOffset = run.getOffsetAtX(xHitPos, trailing);
> 
> Here is the only place where `xHitPos` is used again (the RTL + Text case).  I think it can just be calculated (you need to save off the original `x` value, or even better, don't modify the `x` argument but use a different variable for the `x` calculation).

Added comment above regarding this

> modules/javafx.graphics/src/main/java/javafx/scene/text/Text.java line 1035:
> 
>> 1033:             curRunStart = ((TextRun) runs[runIndex]).getStart();
>> 1034:         }
>> 1035:         TextLayout.Hit h = layout.getHitInfo((float)x, (float)y, getText(), textRunStart, curRunStart, false);
> 
> I think `getText()` as a parameter is not needed here (it is passed at line 260 when it calls `setContent`).  Also, I think this should be `getTextInternal()` -- see the comment in that method.
> 
> I also think the `false` is not needed, see comments on the interface.

Used `getTextInternal()` instead of `getText()`. I believe the text parameter is needed as mentioned before

> modules/javafx.graphics/src/main/java/javafx/scene/text/TextFlow.java line 202:
> 
>> 200:             double x = point.getX();
>> 201:             double y = point.getY();
>> 202:             TextLayout.Hit h = layout.getHitInfo((float)x, (float)y, null, 0, 0, true);
> 
> The `null` and `true` here are not needed IMHO (what would even happen when they conflict, `null` + `false`, or non-`null` + `true`?)

Removed last parameter as suggested

> tests/system/src/test/java/test/robot/javafx/scene/RTLTextCharacterIndexTest.java line 107:
> 
>> 105:  */
>> 106: 
>> 107: public class RTLTextCharacterIndexTest {
> 
> This is a system test, which don't run with the build system. Are you sure this will work on all platforms?  I don't see a specific `Font` used, which means `X_LEADING_OFFSET` may be incorrect when the platform is supplying a different font.

I see that the tests don't run on all the platforms. I will look into this and update the PR

-------------

PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494314778
PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494322207
PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494317666
PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494324563
PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494323474
PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494326008
PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494326979
PR Review Comment: https://git.openjdk.org/jfx/pull/1323#discussion_r1494328009


More information about the openjfx-dev mailing list