I haven't taken a close look, but I've done some automated layout work before. The baseline layout seems to be an interesting kind of problem where controls can influence each other, where normally they are mostly independent.

An iterative approach may be the only viable one, but it does leave me thinking if there isn't a better option. My primary concern is that it is relatively easy to construct a situation that would not converge quickly.

Given a fixed height control A with some text that is top aligned and which has a significant amount of padding at the bottom (maybe it has an image below the text), and another unbounded control B with its text vertically centered. Every time the algorithm tries to align the baselines of these two controls, the container grows a little bit bigger. This enlarges control B, pushing the baseline down (as it is centered), which in turn means that aligning control A on that new baseline will mean its bottom padding now extends beyond the containers size, and the container is enlarged again... Visually:

  +-------+ +-------+
  |   A   | |       |
  |       | |   B   |
  |       | |       |
  +-------+ +-------+

After 1 iteration:

            +-------+
  +-------+ |       |
  |   A   | |       |
  |       | |   B   |
  |       | |       |
  +-------+ +-------+

After 2 iterations:

            +-------+
            |       |
  +-------+ |       |
  |   A   | |   B   |
  |       | |       |
  |       | |       |
  +-------+ +-------+

In ASCII art this converges relatively fast, but it all depends on the size of control A and how large its bottom padding is.

I was wondering if this can be improved upon somehow.

--John

On 05/11/2021 03:31, Michael Strauß wrote:
I previously proposed a new iterative layout algorithm [1] that
supports baseline alignment and introduces new APIs to give developers
control over the way nodes are aligned. This is a solution to the
long-standing problem that JavaFX cannot reliably lay out nodes that
are aligned on their baseline [2]. The new layout algorithm might also
fix some issues where the scene graph layout only settles after
interacting with the controls (for example, by clicking).

I've created a small application that shows the new APIs and a
correctly working baseline-aligned layout [3]. In addition to that, I
also built SceneBuilder with both the old and new layout system, and
played around with it to find out whether there were any regressions
or visual differences. So far, I haven't found any.

In order to move this forward, I think it would be a good idea to test
the latest version of the new layout system in more real-world JavaFX
applications. Any help from JavaFX application developers is greatly
appreciated. It's as easy as checking out the JavaFX sources from the
PR [1], building a local SDK and linking your application with the
binaries.


Finally, here's a high-level overview of the new algorithm:

When Parent::layout() is called on a layout root (i.e. a scene root or
an unmanaged node), it will lay out its children in a loop until the
scene graph under the layout root is fully laid out, which means it is
clean and doesn't require further layout. The totality of layout
activity for a single layout root is called "layout cycle". A layout
cycle will often take a few layout passes to finish (but not more than
2 in most cases). There is no limit on how often Parent::layout() will
iterate to lay out its children, so in principle, this could lead to
an infinite layout loop.

One source of infinite layout loops are incorrectly implemented controls:

    class PathologicalControl extends Region {
        final Text text = new Text("foo");

        PathologicalControl() {
            getChildren().add(text);
        }

        @Override
        protected void layoutChildren() {
            text.relocate(0, text.getLayoutY() + 10);
        }
    }

In this example, each call to layoutChildren() moves down the text
node another 10 pixels from where it was, which causes the layout
algorithm to schedule yet another layout pass. It's an infinite loop.

The layout system detects this by tracking how often a node
invalidates the scene graph in a single layout cycle. If a node
exceeds a threshold of 100 invalidations, it will be suspended from
layout and can no longer invalidate the scene graph in the current
layout cycle. A warning will be logged to notify developers of that
fact.


[1] https://github.com/openjdk/jfx/pull/433
[2] https://bugs.openjdk.java.net/browse/JDK-8090261
[3] https://github.com/mstr2/jfx-layout-sample

Reply via email to