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