Search code examples
javajavafxjavafx-8

Why are the setAlignment methods on layouts static?


I noticed that the setAlignment method for layouts within JavaFX is static. Why is this the case? Doesn't this mean that a single node contained within two layouts of the same class cannot have two different alignments?

For example, I have a node called introText that I want to align differently within two different BorderPane objects:

BorderPane rootLayout = new BorderPane();
BorderPane infoLayout = new BorderPane();
Label introText = new Label("Select an Item");
rootLayout.setCenter(introText);
infoLayout.setTop(introText);
BorderPane.setAlignment(introText, Pos.CENTER);
#Now introText is positioned in the centre on both rootLayout and infoLayout.

How can I position introText as Pos.TOP_LEFT within infoLayout and Pos.CENTER within rootLayout?


Solution

  • First, you can't actually run into the problem you describe. A Node can only appear once in the scene graph. If you add the same node to another parent without removing it from its current parent, then it will be silently removed from its current parent first. So, the answer to:

    How can I position introText as Pos.TOP_LEFT within infoLayout and Pos.CENTER within rootLayout?

    Is: You can't, because introText cannot be a child of both infoLayout and rootLayout at the same time. Now, it may be that the properties of both BorderPane will contain the label, but that label will only be in one of their children lists, and only nodes in the children list will be rendered by that parent.

    As for why methods like BorderPane#setAlignment(Node,Pos) are static, that's most likely because:

    1. A BorderPane does not have a global alignment (unlike e.g., HBox), so it doesn't make sense to have an instance alignment property defined for it.

    2. However, a BorderPane can end up with empty space in each of its "sections", and so it makes sense to allow the developer to define the alignment of each individual child Node to determine how they get positioned in that empty space.

    3. But it doesn't make sense for Node to have an instance alignment property either. It's too specific a property for such a widely scoped class.

    4. So, they defined a static setAlignment method on BorderPane which takes the Node to set the alignment for. This keeps the property scoped to the BorderPane, which is the class that makes uses of it, and associates the value with the Node instance, not the class.

    5. And they made the method static because an instance method would imply the alignment state is stored with the BorderPane instance. That's not the case--the alignment state is stored with the given Node instance (see below).

    These methods work by modifying the properties map that each Node has. For instance, here's the implementation of BorderPane#setAlignment(Node,Pos):

    public static void setAlignment(Node child, Pos value) {
        setConstraint(child, ALIGNMENT, value);
    }
    

    Which calls this method:

    static void setConstraint(Node node, Object key, Object value) {
        if (value == null) {
            node.getProperties().remove(key);
        } else {
            node.getProperties().put(key, value);
        }
        if (node.getParent() != null) {
            node.getParent().requestLayout();
        }
    }
    

    So, as you can see, although these methods may be static, the state is still associated with a node instance. And yes, if you were to take the node out of one BorderPane and add it to another, then it would still have the same alignment. You'd have to change the alignment if and as needed.