<div dir="ltr">I determined this is caused by the StackPane's child node's layoutY value sometimes being 0.0 and sometimes being 7.5.  Which is strange, since layout should always be complete on the TreeCells by the time they are painted.<div><br></div><div>Which got me thinking.. sure maybe on one pass it wasn't initialized or something, but why would it keep changing?</div><div><br></div><div>Then I learned that updateItem is being called much more often than I had thought.  I had previously thought it was only called when the TreeCell was meant to render a new or different TreeItem.  Apparently it is called when the TreeItem selection changes.  I'm not sure why, as no parameters to updateItem include the selection state, so a TreeCell doesn't know that is why it is being called, and the documentation and sample code doesn't mention it.</div><div>In fact the documentation for Cell isItemChanged specifically states:</div><div><br></div><div>     * This method is called by Cell subclasses so that certain CPU-intensive<br>     * actions (specifically, calling {@link #updateItem(Object, boolean)}) are<br>     * only performed when necessary <b>(that is, they are only performed<br>     * when the currently set {@link #itemProperty() item} is considered to be<br>     * different than the proposed new item that could be set).</b><br></div><div><br></div><div>Which implies to me (along with the method name "updateItem") that assigning a different item to the Cell is the only reason for updateItem to be called.</div><div>This is clearly not the case.</div><div><br></div><div>Another observation is that, while changing the selected item such that I observe the misalignment, the last time getBaselineOffset is called for any particular cell while processing that event, it always returns the same value of 7.5, but it seems it doesn't always use this value for when it paints.</div><div>E.g. if I toggle the selection of a cell, updateItem is called once, creating new Nodes for the Cell's graphic, and getBaselineOffset for the StackPane is called 4 times.  The first 3 times the first managed child of the StackPane has layoutY = 0.0, the last time it is 7.5.</div><div><br></div><div>There's a bug here somewhere...</div><div><br></div><div>Scott<br><div><br></div></div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Jan 24, 2023 at 1:43 PM Scott Palmer <<a href="mailto:swpalmer@gmail.com">swpalmer@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr">I'm seeing something odd with the alignment of content in a TextFlow and I'm not sure if I'm doing something wrong, or if there is a bug there.<div>The getBaselineOffset() method of the StackPane is returning inconsistent values.  If I sub-class it to force a constant offset, everything works.  Since the StackPane content is always the same, I would expect getBaselineOffset to always return the same value, but in my sample program (below) sometimes it returns 6.5 and other times it returns 14.0. (Tested on macOS 13.2 w. OpenJDK 19/OpenJFX19)</div><div>Parent.getBaselineOffset() is documented as: "Calculates the baseline offset based on the first managed child." - which should always be the same in my example. Sure enough, I checked and getChildren().get(0).getBaselineOffset() is always returning the same value (13.0) - so where is the discrepancy coming from?</div><div><br></div><div>Possibly related to <a href="https://bugs.openjdk.org/browse/JDK-8091236" target="_blank">https://bugs.openjdk.org/browse/JDK-8091236</a></div><div><br></div><div>The following program demonstrates the issue.  While selecting things in the TreeView the TextFlows are repainted with different alignment between the StackPane node and the Text nodes. <br><div><br><font face="monospace">package bugs;<br><br>import javafx.application.Application;<br>import javafx.scene.Scene;<br>import javafx.scene.control.ContentDisplay;<br>import javafx.scene.control.Label;<br>import javafx.scene.control.TreeCell;<br>import javafx.scene.control.TreeItem;<br>import javafx.scene.control.TreeView;<br>import javafx.scene.layout.StackPane;<br>import javafx.scene.layout.VBox;<br>import javafx.scene.paint.Color;<br>import javafx.scene.shape.Circle;<br>import javafx.scene.shape.Polygon;<br>import javafx.scene.text.Text;<br>import javafx.scene.text.TextFlow;<br>import javafx.stage.Stage;<br><br>public class TextFlowWithIcons extends Application {<br>    public static void main(String[] args) {<br>        launch(args);<br>    }<br><br>    @Override<br>    public void start(Stage primaryStage) {<br>        TreeItem<String> node1 = new TreeItem<>("item");<br>        TreeItem<String> node2 = new TreeItem<>("text");<br>        TreeItem<String> node3 = new TreeItem<>("tree");<br>        TreeItem<String> root = new TreeItem<>("ROOT");<br>        root.setExpanded(true);<br>        root.getChildren().addAll(node1, node2, node3);<br>        var treeView = new TreeView<String>(root);<br>        treeView.setCellFactory(this::cellFactory);<br>        VBox box = new VBox(8, new Label("Fiddle with the TreeView...\nWatch the alignment bounce around."), treeView);<br>        var scene = new Scene(box);<br>        primaryStage.setScene(scene);<br>        primaryStage.setTitle("Graphic Alignment Issue");<br>        primaryStage.show();<br>    }<br><br>    private TreeCell<String> cellFactory(TreeView<String> tv) {<br>        return new CustomCell();<br>    }<br><br>    private static class CustomCell extends TreeCell<String> {<br><br>        public CustomCell() {<br>            setContentDisplay(ContentDisplay.GRAPHIC_ONLY);<br>        }<br><br>        @Override<br>        protected void updateItem(String item, boolean empty) {<br>            super.updateItem(item, empty);<br>            if (item != null && !empty) {<br>                var text1 = new Text("Some ");<br>                var text2 = new Text("colored ");<br>                var text3 = new Text(item);<br>                text2.setFill(Color.GREEN);<br>                var circle = new Circle(6);<br>                circle.setStroke(Color.BLACK);<br>                circle.setFill(Color.RED);<br>                var triangle = new Polygon(-6, 6, 6, 6, 0, -6);<br>                triangle.setFill(Color.YELLOW);<br>                triangle.setStroke(Color.BLACK);<br>                triangle.setScaleX(0.5);<br>                triangle.setScaleY(0.5);<br>                triangle.setTranslateX(4);<br>                triangle.setTranslateY(4);<br>                var icon = new StackPane(circle, triangle)</font></div><div><font face="monospace">// uncomment to fix</font></div><div><font face="monospace">//                {<br>//                    @Override<br>//                    public double getBaselineOffset() {<br>//                        return 12;<br>//                    }<br>//                }<br></font></div><div><font face="monospace">                ;<br>                var textFlow = new TextFlow(icon, text1, text2, text3);<br>                setGraphic(textFlow);<br>            } else {<br>                setGraphic(null);<br>            }<br>            setText(null);<br>        }<br><br>    }<br>}</font><br></div></div><div><font face="monospace"><br></font></div><div><font face="arial, sans-serif">Regards,</font></div><div><font face="arial, sans-serif"><br></font></div><div><font face="arial, sans-serif">Scott</font></div><div><font face="arial, sans-serif"><br></font></div></div>
</blockquote></div>