CSS styling of Shape in TextFlow causes flickering
John Hendrikx
john.hendrikx at gmail.com
Tue Feb 21 17:01:28 UTC 2023
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);
> }
> }
> }
> }
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/openjfx-dev/attachments/20230221/b922e984/attachment-0001.htm>
More information about the openjfx-dev
mailing list