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 22:41:05 UTC 2026
On Thu, 15 Jan 2026 20:48:57 GMT, Michael Strauß <mstrauss at openjdk.org> wrote:
> > 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).
>
> Yes, but also in a more general sense. Applications might also manually add or remove style classes depending on the size of controls.
True, and that's fine, but I think we need to warn if they do that during layout. That's just not the right time, as layouts are very restricted in what you should be doing. Most users never write a `layoutChildren` method and most aren't crazy enough to do these things in `compute` methods. They just need to be aware of things like pre/post layout listeners, and we need to ensure that user code is not called during layout (see below for cells...)
> That's true, but it's also less powerful than a dedicated two-pass approach. The main difference is that WPF/Android communicate down constraints to children during this first pass, asking: "given my available size, how large would you like to be?" The compute methods of JavaFX don't communicate any information from parents to their children, which is why children can't change their answer depending on the constraints imposed by their parents.
Yeah, FX has a limited variant of this with Content Bias. It can ask "If I give you 400 pixels of width, how much height do you want?"
> > `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.
>
> > 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.
>
> Let's assume a control where the number of children depend on its size. Maybe it arranges a set of nodes that it was provided with in some way, and fills the remaining "unused" slots in its layout with placeholder nodes. This implies that the control only knows how many placeholder nodes it needs after it was laid out. Adding those nodes after the layout pass is done means that they now don't have CSS styling. I think a case like this is already accounted for in the existing layout system, but solving this correctly in an iterative system probably means that CSS needs to be part of the loop.
Although I think I see why CSS needs to be added to the loop (and more), I still think that you should refrain from adding/removing children during layout, especially if they are cells. This still best handled in a pre-layout listener because if there is no follow-up loop, you will see CSS flicker (white background). Even in a looped system this can then occur when loops are exhausted.
The View classes are good examples of dynamic controls that need to have more or less children depending on their size. They should be initializing these children (including calling `Cell#updateItem`) as part of a pre-layout listener. This allows:
- Flicker free adding of children (CSS runs still, no need to rely on a next loop)
- User code doing whatever they want in `updateItem` which is not the case if that method is called during the layout phase (there are bugs currently where sometimes `updateItem` is called during layout.. very bad, as users are unaware that this is the case)
If it turns out there weren't enough (invisible) cells to fill the entire view port, then the View class can request another layout pass, which can be looped immediately (and should include calling the pre-layout listeners again). So I think you are right here that we should include CSS in the loop so newly added children in a 2nd loop also get the correct styles (hopefully it's cheap if not much changed). So I think it perhaps could work like this:
1. Pulse starts
2. Pre-layout listeners called
- View controls add/remove children, call `updateItem` based on their current size
- `updateItem` can do whatever it wants, including modifying styles, adding/removing children, user code need not worry about layout semantics
3. CSS pass
LAYOUT STARTS -- refrain from doing "more" work besides positioning/sizing
4. Sizes get computed (by `resizeRootToPreferredSize`) down to leafs
5. Layout methods are called (according to dirty status) down to leafs
LAYOUT ENDS
6. Either during layout or in a post-layout listener, the View class discovers it did not have enough cells, it asks for a new layout
7. After post layout listeners have run, check if another layout is needed immediately, go to step 2 (unless limit exceeded)
8. Pulse ends
In this way we can prevent unnecessary layouts, and also prevent doing dangerous things during layout.
-------------
PR Comment: https://git.openjdk.org/jfx/pull/1945#issuecomment-3757227855
More information about the openjfx-dev
mailing list