Правильный способ создания декоратора элементов управления JavaFX

#javafx

Вопрос:

Я хочу создать элемент управления, который будет прикреплять сообщения проверки к любому другому элементу управления JavaFX, например:

         
<ValidationDecorator fx:id="usernameE" GridPane.columnIndex="2">
            <decorated>
                <TextField fx:id="usernameF" />
            </decorated>
</ValidationDecorator>

 

Пока это работает довольно хорошо:
введите описание изображения здесь

Но я столкнулся с проблемой с построителем сцен, он не показывает дочерние элементы для моего пользовательского элемента управления: введите описание изображения здесь

Код для моего пользовательского компонента (обратите внимание, что я добавил @DefaultProperty("decorated") :

 package org.foo.javafx.validation.extra;

import javafx.beans.DefaultProperty;
import javafx.beans.InvalidationListener;
import javafx.beans.property.SimpleListProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.text.Text;
import org.foo.javafx.validation.lib.Objection;
import org.foo.javafx.validation.lib.ObjectionSeverity;
import org.foo.javafx.validation.lib.Input;

import java.util.List;

@DefaultProperty(value = "decorated")
public class ValidationDecorator extends VBox {
    private final VBox decoratedComponentsContainer;
    private final VBox validationMessagesContainer;

    private final SimpleListProperty<Objection> objectionsProperty =
            new SimpleListProperty<>(FXCollections.observableArrayList());

    public ValidationDecorator() {
        decoratedComponentsContainer = new VBox();
        decoratedComponentsContainer.setPrefHeight(VBox.USE_COMPUTED_SIZE);
        decoratedComponentsContainer.setPrefWidth(VBox.USE_COMPUTED_SIZE);

        validationMessagesContainer = new VBox();
        validationMessagesContainer.setPrefHeight(VBox.USE_COMPUTED_SIZE);
        validationMessagesContainer.setPrefWidth(VBox.USE_COMPUTED_SIZE);
        validationMessagesContainer.setSpacing(2);

        this.setPrefHeight(USE_COMPUTED_SIZE);
        this.setPrefWidth(USE_COMPUTED_SIZE);
        this.setSpacing(2);

        super.getChildren().addAll(decoratedComponentsContainer, validationMessagesContainer);

        objectionsProperty.addListener((InvalidationListener) observable -> {
            List<Objection> objections = this.objectionsProperty.getValue();

            var messagesFx = validationMessagesContainer.getChildren();

            if (messagesFx.size() > objections.size()) {
                messagesFx.remove(objections.size(), messagesFx.size());
            }
            while (messagesFx.size() < objections.size()) {
                messagesFx.add(new ValidationMessageFx());
            }

            for (int i = 0; i < objections.size(); i  ) {
                ((ValidationMessageFx)messagesFx.get(i)).setObjection(objections.get(i));
            }
        });
    }

    public ObservableList<Node> getDecorated() {
        return decoratedComponentsContainer.getChildren();
    }

    public SimpleListProperty<Objection> objectionsProperty() {
        return objectionsProperty;
    }

    public <UIV,MV> void displayErrorsFor(Input<UIV, MV> input) {
        this.objectionsProperty().bind(input.objectionsProperty());
    }

    public class ValidationMessageFx extends HBox {
        private final Circle circle;
        private final Text text;

        public ValidationMessageFx() {
            super.setSpacing(5);
            super.setAlignment(Pos.BASELINE_LEFT);

            this.circle = new Circle(5.0, Color.RED);
            this.text = new Text();

            this.getChildren().addAll(circle, text);
        }

        public void setObjection(Objection e) {
            this.text.setText("!!!"   e.message);
            this.circle.setFill(e.severity == ObjectionSeverity.ERROR ? Color.RED : Color.ORANGE);
        }
    }
}


 

Since my custom control extends HBox overriding getChildren() property does not work (it breaks internal HBox logic). I also checked ScrollPane source code, as it has single content property but ScrollPane do not inherit from Pane class that defines getChildren() accessor.

Any idea how I can do this without writing a custom control from scratch (I want to reuse VBox layout logic).