UICompositor.java

package edu.ntnu.idi.idatt.view.components.ui;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List;
import java.util.function.Consumer;

import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;

/**
 * UICompositor class
 *
 * <p>
 * Builder class for simpler UI layout creation.
 * </p>
 */
public class UICompositor {

  private final Pane parent;

  /**
   * Constructor for UICompositor.
   * 
   * @param builder - UICompositor.Builder object.
   */
  public UICompositor(Builder builder) {
    this.parent = builder.parent;
  }

  /**
   * Method for obtaining the root element.
   * 
   * @return root pane.
   */
  public Parent makeUI() {
    return parent;
  }

  /**
   * Builder class for UICompositor instances.
   */
  public static class Builder {
    private Pane parent;
    private Deque<Pane> wrappers = new ArrayDeque<>();

    /**
     * Setter for UICompositor parent/root element.
     * 
     * @param parent - Root element
     * @return Builder - self
     */
    public Builder parent(Pane parent) {
      this.parent = parent;
      return this;
    }

    /**
     * Method for streching a node and positioning within.
     *
     * <p>
     * Works for HBox and VBox instances.
     *
     * @param position - Position of inner nodes.
     * @return builder - self.
     */
    public Builder growWithAlignment(Pos position) {
      Pane root = getRoot();
      if (root instanceof HBox) {
        ((HBox) root).setAlignment(position);
        root.setMaxWidth(Double.MAX_VALUE);
        HBox.setHgrow(root, Priority.ALWAYS);
      }
      if (root instanceof VBox) {
        ((VBox) root).setAlignment(position);
        root.setMaxHeight(Double.MAX_VALUE);
        VBox.setVgrow(root, Priority.ALWAYS);
      }
      return this;
    }

    /**
     * Method for obtaining newest layout node on the stack.
     * 
     * @return newest element on the stack.
     */
    private Pane getRoot() {
      return wrappers.isEmpty() ? parent : wrappers.peek();
    }

    /**
     * Method for adding content to newest stack element.
     * 
     * @param child - Node/content
     * @return bulder - self.
     */
    public Builder addContent(Parent child) {
      Pane root = getRoot();
      root.getChildren().add(child);
      return this;
    }

    /**
     * Method for adding multiple contents to newest stack element.
     * 
     * @param children - array of nodes/content.
     * @return builder - self.
     */
    public Builder addAllContent(Parent... children) {
      Pane root = getRoot();
      root.getChildren().addAll(List.of(children));
      return this;
    }

    /**
     * Method for adding a layer to the stack.
     * 
     * @param wrapper - Pane type.
     * @return builder - self.
     */
    public Builder wrap(Pane wrapper) {
      wrappers.push(wrapper);
      return this;
    }

    /**
     * Method for adding newest stack element to previous on stack element.
     * 
     * @return builder - self.
     */
    public Builder unwrap() {
      Pane current = wrappers.pop();

      if (wrappers.isEmpty()) {
        parent.getChildren().add(current);
      } else {
        wrappers.peek().getChildren().add(current);
      }
      return this;
    }

    /**
     * Method for changing newest stack element properties.
     * 
     * @param handle - Functional interface with reference to newest stack element.
     * @return builder - self.
     */
    public Builder properties(Consumer<Pane> handle) {
      handle.accept(getRoot());
      return this;
    }

    /**
     * Method for adding filler to newest stack element.
     *
     * <p>
     * Creates a filler for layered stack elements type HBox, VBox.
     * </p>
     *
     * @return builder = self.
     */
    public Builder filler() {
      Pane root = getRoot();

      Region filler = new Region();
      if (root instanceof HBox) {
        HBox.setHgrow(filler, Priority.ALWAYS);
      }
      if (root instanceof VBox) {
        VBox.setVgrow(filler, Priority.ALWAYS);
      }
      root.getChildren().add(filler);
      return this;
    }

    /**
     * Method for applying the builder method.
     * 
     * @return instance of created UICompositor.
     */
    public UICompositor build() {
      return new UICompositor(this);
    }

  }

}