RFR: 8370498: Improve how Node detects whether a layout property change requires a new layout pass [v4]
John Hendrikx
jhendrikx at openjdk.org
Thu Jan 15 19:28:45 UTC 2026
On Thu, 15 Jan 2026 18:32:39 GMT, Michael Strauß <mstrauss at openjdk.org> wrote:
> > This is why IMHO any layout that relies on convergence is already broken; it just happens to broken in a way that eventually leads to a stable result...
>
> A single top-down layout pass only works when the layout constraints are acyclic. However, JavaFX expressly supports cyclic constraints, so the layout system can't chicken out. As soon as there's a cyclic dependency (parent layout is constrained by child, child layout is constrained by parent), you've got a fixed-point problem and need to solve it in some form, for example with multiple layout phases or iteration.
>
> Cyclic dependencies can appear in several situations:
>
> * CSS/layout coupling: styling influences sizing, sizing influences styling
> * Parent size depends on child size
> * Child wraps based on parent width
> * Baseline alignment
> * ...
Are you referring to media queries for the window size when you say that sizing can influence styling? The others can be resolved similar to how `ScrollPane` does this (see below).
> Looking at how other frameworks do this, we see that WPF and Android use two layout phases: a constraint-carrying measurement pass (given some available size constraint, how much space would each node like to have?), and a second arrange pass that authoritatively tells each node how much space it is given.
I would say that there's also two passes in FX, although less obvious. The first pass is basically the calling of the compute methods. These cascade down all the way down to the leaf level as each compute will call child compute methods (but can use cached results) before any layout methods are called (apart from the root one). I'm not sure what that pass should be called, but it does sound a lot like what WPF is doing. Only after this does the layout pass cascade down again via the layout calls.
This is why some layout problems can sometimes be solved by overriding a compute method and doing some unorthodox work there (for which the correct mechanism is to install a pre-layout listener).
> Such a system can break some cycles, especially the kind where parent and child sizes mutually constrain each other. It's not a catch-all solution; there can be higher-level cycles, for example with a scroll bar that, upon appearing, causes the layout to reflow, which then again influences the scroll bar.
`ScrollPane` handles that in one pass. Basically it computes the size of the children, and if it determines there is need of a scroll bar, it does those calculations again (which can result in the appearance of the 2nd scrollbar, and the calculations are repeated one more time; this is quite visible in `ScrollPaneSkin`). In effect, it solves this problem internally without waiting for another layout pass, as compute methods can be called as often as desired.
> Android detects relayout requests during the layout phase, and will allow for one additional round of measure+layout in the same frame; if a second relayout request is encountered, it is deferred to the next frame.
>
> In JavaFX, we don't have two phases of layout to break the easier cycles, and we don't have multiple rounds of layout. I think it is obvious that we need to do something about that, and pretending that cyclic constraints don't exist is not that.
>
> What I believe can work for JavaFX is a layout system that basically does this for each frame:
>
> ```java
> while (!layoutClean) {
> doCssPass();
> doLayoutPass();
> }
> ```
>
> If this doesn't settle in a very small number of iterations (something like 3), we can do the remainder of the work in subsequent frames to prevent unresponsive applications. Maybe we can even detect oscillations (e.g. scroll bar flip-flop) and suspend layout in these cases.
I think doing a loop of layout passes up to a limit is fine, although I'm not sure if CSS needs to take part in that. One should not be changing styles during layout, that's a pre-layout concern (which is before CSS runs) and not something to do in the `compute` chain of methods nor the `layoutChildren` methods.
Of course, JavaFX being very unrestrictive, one can easily fiddle with CSS during layout calls, and create bad layouts, which hopefully converge, but at some point there is **nothing** that can be done. As these problems tend to be very visible, they're usually fixed quickly, unlike converging layouts which escape notice. This is why I think we should warn when certain actions are taken during layout. A layout that converges now, may stop converging when say `modena.css` is updated or replaced.
-------------
PR Comment: https://git.openjdk.org/jfx/pull/1945#issuecomment-3756499809
More information about the openjfx-dev
mailing list