<html><head></head><body><div style="font-family: Verdana;font-size: 12.0px;"><div>Looking at the PR, there is one thing I have a 'bad feeling' with.</div>

<div>And that is that everything is done in an eager way.<br/>
E.g. we now maintain another list, the <strong>contentModel</strong>, even if you don't need it.</div>

<div>
<div>I see why this API can be useful. And I even needed something like this, just not very often.<br/>
Performance wise this should not make a big impact, still something we may should discuss here.</div>

<div> </div>

<div>-- Marius</div>

<div> 
<div name="quote" style="margin:10px 5px 5px 10px; padding: 10px 0 10px 10px; border-left:2px solid #C3D9E5; word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;">
<div style="margin:0 0 10px 0;"><b>Gesendet:</b> Donnerstag, 13. April 2023 um 20:55 Uhr<br/>
<b>Von:</b> "Michael Strauß" <michaelstrau2@gmail.com><br/>
<b>An:</b> "openjfx-dev" <openjfx-dev@openjdk.org><br/>
<b>Betreff:</b> Content Graph</div>

<div name="quoted-content">I've previously proposed an enhancement to introduce a "document<br/>
graph" to JavaFX that would solve a problem that many people have<br/>
encountered over the years:<br/>
<a href="https://mail.openjdk.org/pipermail/openjfx-dev/2022-June/034417.html" target="_blank">https://mail.openjdk.org/pipermail/openjfx-dev/2022-June/034417.html</a><br/>
<br/>
In a nutshell, the problem is that the JavaFX scene graph is a black<br/>
box in the presence of control skins, which makes it hard to reason<br/>
about its structure. On top of that, several nodes that look like they<br/>
are part of the scene graph (MenuItem or Tab) aren't scene graph nodes<br/>
at all.<br/>
<br/>
<br/>
1) Content Graph<br/>
<br/>
We can solve this problem by adding a content graph. The content graph<br/>
is a structure that comprises all content nodes of a scene. Content<br/>
nodes represent the significant scene content, which exludes all nodes<br/>
contributed by skins, but includes some of the aforementioned nodes<br/>
that are not part of the scene graph.<br/>
<br/>
The content graph consists of two new interfaces that live in the<br/>
`javafx.content` package:<br/>
<br/>
public interface ContentNode {<br/>
ContentParent getContentParent();<br/>
<br/>
Styleable lookupContent(String selector);<br/>
Set<Styleable> lookupAllContent(String selector);<br/>
}<br/>
<br/>
public interface ContentParent extends ContentNode {<br/>
ObservableList<ContentNode> getContentChildren();<br/>
}<br/>
<br/>
These interfaces are implemented by `javafx.scene.Node` and<br/>
`javafx.scene.Parent`, but also by other classes like `MenuItem` or<br/>
`Tab`. Using these interfaces, the content graph can be traversed in<br/>
the same way as the scene graph.<br/>
<br/>
Consider the following program, which creates a simple user interface:<br/>
<br/>
var root = new VBox();<br/>
var menuBtn = new MenuButton();<br/>
menuBtn.getItems().add(new MenuItem("Item 1"));<br/>
menuBtn.getItems().add(new MenuItem("Item 2", new Rectangle()));<br/>
root.getChildren().add(menuBtn);<br/>
root.getChildren().add(new Button("Button 1", new Rectangle()));<br/>
root.getChildren().add(new Button("Button 2", new Circle()));<br/>
<br/>
Suppose a method `printSceneGraph(Node root)`, which traverses the<br/>
scene graph and prints out the nodes it encounters.<br/>
Here is the output before the UI is shown:<br/>
<br/>
javafx.scene.layout.VBox<br/>
....javafx.scene.control.MenuButton<br/>
....javafx.scene.control.Button<br/>
....javafx.scene.control.Button<br/>
<br/>
And here's the output of the same method, but after the UI is shown:<br/>
<br/>
javafx.scene.layout.VBox<br/>
....javafx.scene.control.MenuButton<br/>
........javafx.scene.control.skin.MenuButtonSkinBase$MenuLabeledImpl<br/>
............com.sun.javafx.scene.control.LabeledText<br/>
........javafx.scene.layout.StackPane<br/>
............javafx.scene.layout.StackPane<br/>
....javafx.scene.control.Button<br/>
........javafx.scene.shape.Rectangle<br/>
........com.sun.javafx.scene.control.LabeledText<br/>
....javafx.scene.control.Button<br/>
........javafx.scene.shape.Circle<br/>
........com.sun.javafx.scene.control.LabeledText<br/>
<br/>
Note that the scene graph contains many more nodes after skins have<br/>
been inflated, but it still doesn't contain the `MenuItem` instances.<br/>
<br/>
Now also suppose a method `printContentGraph(ContentNode root)`, which<br/>
does the same as before, but works on the content graph.<br/>
This is the output of that method:<br/>
<br/>
javafx.scene.layout.VBox<br/>
....javafx.scene.control.MenuButton<br/>
........javafx.content.Text<br/>
........javafx.scene.control.MenuItem<br/>
............javafx.content.Text<br/>
........javafx.scene.control.MenuItem<br/>
............javafx.content.Text<br/>
............javafx.scene.shape.Rectangle<br/>
....javafx.scene.control.Button<br/>
........javafx.content.Text<br/>
........javafx.scene.shape.Rectangle<br/>
....javafx.scene.control.Button<br/>
........javafx.content.Text<br/>
........javafx.scene.shape.Circle<br/>
<br/>
Note that the content graph is not affected by skins, and the output<br/>
is the same whether the method is called before or after skin<br/>
inflation.<br/>
<br/>
<br/>
2) Defining the Content Model<br/>
<br/>
The content graph consists of the content model of all participating<br/>
controls. Implementors are responsible for defining the content model<br/>
of a control by overriding the protected `buildContentModel` method,<br/>
and adding the content that is significant for the control:<br/>
<br/>
public class SplitPane {<br/>
...<br/>
@Override<br/>
protected void buildContentModel(<br/>
Consumer<ObservableList<? extends ContentNode>> contentModel) {<br/>
super.buildContentModel(contentModel);<br/>
contentModel.accept(getItems());<br/>
}<br/>
...<br/>
}<br/>
<br/>
If a control subclass wants to extend the content model of an existing<br/>
control, it simply overrides the `buildContentModel` method again as<br/>
shown above. The content model of a subclass can only be extended, it<br/>
can't be constrained. This is analogous to how a subclass of `Labeled`<br/>
can only add new properties, but not remove the existing<br/>
`Labeled.text` and `Labeled.graphic` properties.<br/>
<br/>
Note that if a custom control is not aware of the content graph and<br/>
does not override the `buildContentModel` method, that's not an issue<br/>
in itself; it simply means that the custom control will not contribute<br/>
additional nodes to the content graph.<br/>
<br/>
<br/>
3) Operations on the Content Graph<br/>
<br/>
The content graph supports query operations on styleable nodes by<br/>
adding two new lookup methods: `lookupContent` and `lookupAllContent`.<br/>
This solves a problem that was discussed on the mailing list<br/>
previously:<br/>
<a href="https://mail.openjdk.org/pipermail/openjfx-dev/2022-December/037770.html" target="_blank">https://mail.openjdk.org/pipermail/openjfx-dev/2022-December/037770.html</a><br/>
<br/>
The particular problem that was described in this email can be fixed<br/>
simply by using `lookupAllContent` instead of `lookupAll`:<br/>
window.getScene().getRoot().lookupAllContent(".split-pane")<br/>
<br/>
<br/>
<br/>
I'm interested in hearing your thoughts on this idea. If there is<br/>
sufficient interest, I can contribute a PR for this enhancement.</div>
</div>
</div>
</div></div></body></html>