JDK-8091012 : [Text] No way to distinguish between a hit to the last character and a hit beyond the end of text
  • Type: Enhancement
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: 8u20
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2014-07-03
  • Updated: 2025-12-05
  • Resolved: 2025-12-05
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdResolved
Related Reports
Duplicate :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
When hitting a Text node, there is no way to distinguish between a hit to the trailing edge of the last character and a hit beyond the end of text. Both return a HitInfo

    charIndex: n-1, isLeading: false

where n is the length of the text. The code to showcase the hit beyond the end of text is below.
For a hit beyond the end of text, I would suggest to return a HitInfo

    charIndex: n, isLeading: true

instead. This resolves to the same insertion index, but provides a way to tell apart the two cases.

Note that making this change also resolves RT-37801.

I realize that impl_* methods are not public API, but I'm using them in RichTextFX [1] anyway because there's no public API for this functionality. If you are planning/willing to expose more public API on Text/TextFlow, I will be happy to summarize what private API is currently used in RichTextFX.


import javafx.geometry.Point2D;
import javafx.scene.text.Text;

import com.sun.javafx.scene.text.HitInfo;

public class HitEndOfText {

    public static void main(String[] args) {
        Text text = new Text("foo");
        HitInfo hit = text.impl_hitTestChar(new Point2D(9999, 1));
        System.out.println(hit);
        // prints: charIndex: 2, isLeading: false
        // expected: charIndex: 3, isLeading: true
    }

}

[1] https://github.com/TomasMikula/RichTextFX
Comments
I believe the new APIs delivered in JDK-8341670 [Text,TextFlow] Public API for Text Layout Info provide the necessary information (please let me know if it is not so).
05-12-2025

With line wrapping, the mouse event might be within Text bounds, yet not hitting any character.
10-05-2023

[~tmikula] : another application-side possibility is to obtain `PickResult` from the `MouseEvent` and see if a Text instance is there. Would that work for your use case(s)?
10-05-2023

Here's a use case: Suppose the last (or first) word in a Text is selected (selection implemented by your own means, with a blue rectangle placed behind the text). Then you press mouse button and drag. You need to distinguish between - mouse pressed on the last character, in which case you move the selection; or - mouse pressed after the last character, in which case you start a new selection.
10-05-2023

So what do you want if you have a pt that is before the first char ? An index of -1 ? In either case it seems wrong to return an index of non-existent char. I am not sure of the need for this and Java 2D does what FX does too https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/java/awt/font/TextLayout.html#hitTestChar(float,float) "Coordinates outside the bounds of the TextLayout map to hits on the leading edge of the first logical character, or the trailing edge of the last logical character, as appropriate" Perhaps there's some other info that you are missing like being able to get the bounds that leads to this request ?
10-05-2023

I guess that could work as the new workaround.
25-11-2015

Ah, good point. You could use BreakIterator to calculate the next index. int i1 = hit.getCharIndex(); int i2 = hit.getInsertionIndex(); if (hit.isLeading()) { BreakIterator charIterator = BreakIterator.getCharacterInstance(); charIterator.setText(text); int next = charIterator.following(i1); if (next == BreakIterator.DONE) { i2 = i1 + 1; } else { i2 = next; } }
25-11-2015

That will produce a false negative for any direct hit to the leading half of a character: charIndex and insertionIndex will be equal, so the shape will be empty, so a miss will be reported, while it is a direct hit.
25-11-2015

I haven't tested this, but it seems that you could calculate the distance from the point to the character's bounding box using something like this (with the proposed new API for 9). HitInfo hit = text.hitTest(point); Path shape = new Path(text.rangeShape(hit.getCharIndex(), hit.getInsertionIndex())); if (shape.getBoundsInLocal().contains(point)) { // Direct hit }
25-11-2015

I agree that my proposed solution is not robust. I was just hoping that it would generally make sense, not hurt anyone and help in my specific use-case. Let's focus on the best way to determine the character index at (x, y), if any. Thanks for the proposed workaround.
09-07-2014

>y to paragraphIndex is common, y to lineIndex is not currently used anywhere in RichTextFX. There's y to paragraphIndex and then hit test on the paragraph's TextFlow. Other option is to get the offset returned by the hit test, find the line, find the line bounds, intersect it with the x,y point. You can see the offset to line index at the first of PrismTextLayot#getCaretShape(). At this point I don't think that changing impl_hitTestChar() is really going to help you much, you still have the before the line case and the wrapped line case. The change is probably fine, but there is a change is can break something in controls and we also need to make sure that "charIndex: n-1, isLeading: false" and "charIndex: n, isLeading: true" produce the exactly same caret shape (when used in getImpl_caretShape(). This needs to be true for complex text (Thai cluster for example) and a bidi (Arabic or Hebrew). Do you agree ?
09-07-2014

> Do you use one TextFlow per line/paragraph ? Yes. > If so I would imagine the y to lineIndex procedure to be very common, no ? y to paragraphIndex is common, y to lineIndex is not currently used anywhere in RichTextFX. There's y to paragraphIndex and then hit test on the paragraph's TextFlow. > You should probably keep track if the editor is using a single font (and no wrap), therefore fixed line height. In which case y to lineIndex is nothing bug a single division. I need RichTextFX to work in general, not just for code editors with no line wrapping. > That is a very big (and common) performance gain in an editor (helps with scroll per line, page, hit test, scrollbar configuration, etc). I may employ optimizations for this special case, but if the general case is slow, it's still bad.
07-07-2014

>OK, but then I first need to determine the line at y. Do you use one TextFlow per line/paragraph ? If so I would imagine the y to lineIndex procedure to be very common, no ? You should probably keep track if the editor is using a single font (and no wrap), therefore fixed line height. In which case y to lineIndex is nothing bug a single division. That is a very big (and common) performance gain in an editor (helps with scroll per line, page, hit test, scrollbar configuration, etc).
07-07-2014

OK, but then I first need to determine the line at y.
07-07-2014

>If there was API for that, it would be great. In my current workaround I'm doing something similar: I get the caret shape for the insertion position and then If you are already getting the TextLine (TextLayout#getLines()) then you have a getBounds() in there. These bounds should be good to you, if I'm not wrong they included the line.x which accounts for line alignment. Note you can have the same problem of the left (before the last char), specially easy to verify if the text center or right aligned (or mirrored, for bidi text). I believe you should fix this bug using the lines bounds. Maybe you don't care for wrapped lines today, but that can change, and your code will be ready to handle it.
07-07-2014

> Then you will have the bug (showing the tooltip when the cursor is beyond the last char) for wrapped lines, no ? In the general case, yes. I need it for a code editor with no line wrapping, so I didn't care about wrapped lines. > It seems to me you want to intersect the x,y with lines bounds before doing using getHitInfo() ? > i.e, "if (x > lineWidth) return;" If there was API for that, it would be great. In my current workaround I'm doing something similar: I get the caret shape for the insertion position and then if(x > caretBounds.getMinX()) return;
07-07-2014

>I meant the change to only affect the end of the whole text, Then you will have the bug (showing the tooltip when the cursor is beyond the last char) for wrapped lines, no ? It seems to me you want to intersect the x,y with lines bounds before doing using getHitInfo() ? i.e, "if (x > lineWidth) return;"
07-07-2014

I meant the change to only affect the end of the whole text, i.e. the end of the last line if text is wrapped. Then, for placing the caret, "charIndex: n, isLeading: true", is correct as well, right? I am trying to get the character index under the mouse. (And display a tooltip whose content depends on the word under cursor, as in [1]. I don't want to display any tooltip if the mouse is beyond the end of line.) Here's the list of what other private API I use in RichTextFX: * I get the TextLayout instance from TextFlow by reflection. * I call getHitInfo() on TextLayout (this one is covered by JIRA issues). * I call getCaretShape() and getRange() mathods on TextLayout. * I call getLines() on TextLayout by reflection and use it to: * get the number of visual lines in a TextFlow; * get line number for a logical position in text; * get vertical center of a line * using this to implement getHitInfo(double x, int lineIdx), i.e. hitting the TextFlow at the given line and x coordinate. [1] https://github.com/TomasMikula/RichTextFX#custom-tooltips
04-07-2014

Personally I think that "charIndex: n-1, isLeading: false" is correct. If you click beyond the end line you should put caret at "charIndex: n-1, isLeading: false". You need to think about wrapping when considering this problem, the solution you proposed does not work when clicking on beyond the end wrapped line. Are you trying to place the caret ? Is not, what are you trying to do ? As for API, see: https://javafx-jira.kenai.com/browse/RT-29257 https://javafx-jira.kenai.com/browse/RT-29077 https://javafx-jira.kenai.com/browse/RT-8060 https://javafx-jira.kenai.com/browse/RT-26961 is there anything else you need not listed above ?
03-07-2014

Felipe to evaluate.
03-07-2014