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