Content Graph
John Hendrikx
john.hendrikx at gmail.com
Fri Apr 14 07:57:29 UTC 2023
Some comments are inline.
I think this may be a good idea, but I would like to know more about the
use cases.
All I can think of is that it *may* be useful when you want to persist a
Scene (or the states involved); but as you're building the Scene
yourself, you should already be aware of how it is structured. I also
saw the split pane use case, and I wonder if that can't be solved
differently (perhaps SplitPane needs to be smarter here).
As I barely know of any use cases for the current `lookup` methods,
another set of them doesn't sound that useful to me either. The only
way I've ever used lookup (which is limited in how useful it is as it
can't return an instance of a specific type) is to mark a node for
".initial-focus" when displaying a window.
Some further questions as well:
1) Could Skins be inflated more eagerly? It wouldn't solve everything,
but it would solve the SplitPane use case.
2) How far does a content graph go? It contains MenuItem. Should or will
it contain ListView items?
3) Will this require a new observable list for every parent node, or is
it going to be the same list for most of the nodes?
On 13/04/2023 20:55, Michael Strauß wrote:
> I've previously proposed an enhancement to introduce a "document
> graph" to JavaFX that would solve a problem that many people have
> encountered over the years:
> https://mail.openjdk.org/pipermail/openjfx-dev/2022-June/034417.html
>
> In a nutshell, the problem is that the JavaFX scene graph is a black
> box in the presence of control skins, which makes it hard to reason
> about its structure. On top of that, several nodes that look like they
> are part of the scene graph (MenuItem or Tab) aren't scene graph nodes
> at all.
>
>
> 1) Content Graph
>
> We can solve this problem by adding a content graph. The content graph
> is a structure that comprises all content nodes of a scene. Content
> nodes represent the significant scene content, which exludes all nodes
> contributed by skins, but includes some of the aforementioned nodes
> that are not part of the scene graph.
>
> The content graph consists of two new interfaces that live in the
> `javafx.content` package:
>
> public interface ContentNode {
> ContentParent getContentParent();
>
> Styleable lookupContent(String selector);
> Set<Styleable> lookupAllContent(String selector);
> }
>
> public interface ContentParent extends ContentNode {
> ObservableList<ContentNode> getContentChildren();
> }
Would the returned list here be read only?
> These interfaces are implemented by `javafx.scene.Node` and
> `javafx.scene.Parent`, but also by other classes like `MenuItem` or
> `Tab`. Using these interfaces, the content graph can be traversed in
> the same way as the scene graph.
>
> Consider the following program, which creates a simple user interface:
>
> var root = new VBox();
> var menuBtn = new MenuButton();
> menuBtn.getItems().add(new MenuItem("Item 1"));
> menuBtn.getItems().add(new MenuItem("Item 2", new Rectangle()));
> root.getChildren().add(menuBtn);
> root.getChildren().add(new Button("Button 1", new Rectangle()));
> root.getChildren().add(new Button("Button 2", new Circle()));
>
> Suppose a method `printSceneGraph(Node root)`, which traverses the
> scene graph and prints out the nodes it encounters.
> Here is the output before the UI is shown:
>
> javafx.scene.layout.VBox
> ....javafx.scene.control.MenuButton
> ....javafx.scene.control.Button
> ....javafx.scene.control.Button
>
> And here's the output of the same method, but after the UI is shown:
>
> javafx.scene.layout.VBox
> ....javafx.scene.control.MenuButton
> ........javafx.scene.control.skin.MenuButtonSkinBase$MenuLabeledImpl
> ............com.sun.javafx.scene.control.LabeledText
> ........javafx.scene.layout.StackPane
> ............javafx.scene.layout.StackPane
> ....javafx.scene.control.Button
> ........javafx.scene.shape.Rectangle
> ........com.sun.javafx.scene.control.LabeledText
> ....javafx.scene.control.Button
> ........javafx.scene.shape.Circle
> ........com.sun.javafx.scene.control.LabeledText
>
> Note that the scene graph contains many more nodes after skins have
> been inflated, but it still doesn't contain the `MenuItem` instances.
>
> Now also suppose a method `printContentGraph(ContentNode root)`, which
> does the same as before, but works on the content graph.
> This is the output of that method:
>
> javafx.scene.layout.VBox
> ....javafx.scene.control.MenuButton
> ........javafx.content.Text
> ........javafx.scene.control.MenuItem
> ............javafx.content.Text
> ........javafx.scene.control.MenuItem
> ............javafx.content.Text
> ............javafx.scene.shape.Rectangle
> ....javafx.scene.control.Button
> ........javafx.content.Text
> ........javafx.scene.shape.Rectangle
> ....javafx.scene.control.Button
> ........javafx.content.Text
> ........javafx.scene.shape.Circle
I see a "javafx.content.Text" here, is that intended to be a new type as
well?
> Note that the content graph is not affected by skins, and the output
> is the same whether the method is called before or after skin
> inflation.
>
>
> 2) Defining the Content Model
>
> The content graph consists of the content model of all participating
> controls. Implementors are responsible for defining the content model
> of a control by overriding the protected `buildContentModel` method,
> and adding the content that is significant for the control:
>
> public class SplitPane {
> ...
> @Override
> protected void buildContentModel(
> Consumer<ObservableList<? extends ContentNode>> contentModel) {
> super.buildContentModel(contentModel);
> contentModel.accept(getItems());
> }
> ...
> }
>
> If a control subclass wants to extend the content model of an existing
> control, it simply overrides the `buildContentModel` method again as
> shown above. The content model of a subclass can only be extended, it
> can't be constrained. This is analogous to how a subclass of `Labeled`
> can only add new properties, but not remove the existing
> `Labeled.text` and `Labeled.graphic` properties.
For a lot of controls I think the content nodes will be fixed
(SplitPane, Label, Button), but for others like MenuButton or containers
these need to be modified. Does it make sense to use a similar model
here that is used for properties? Some controls would still need to be
able to modify the list directly, in which case subclasses could go that
route as well, circumventing these protections.
If the primary content graph is always determined by the "base" control,
and subclasses can only contribute additional nodes without being able
to modify anything, would that make it impossible to create a new
container type node (or perhaps we're already constrained here by having
to subclass Parent) ?
--John
More information about the openjfx-dev
mailing list