Hi Andy,

Thanks for having a thorough look at the proposal!

The thing with Swing vs JavaFX here is that Swing's panes are a lot
lighter, so having a large nested tree of them, even for trivial things,
is hardly ever an issue.  However, in FX, every Pane brings in
properties, CSS, a Scene graph peer (which needs synchronization),
transforms, effects, etc -- it is quite fair to say that JavaFX panes
are a **lot** heavier than the Swing equivalents.

I'm pretty sure this is why Skins don't use any layout container Nodes
if they can avoid it, and why Skins stopped using a Skin Node, even
though that would be a much nicer abstraction (ie. instead of the Skin
manipulating the children of the Skinnable, there is only a single Node
added to the Skinnable, and the Skin then manipulates the children of
the Node it provided).

I will see if I can make a proof of concept PR for this, as that may
help to see all the details.  I'll try to include at least one container
converted to use a virtual layout to make it even more clear how all of
it should work -- that will entail a bit more work as many of the "core"
algorithms that FX layouts use are hidden in protected methods on Region
(which a virtual layout can't reach).  These can however be moved to a
helper (which can be kept private for now), and Region can then delegate
there (to avoid duplication).

--John

On 16/07/2026 23:41, Andy Goryachev wrote:
> As a former Swing dev, I feel comfortable nesting various panes, or
> use my own CPane (a mix of BorderPane with an inner GridPane), but I
> do see the point of separating the layout from the Node altogether.
>  Not only it reduces the memory footprint, but also speeds up CSS
> processing.  And I don't see any other obvious pitfalls.
>
> We would have to look at the details - how to get the constraints and
> how to pass the render parameters (scale, etc.), but generally I
> support this proposal.  Thank you for the patience and explanations.
>
> -andy
>
>
>
>
> *From: *Marius Hanl <[email protected]>
> *Date: *Thursday, July 16, 2026 at 13:20
> *To: *John Hendrikx <[email protected]>; Andy Goryachev
> <[email protected]>; [email protected]
> <[email protected]>
> *Subject: *Re: [External] : Proposal to split off layout and layout
> metric methods from Node to interfaces it implements
>
> This Message Is From an External Sender
> This message came from outside your organization.
> Report Suspicious
> <https://us-phishalarm-ewt.proofpoint.com/EWT/v1/ACWV5N9M2RV99hQ!N727doPOsOLk1ZPh99KUGFxSAPonZG2OpRgnfIayjZjm2sMdFNKyEO4PdWQHj7VZBpLExxT5Tadk_SQaamSEhJY-Uckr0pIGmQ$>
>  
> I would like to add that not just that Node is heavy, every
> intermediate Pane also need to be processed by CSS, may receive a
> CssStyleHelper (e.g. to hold triggerStates) and counting in to the
> depth. Since this is currently the area I'm looking in, I thought this
> would be worth to add to the discussion. I still think improving the
> memory footprint of Node is a good thing. But that is a separate
> problem IMO. -- Marius Am 16.07.2026 um 20:35 schrieb John Hendrikx: >
> On 16/07/2026 17:26, Andy Goryachev wrote: >> I see, you want to avoid
> the overhead of the intermediate pane(s), >> mainly because the Node
> class is too heavy. > Exactly, and not only that, to avoid the
> overhead of a graphic peer > (HBox's are still Regions), CSS styling,
> another layer to go through for > dispatching/bubbling for events,
> several dozen fields, and many > unavoidable properties that are
> getting created.  A Node can easily > consume upwards of a few kilo
> bytes to do "nothing". > >> I wonder if a less invasive solution for
> that problem would be to put >> the rarely used properties into a hash
> table instead, something along >> the lines of
> https://urldefense.com/v3/__https://github.com/andy-goryachev-oracle/jfx/pull/20__;!!ACWV5N9M2RV99hQ!M7Gznm-D9Ls80kO46mnkiWonaVdBTWnXxm1PcjXB6s55b3oDzqknwYXz3hiZepAPaBSshBFcXXa8jQJmMb5zQ_Jg$
>  .
> > I don't see how that would be a less invasive solution, considering
> > you'd be introducing a hash map lookup to find properties.  It also
> > doesn't solve the problem; a layout container would still have to be
> a > Node, and no matter how many properties you stash in that map, it
> will > still consume on the order of 10-30x more memory than a
> non-Node layout > container. > >> This way we'd eliminate most of the
> overhead and do it without adding >> any public APIs (apart from
> exposing the layout classes once they >> mature).  The reason I
> dislike the idea of Layoutable is because it >> impacts all the Node's
> descendants, even though conceptually it only >> belongs to Parent. >
> That's not quite correct, every Node, including one without children,
> > like Shape (not a Parent), Subscene, ImageView, etc, can and must >
> participate in layout.  Every Node has: > > - resizable + resize +
> resizeRelocate methods > - minWidth/prefWidth/maxWidth etc. > - layout
> constraint > - layout x/y > > Parent only governs a potential list of
> children, and governs some flags > to determine when the layout system
> should be triggered, but that's > orthogonal -- these flags would not
> be part of the proposed interface. > Parent has nothing to do with
> whether it can participate in a layout, as > all Nodes already can. 
> The children of an HBox are in > ObservableList<Node> which basically
> means anything can participate in a > layout, including shapes,
> subscenes, imageviews, etc. > > The Layoutable interface simply marks
> something as "being able to > participate in layout".  It is not a
> marker for a layout algorithm(!). > It just so happens though that
> layout containers themselves (ones that > can do layouts) also
> themselves can participate in a "grander" layout, > just like an HBox
> can contain more HBoxes. > > Remember how this started: > > - We
> discover bugs in LabelSkinBase / TitledPane etc with regards how >
> they handle graphic Nodes that use more advanced features (like
> min/max > widths, content bias, alignment).  I can put a full scene
> graph in the > "graphic" part of a Label (although often a HBox is
> sufficient), and > expect that to be handled correctly without bugs. >
> > - We find out that these classes badly duplicate the code that is in
> > HBox, taking shorts cuts like only called `prefWidth(-1)` on the
> graphic > node and assuming that is sufficient. > > - The reason for
> that was simple: HBox is too heavy; having a HBox or > VBox in every
> Labeled would create a massive amount of additional nodes, > it made
> sense to optimize those away, just like it made sense to change > the
> Skin API to no longer require that it supplies a Node (it still >
> works if you do, but none of the FX skins do it that way anymore,
> again, > it would be wasting precious Nodes). > > - We discuss how it
> would be nice if the algorithm that HBox has could > be provided
> without having to use an HBox, so these classes don't have > to poorly
> duplicate very complicated layout logic > > We're now here with a
> proposal that almost couldn't be simpler: > introduce an interface so
> both Nodes and a light-weight layout > containers can have a common
> ancestor, and have Node implement that; for > Node it would literally
> mean: > > - Copy a dozen related method signatures + documentation to
> a new interface > - Add `@Override` to the affected methods in Node,
> and strip their > documentation (that is on the interface now, you
> won't notice in Javadoc) > - The change is 100% source and binary
> compatible, and even has no > performance impact if calling these
> methods via a Node reference > > Even in core-libs they've done this
> several times, like introducing >
> SequencedCollection/SequenceMap/SequencedSet interfaces, or the >
> AbstractStringBuilder which became the new ancestor of StringBuffer
> when > StringBuilder was introduced. > > Adding such an interface
> would simply mark Node as something that "can > participate in
> layouts", which is already exactly what a Node is (as it > has hard
> coded all those methods on it already). > > --John > > >> -andy >> >>
> >> >> >> *From: *John Hendrikx <[email protected]> >> *Date:
> *Thursday, July 16, 2026 at 03:37 >> *To: *Andy Goryachev
> <[email protected]>; >> [email protected]
> <[email protected]> >> *Subject: *Re: [External] : Proposal to
> split off layout and layout >> metric methods from Node to interfaces
> it implements >> >> This Message Is From an External Sender >> This
> message came from outside your organization. >> Report Suspicious >>
> <https://us-phishalarm-ewt.proofpoint.com/EWT/v1/ACWV5N9M2RV99hQ!NB26dgOv2uIPHxPgvfKeeB8wifO7UNM_qPE-ApXJee7-lnArw9ivFU3vA1OKWzk7SOFWi4LZ-I0apqiGweab_xqLu69yFJ24pTitxw$
> > >> >> >> On 15/07/2026 20:10, Andy Goryachev wrote: >> >> Right,
> static layouts will only work in simple cases, the more >> complicated
> ones will require an instance. >> >> My point is that if we are laying
> out Nodes, there is no need to >> invent another abstraction layer
> because Nodes contain all the >> required data.  This will eliminate
> the "Bonus" section which some >> seem to like. >> >> The Bonus
> section is bonus, all else is still needed. >> >> The problem is:
> we're not laying out only Nodes.  We're also laying >> out potentially
> light-weight layout containers (that purposely are not >> Nodes).  So
> the list of things to lay out can contain a mix of Nodes >> and
> Layouts.  A layout acts as a Node, but isn't one. >> >> Let's take my
> example: A TitleSubtileControl that can have a Graphic >> on the left
> and on the right two lines of text one above the other >> (title and
> subtitle) >> >> - The TitleSubtileControl is a normal Node, that wants
> to avoid having >> a nested HBox and VBox so it creates: >>   - A
> HBoxLayout within it nested a VBoxLayout and a Graphic (a Layout >>
> and a Node) >>   - A VBoxLayout within it nested a Title and a
> Subtitle Text (two Nodes) >>   - It adds only the Nodes to the
> scenegraph as its children (so: >>
> titleSubtitleControl.getChildren().addAll(graphic, title, subtitle) >>
> >> Now the problem is, HBoxLayout needs to accept a VBoxLayout (not a
> >> Node) and a Graphic (a Node).  The only way to do that is to store
> >> them as Objects since they don't share a common ancestor;
> VBoxLayout >> can't be a Node because that would pull in a zillion
> private fields >> and properties making it "heavy". >> >> The solution
> I presented is to create this common ancestor, which I've >> dubbed
> Layoutable. It contains methods that only layouts need (like >>
> VBoxLayout or VBox itself).  These methods already exist on Node, >>
> they're just not grouped as part of an interface it implements.  Doing
> >> such a change is source and binary compatible. >> >> So the end
> state is: >> >> - VBoxLayout implements Layoutable (but is not a
> Node!) >> - Node implements Layoutable >> >> When VBoxLayout and a
> Node are nested in HBoxLayout, the HBoxLayout >> can treat both as
> Layoutable's making Layouts clean (there would have >> to be a million
> instanceof checks otherwise, or we'd need to wrap >> Nodes in a
> LayoutableWrapper). >> >> Sorry for the confusion with the bonus
> section, I hope this clarifies >> why it would a good move to have a
> common ancestor for Nodes and Layouts. >> >> --John >> >> >> >> I
> guess what I am trying to say is that code reuse (and removal of >>
> hard-to-maintain code replication) should be the primary goal, >>
> while adding new features (Canvas) should come last, if ever. >> >>
> -andy >> >> >> *From: *John Hendrikx <[email protected]> >>
> *Date: *Wednesday, July 15, 2026 at 10:49 >> *To: *Andy Goryachev
> <[email protected]>; >> [email protected]
> <[email protected]> >> *Subject: *Re: [External] : Proposal to
> split off layout and >> layout metric methods from Node to interfaces
> it implements >> >> This Message Is From an External Sender >> This
> message came from outside your organization. >> Report Suspicious >>
> <https://us-phishalarm-ewt.proofpoint.com/EWT/v1/ACWV5N9M2RV99hQ!NB26ligkesgE1bPqfVnUuELFLOB1vmX7gUMnmgafz5PcT3tCIOQ6CH3Y_2TlHn6_oY4aATre-aOhU774w6HiWg4mLvHIFUYvM_SnXw$
> > >> >> >> >> On 15/07/2026 19:32, Andy Goryachev wrote: >> >> * >>
> Yes, they don't need to be public immediately, although >> they
> definitely can be as they're nothing more than a >> "copy" of the
> layout panes we already have, but packaged >> without the Node
> dependency.  We would need pretty quickly >> the Node implementing the
> Layoutable interface (which >> opens the path for people writing their
> own virtual layouts). >> >> >> >> One thing I wanted to mention is
> that these layout classes can >> probably be static (unless they need
> to keep some internal >> state like HBox.tempArray).  If I understand
> the logic, there >> is no need for the Layoutable interface either,
> the owner Node >> is merely delegates layoutChildren() to the
> corresponding >> layout class, passing all the necessary information
> (scale, >> snap, etc.) >> >> If you want virtual layouts to compose,
> you need to be able to >> point at something.  Let's say you have a
> simple layout: >> >>     HorizontalLayout[ Graphic + Text ] >> >> This
> can be relatively static, agreed. >> >> However, if you want something
> more complicated: >> >>     HorizontalLayout[ Graphic +
> VerticalLayout[Title + Subtitle] ] >> >> ...then how would the static
> call look like to HorizontalLayout? >> >> Furthermore,
> HorizontalLayout would need to accept both Node and >> Layout somehow
> if they don't implement a common interface (so >> Object[] ?) >> >>
> Also, layouts often have a ton of parameters to tweak: padding, >>
> alignment, spacing, margins, snapToPixel, etc... that's a lot of >>
> things to pass in to a static layout.  This quickly invites a >>
> "parameter" object, at which point you can just make the Layout an >>
> instance (which gives more flexibility for also caching things and >>
> other interal bookkeeping). >> >> The best you can do as a "static"
> solution is to have some >> helpers, like the SpaceDistributor class I
> once created, but the >> actual layouts are *far* more complicated,
> and I don't think you >> can easily get away with having them just be
> a static method call. >> >> >> In other words, it might be possible to
> achieve the main goal >> of code reuse without any changes to the
> public APIs.  The >> layout classes can be made public later, once
> matured. >> >> Only if you accept the layouts would be extremely
> limited (no >> mixing of Layouts and Nodes). >> >> Regardless, there
> wouldn't be any changes to existing public API's >> by having Node
> implement Layoutable.  At most, there would be one >> or two methods
> to offer a better abstraction for just plainly >> exposing
> "getProperties()" on Layoutable.  Implementing an >> interface that
> exposes methods that Node already has is a source >> and binary
> compatible change. >> >> --John >> >> >> What do you think? >> >>
> -andy > >

Reply via email to