UIFactory.java
package edu.ntnu.idi.idatt.view.components.ui;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import edu.ntnu.idi.idatt.session.UserSession;
import edu.ntnu.idi.idatt.view.SceneFactory;
import edu.ntnu.idi.idatt.view.components.AbstractViewUI;
import edu.ntnu.idi.idatt.view.components.elements.IconComponent;
import edu.ntnu.idi.idatt.view.components.elements.SearchBarComponent;
import edu.ntnu.idi.idatt.view.components.primitives.ActionEventHandler;
import edu.ntnu.idi.idatt.view.util.CssUtils;
import edu.ntnu.idi.idatt.view.util.ResourceUtils;
import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleStringProperty;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Parent;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
/**
* Factory class for creating UI elements.
*
* <p>
* Simplifies the repetative work to create
* equal layouts across multiple views.
* </p>
*
* {@link AbstractViewUI}
*/
public class UIFactory {
/**
* Wrapper for creating simple header.
*
* <p>
* Creates a simple header containing a searchbar.
* </p>
*
* @param placeholder - Placeholder for searchbar.
* @param searchQuery - Functional interface of searchbar input property.
* @return header element.
*/
public static Parent createHeader(String placeholder, Consumer<SimpleStringProperty> searchQuery) {
return createDefaultHeader(placeholder, searchQuery);
}
/**
* Wrapper for creating extended header.
*
* <p>
* Creates a header with searchbar with sorting ability.
* </p>
*
* @param placeholder - Placeholder for searchbar.
* @param searchQuery - Functional interface of searchbar input property.
* @param sortItems - List of available sorting labels.
* @param sortHandlers - List of functional interfaces to react to the items.
* @return header element.
*/
public static Parent createHeader(String placeholder, Consumer<SimpleStringProperty> searchQuery,
List<String> sortItems,
ActionEventHandler... sortHandlers) {
if (sortItems.size() != sortHandlers.length) {
System.out.println("Failed to build header!");
return null;
}
MenuButton menu = new MenuButton("Sort by..");
menu.getStyleClass().add("menu-button");
sortItems.forEach(item -> {
MenuItem m = new MenuItem(item);
m.setOnAction((e) -> sortHandlers[sortItems.indexOf(item)].handle());
menu.getItems().add(m);
});
return createDefaultHeader(placeholder, searchQuery, menu);
}
/**
* Method for creating all headers.
*
* <p>
* This method is used for creating all headers through the public wrappers.
* </p>
*
* @param placeholder - Placeholder for searchbar.
* @param searchQuery - Functional interface of searchbar input property.
* @param addons - Parent objects to add to the left site header
* @return header element.
*/
private static Parent createDefaultHeader(String placeholder, Consumer<SimpleStringProperty> searchQuery,
Parent... addons) {
SearchBarComponent bar = new SearchBarComponent(placeholder);
HBox.setMargin(bar, new Insets(20));
UICompositor header = new UICompositor.Builder()
.parent(new HBox())
.growWithAlignment(Pos.CENTER)
.properties(parent -> parent.setPadding(new Insets(0, 10, 0, 10)))
.addAllContent(addons)
.filler()
.filler()
.addContent(bar)
.filler()
.filler()
.filler()
.build();
searchQuery.accept(bar.getQuery());
return header.makeUI();
}
/**
* Wrapper for creating navigation with title.
*
* <p>
* Creates a navigation with a title.
* </p>
*
* @param title - Title string.
* @param buttonLables - List of button names.
* @param handlers - Array of functional interfaces for button clicks.
* @return navigation element.
*/
public static Parent createNavigation(String title, List<String> buttonLables, ActionEventHandler... handlers) {
Label titleLabel = new Label(title);
return createDefaultNavigation(titleLabel, buttonLables, handlers);
}
/**
* Wrapper for creating navigation with title label.
*
* <p>
* Creates a navigation with a title label.
* </p>
*
* @param titleLabel - Label title object.
* @param buttonLables - List of button names.
* @param handlers - Array of functional interfaces for button clicks.
* @return navigation element.
*/
public static Parent createNavigation(Label titleLabel, List<String> buttonLables,
ActionEventHandler... handlers) {
return createDefaultNavigation(titleLabel, buttonLables, handlers);
}
/**
* Method for creating all navigations.
* <p>
* This method creates all navigations through the public wrappers.
* </p>
*
* @param titleLabel - Label title object.
* @param buttonLables - List of button names.
* @param handlers - Array of functional interfaces for button clicks.
* @return navigation element.
*/
private static Parent createDefaultNavigation(Label titleLabel, List<String> buttonLables,
ActionEventHandler... handlers) {
if (buttonLables.size() != handlers.length) {
System.out.println("Failed to build navigation!");
return null;
}
CssUtils.apply(titleLabel, CssUtils.BIG_TEXT_32);
ArrayList<Button> buttons = new ArrayList<>();
for (String buttonLabel : buttonLables) {
buttons.add(new Button(buttonLabel));
}
buttons.forEach(b -> {
b.setOnAction((e) -> handlers[buttons.indexOf(b)].handle());
});
UICompositor.Builder navigationBuilder = new UICompositor.Builder()
.parent(new VBox(10))
.growWithAlignment(Pos.CENTER)
.properties(nav -> nav.setPadding(new Insets(40)))
.addContent(titleLabel);
buttons.forEach(b -> {
navigationBuilder.addContent(b);
CssUtils.fadeInAnimation(b);
});
navigationBuilder
.filler();
if (!SceneFactory.isFinal()) {
Button previous = new Button("Previous Page");
previous.setOnAction((e) -> SceneFactory.goBack());
navigationBuilder.addContent(previous);
}
Button progress = new Button("Advance To Next Week");
progress.setOnAction((e) -> {
UserSession.getInstance().getExchange().advance();
UserSession.getInstance().updateGameState();
SceneFactory.reloadCurrent();
});
navigationBuilder.addContent(progress);
// Styles and properties of childs
navigationBuilder.properties(nav -> nav.getChildren()
.filtered(c -> !(c instanceof Label))
.forEach(c -> ((Region) c).setMaxWidth(Double.MAX_VALUE)));
navigationBuilder.properties(nav -> nav.getChildren()
.filtered(c -> c instanceof Button)
.forEach(c -> c.getStyleClass().add("button-outlined")));
UICompositor navigation = navigationBuilder.build();
return navigation.makeUI();
}
/**
* Method for creating menu element.
*
* <p>
* Creates a menu with clickable buttons.
* </p>
*
* @param title - Title of the menu.
* @param buttonLables - List of button names.
* @param handlers - Array of functional interfaces for button clicks.
* @return menu element.
*/
public static Parent createMenu(String title, List<String> buttonLables, ActionEventHandler... handlers) {
if (buttonLables.size() != handlers.length) {
System.out.println("Failed to build menu!");
}
Label titleLabel = new Label(title);
CssUtils.apply(titleLabel, CssUtils.BIG_TEXT_32);
ArrayList<Button> buttons = new ArrayList<>();
for (String buttonLabel : buttonLables) {
buttons.add(new Button(buttonLabel));
}
buttons.forEach(b -> {
b.setOnAction((e) -> handlers[buttons.indexOf(b)].handle());
});
UICompositor.Builder menuBuilder = new UICompositor.Builder()
.parent(new VBox(10))
.growWithAlignment(Pos.CENTER)
.properties(nav -> nav.setPadding(new Insets(40)))
.addContent(titleLabel);
buttons.forEach(b -> menuBuilder.addContent(b));
menuBuilder
.filler()
.properties(nav -> nav.getChildren()
.filtered(c -> !(c instanceof Label))
.forEach((c -> ((Region) c).setMaxWidth(Double.MAX_VALUE))));
menuBuilder.properties(nav -> nav.getChildren()
.filtered(c -> c instanceof Button)
.forEach(c -> c.getStyleClass().add("button-outlined")));
UICompositor menu = menuBuilder.build();
return menu.makeUI();
}
/**
* Method for creating toolbar.
*
* <p>
* Creates a toolbar with UserSession value displays and event handlers.
* </p>
*
* @see UserSession
*
* @param menuHandle - Functional interface to menu button click.
* @param quitHandle - Functional interface to exit button click.
* @return toolbar element.
*/
public static Parent createToolbar(ActionEventHandler menuHandle, ActionEventHandler quitHandle) {
// Initializing formatter locally.
NumberFormat formatter = NumberFormat.getNumberInstance();
formatter.setMaximumFractionDigits(2);
Label balance = new Label();
balance.textProperty().bind(
Bindings.createStringBinding(
() -> String.format("Balance: %s $", formatter.format(UserSession.getInstance().moneyProperty().get())),
UserSession.getInstance().moneyProperty()));
Label week = new Label();
week.textProperty().bind(
UserSession.getInstance().weekProperty().asString("Week: %d"));
Label playerStatus = new Label();
playerStatus.textProperty().bind(Bindings.format("Status: %s",
UserSession.getInstance().statusProperty()));
Label playerNetWorth = new Label();
playerNetWorth.textProperty().bind(
Bindings.createStringBinding(
() -> String.format("Net Worth: %s $",
formatter.format(UserSession.getInstance().netWorthProperty().get())),
UserSession.getInstance().netWorthProperty()));
CssUtils.apply(balance, CssUtils.MED_TEXT_16);
CssUtils.apply(week, CssUtils.MED_TEXT_16);
CssUtils.apply(playerStatus, CssUtils.MED_TEXT_16);
CssUtils.apply(playerNetWorth, CssUtils.MED_TEXT_16);
IconComponent userIcon = new IconComponent(ResourceUtils.MENU_ICON, null, 44);
IconComponent quitIcon = new IconComponent(ResourceUtils.QUIT_ICON, null, 44);
UICompositor toolbar = new UICompositor.Builder()
.parent(new HBox())
.growWithAlignment(Pos.CENTER)
.properties(parent -> parent.setPadding(new Insets(5)))
.wrap(new VBox(5))
.properties(wrapper -> ((VBox) wrapper).setAlignment(Pos.CENTER))
.addAllContent(balance, week)
.unwrap()
.filler()
.wrap(new VBox(5))
.properties(wrapper -> ((VBox) wrapper).setAlignment(Pos.CENTER))
.addAllContent(playerStatus, playerNetWorth)
.unwrap()
.wrap(new HBox(5))
.properties(wrapper -> ((HBox) wrapper).setAlignment(Pos.CENTER))
.addAllContent(userIcon, quitIcon)
.unwrap()
.build();
userIcon.onIconClick(() -> menuHandle.handle());
quitIcon.onIconClick(() -> quitHandle.handle());
return toolbar.makeUI();
}
}