I've run into something similar, maybe even the same issue.
My issue was that modifying the list of cell children (in a Skin for
ListView) caused a 1 frame white flicker (my application is black, so it
was annoying) because no CSS was applied yet to those newly added
children. This occured when the ListView first only had a few children
and wasn't completely filled, and then switching to a list which
required more cells to be created. I couldn't find a "correct" place to
modify the list of children that would avoid the flicker
(layoutChildren/computePrefWidth are places I tried).
In the end I did this, and added a comment to remind me why the hack was
there:
/*
* A pre-layout pulse listener is added to the current Scene to
manage the
* cells before the CSS pass occurs (this could also be done with
an AnimationTimer).
*
* If cells are not managed before the CSS pass, new cells will be
rendered for
* one frame without CSS applied. This results in a visual artifact
(a white flash
* for example if the background is supposed to be dark, while
white is the default
* color without any CSS applied).
*/
private final Runnable pulseListener = () -> {
int lines = vertical ? visibleColumns.get() : visibleRows.get();
int firstIndex = (int)(scrollPosition.get()) * lines;
content.manageCells(firstIndex);
};
Now, the reason I think this may be same issue is that you're also doing
a modification of the children list during layout: setting the graphic
is sort of equivalent to label.getChildren().add(graphic)
--John
On 20/02/2023 20:58, Scott Palmer wrote:
I'm seeing an odd issue with using CSS to colour a Shape when I have
it as a child of a TextFlow.
My use case is a "rich" text Cell in a tree or list. I want to have
the text portion in multiple colours and so instead of using the
graphic and text parts of a typical Cell, I'm using only the Graphic
and setting it to a TextFlow to get the text colours. This means that
I lose the ability to set a graphic independen to the text, and so the
graphic is also added to the TextFlow.
To style the graphics, which are made of simple shapes, I'm using
CSS. It makes it easy to adapt the colours for when a cell is
selected etc. What I've noticed is that as the cell selection moves
around the Shape component of the TextFlow flickers, as if it is drawn
first using default colours and then the CSS is applied afterward. I
tried working around this by explicitly calling applyCss() on the
Shape from the Cell's update method, but it did not help. If I
explicitly set the Stroke and Fill via the Shape API the flickering
does not occur. If I use CSS, but set the Shape as the Cell's graphic
and resort to only having plain text, there is no flickering.
A test program that demonstrates this is below.
Bug or known limitation?
Scott
------------------------------------
package bugs;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
public class CSSFlickerInTextFlow extends Application {
public static void main(String[] args) {
launch(args);
}
private CheckBox onlyCssCB;
@Override
public void start(Stage stage) throws Exception {
ListView<String> list1 = new ListView<>();
ListView<String> list2 = new ListView<>();
var items1 = list1.getItems();
var items2 = list2.getItems();
for (int i = 1; i < 23; i++) {
var x = "Item #"+i;
items1.add(x);
items2.add(x);
}
list1.setCellFactory(t -> new MyCell1());
list2.setCellFactory(t -> new MyCell2());
onlyCssCB = new CheckBox("Use only CSS (shapes will flicker in
TextFlow)");
HBox buttons = new HBox(8, onlyCssCB);
buttons.setPadding(new Insets(4));
Tab custom = new Tab("Everything in Graphic", list1);
Tab withoutColoredText = new Tab("Graphic + Text", list2);
TabPane tabs = new TabPane(custom, withoutColoredText);
VBox root = new VBox(
new Label("""
Focus in list, cursor up and down, pay
attention to the circle.
Try the same with the box checked - circle
flickers.
Doesn't happen when not using the TextFlow.
"""),
buttons, tabs);
root.setPadding(new Insets(4));
var scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Flickering with CSS in TextFlow");
stage.show();
}
private Shape makeGraphic() {
Shape graphic = new Circle(6);
if (!onlyCssCB.isSelected()) {
graphic.setFill(Color.SALMON); // CSS overrides these but
causes flickering if not the same
graphic.setStroke(Color.BLACK);
}
graphic.setStyle("-fx-fill: salmon; -fx-stroke: black;");
return graphic;
}
class MyCell1 extends ListCell<String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
setText(null);
if (!empty && item != null) {
TextFlow flow = new TextFlow();
Shape graphic = makeGraphic();
graphic.setTranslateY(2);
var nodeList = flow.getChildren();
Text name = new Text(item + " : ");
name.setStyle("-fx-fill: -fx-text-background-color;");
Text extra = new Text("with Color");
extra.setStyle("-fx-fill:ladder(-fx-background, white 49%, salmon 50%);");
nodeList.add(graphic);
nodeList.add(new Rectangle(4,0)); // gap
nodeList.add(name);
nodeList.add(extra);
setGraphic(flow);
} else {
setGraphic(null);
}
}
}
class MyCell2 extends ListCell<String> {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
setGraphic(makeGraphic());
setText(item);
} else {
setText(null);
setGraphic(null);
}
}
}
}