JDK-8305001 : TextFlow.caretShape() ignores inline Nodes
  • Type: Bug
  • Component: javafx
  • Sub-Component: graphics
  • Affected Version: jfx21
  • Priority: P3
  • Status: Resolved
  • Resolution: Not an Issue
  • Submitted: 2023-03-27
  • Updated: 2023-05-04
  • Resolved: 2023-05-04
Related Reports
Blocks :  
Blocks :  
Relates :  
Description
TextFlow.caretShape() produces incorrect caret paths in the presence of inline Nodes, as can be seen using the Monkey Tester TextFlow Page (see attached screenshots).

The code that visualizes caret shape iterates over the length of text, appending PathElements obtained from caretShape() for each position in the text:

TextFlow control = ...
            for(int i=0; i<len; i++) {
                PathElement[] es = control.caretShape(i, true);
                caretPath.getElements().addAll(es);
            } 

To reproduce, select Text: "Inline Nodes" and check 'show caret path'.

Monkey Tester:
https://github.com/andy-goryachev-oracle/Test/blob/main/src/goryachev/monkey/MonkeyTesterApp.java
Comments
Thank you for investigating this issue! You are absolutely correct - there are two issues that led to creation of this ticket: 1. the test page was creating shapes prematurely, before the layout pass had a chance to size the embedded nodes. If we modify the code to query for caretShape() after the layout pass, the problem goes away 2. computeTextLength() is indeed incorrect. It looks like TextLayout is treating embedded nodes as having text with length of 1 character closing the ticket.
04-05-2023

Inline nodes aren't necessarily ignored by TextFlow - in MonkeyTester case the text and button Nodes are created by createTextArray(), added to TextFlow and immediately used to cacluate possible caret positions via TextFlow.caretShape(). Because these Nodes (in this case Buttons) are "fresh", they have no skin data attached to them yet as we didn't go through a layout pass yet. That means, when computing min/pref width and height we have no skin data to refer to, which makes the Buttons return 0 min/pref width and height. In other words, caretShape() does not ignore the nodes, but rather acquires Nodes' dimensions as 0x0, which desyncs the caret positions. When we let a layout pass go (ex. display even one frame), the nodes will go through CSS processing which will apply a skin to them. That will also invalidate TextFlow's cache and make it properly calculate caret positions - I checked it by adding a Button which re-acquires data from TextFlow.caretShape() without recreating the Text and Button Nodes and the positions line up properly. I also noticed a small bug in MonkeyTester itself, in TextFlowPage the computeTextLength() method only counts Text Nodes into calculations, which with inline Button Nodes skips some last caret positions when fetching them from TextFlow.caretShape().
04-05-2023

Reproduced it on Windows and macOS with latest master branch.
13-04-2023