On Wed, 7 Jan 2026 21:21:14 GMT, John Hendrikx <[email protected]> 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
* ...

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.

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. 
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:

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.

-------------

PR Comment: https://git.openjdk.org/jfx/pull/1945#issuecomment-3756305694

Reply via email to