I saw you resolved the issue already in your next mail.

As you're modifying the Cell when it is being updated, it won't be so easy to do the same thing I'm doing.  For me, the problem was that adding additional cells only when they're needed is done during `layoutChildren`, which is after CSS has been applied. Any new children therefore would temporarily have no CSS applied to them.  If those children happen to be in a very visible location (like in the center of the screen) you'd see a flash (this can happen when a list contains 5 children, but the visible area can hold 20; if the list is changed to hold more than the 5 existing children, the newly added items would have a brief flash).  By moving the code that adds new children to a pulse listener, I can add the children before CSS is applied and before `layoutChildren` is called.  I suspect this "problem" may be present in more cell based skins, but it is not often noticed (white is often a standard background color, and the flash is very brief when JavaFX runs at 60 fps as it is only there for 1 frame).

I saw you resolved the problem by calling applyCSS, but there may be an alternative; if you could always put a graphic on your cell (ie, don't ever set it to `null`) but when you want it hidden just change the cell Labeled#contentDisplay to ContentDisplay.TEXT_ONLY, you may be able to avoid the graphic needing to be added/removed from the node.  Note however that you can't share graphics between Nodes, so make sure they all have their own instance.  Other ways may also work (setting the graphic node to invisible/unmanaged, etc..)

This may make it possible to avoid having to call applyCSS.

--John

On 22/02/2023 01:37, Scott Palmer wrote:
I agree, this seems like it could be the same issue.  Could you give me a little more context as to how this code works? I'm not implementing my own skin (maybe I should, I've never tried that before).  Is this something I can trigger without a custom skin?

Regards,

Scott.

On Tue, Feb 21, 2023 at 12:01 PM John Hendrikx <john.hendr...@gmail.com> wrote:

    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);
                }
            }
        }
    }

Reply via email to