Issue
What i have so far
I was trying to implement a multi-line TextInput with javaFx, one that is capable of displaying emojis, I used a VBox of FlowPanes (a FlowPane for each line), split the line by spaces into words, the words are displayed in an HBox and the HBox will contain Text nodes for text and ImageView for Emojis, The current setup is described in the following image
The following screenshot shows what i have so far
The problem
The problem i'm facing is with the complex word, when multiple emojis (ImageViews) are displayed in an HBox, the Carret position estimation will be wrong by half a pixel for each image (because i'm assuming the width of the HBox would be equal to the sum of the fitWidths of its children ?, but it's not), as if the HBox has some kind of spacing although the spacing property is set to 0.
the effect gets worse if a lot of emojis are shown together, as shown in the following screenshots
It's also not Padding, or Borders, any help would be appreciated, and i'm sorry for the long boring explanation, i had to add it in case it would help anyone help me resolve the issue.
Solution
After a lot of investigation, it turns out to be caused by me setting the fitWidth of the child ImageViews to decimal values (non-integer), the ImageView accepts a double as the fitWidth but it seems to be rounded to the closest greater integer when rendering, calling the getFitWidth() method would still return the double you have set but the Parent will lay them out with the rounded value (because i guess a physical pixel can't display half a pixel right ?).
So calling getWidth() on the HBox parent would return a greater value than the sum of the fitWidths of the child ImageViews ONLY IF the fitWidths of the ImageViews are non-integer.
This can be tested using the following piece of code
ArrayList<Image> images = new ArrayList<Image>();
//Fill the list with N images
StackPane root = new StackPane();
root.setPadding(new Insets(15));
HBox parent = new HBox(0);
for (Image image : images) {
ImageView view = new ImageView(image);
view.setPreserveRatio(true);
view.setFitWidth(fitWidth);
parent.getChildren().add(view);
}
root.getChildren().add(parent);
ps.setOnShown(event -> {
double sum = 0;
for (Node node : parent.getChildren()) {
sum += ((ImageView) node).getFitWidth();
}
System.out.println("fitWidth : " + fitWidth);
System.out.println("Sum of fitWidths of child ImageViews : " + sum);
System.out.println("Width of the parent HBox : " + parent.getWidth());
});
ps.setScene(new Scene(root));
ps.show();
Running this with a fitWidth of 31.5 and 32.0 gives the following results
fitWidth : 31.5
Sum of fitWidths of child ImageViews : 315.0
Width of the parent HBox : 320.0
fitWidth : 32.0
Sum of fitWidths of child ImageViews : 320.0
Width of the parent HBox : 320.0
Notice that the width of the parent is the same but the sum of fitWidths isn't, because the value is rounded at some point during layout or render, which caused the issue described in the question.
Solving this would really differ from one context to another, i solved it by casting the double to an int when setting the fitWidth but it's really up to the developer.
Answered By - SDIDSA
Answer Checked By - Timothy Miller (JavaFixing Admin)