ItemBanner.java
package no.ntnu.idatt1002.demo.view.components;
import java.util.ArrayList;
import java.util.List;
import javafx.animation.FadeTransition;
import javafx.animation.TranslateTransition;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
/**
* A banner of items.
*/
public class ItemBanner extends VBox {
HBox bannerContainer = new HBox();
private final ArrayList<ItemPane> items = new ArrayList<>();
private int itemListIndex = 0;
private Text bannerTitle = new Text();
private ArrowButton leftArrowButton = new ArrowButton(ArrowButton.Direction.LEFT);
private ArrowButton rightArrowButton = new ArrowButton(ArrowButton.Direction.RIGHT);
public ItemBanner() {
this(new ItemPane[0]);
}
public ItemBanner(ItemPane item) {
this(new ItemPane[] { item });
}
/**
* Constructor for the ItemBanner.
*
* @param items The items to be displayed in the banner.
*/
public ItemBanner(ItemPane[] items) {
super();
this.items.addAll(List.of(items));
// Add action listeners to the arrow buttons to change the index and update the
// page
leftArrowButton.setOnAction(e -> {
transitionTo(getItemListIndex() - 1);
update();
});
rightArrowButton.setOnAction(e -> {
transitionTo(getItemListIndex() + 1);
});
bannerTitle.getStyleClass().add("banner-title");
this.getStyleClass().add("item-banner");
this.getChildren().addAll(bannerTitle, bannerContainer);
bannerContainer.getStyleClass().addAll("centered", "full-width");
this.getStyleClass().addAll("centered", "full-width");
update();
}
public void addItem(ItemPane item) {
items.add(item);
update();
}
/**
* Set the items to be displayed in the banner.
*
* @param items The items to be displayed in the banner.
*/
public void setItems(ItemPane[] items) {
this.items.clear();
this.items.addAll(List.of(items));
update();
}
/**
* Starts a transition to the specified item index.
*
* @param itemIndex The index of the item to transition to.
*/
public void transitionTo(int itemIndex) {
if (verifyIndex(itemIndex, items.size()) == itemListIndex) {
return;
}
startTransition(itemIndex - itemListIndex, itemIndex);
}
private void startTransition(int itemsToMove, int moveIndex) {
int animationTime = 300;
if (itemsToMove == 0) {
return;
}
for (int i = 0; i < Math.abs(itemsToMove) + 3; i++) {
int currentItem = i + getItemListIndex() - 1;
// Check if the item index is out of bounds
if (currentItem < 0 || currentItem > items.size() || items.size() <= 3) {
continue;
}
// Fade out the item if it is going to be moved out of the banner
if (i <= itemsToMove || i > itemsToMove + 3) {
FadeTransition fadeTransition = new FadeTransition();
fadeTransition.setNode(this.items.get(currentItem));
fadeTransition.setFromValue(1);
fadeTransition.setToValue(0);
fadeTransition.setDuration(javafx.util.Duration.millis(animationTime / 2));
fadeTransition.play();
}
// TODO: add a fade in transition for the new items
TranslateTransition translateTransition = new TranslateTransition();
translateTransition.setNode(this.items.get(currentItem));
translateTransition.setByX(
itemsToMove * -1 * ((this.getWidth() / 3) - this.rightArrowButton.getWidth()));
translateTransition.setDuration(javafx.util.Duration.millis(animationTime));
translateTransition.setInterpolator(javafx.animation.Interpolator.EASE_BOTH);
translateTransition.play();
// cleaning up after every single transition is dumb
// TODO: fix this
translateTransition.setOnFinished(e -> {
animationCleanup(moveIndex);
});
}
}
private void animationCleanup(int moveIndex) {
items.forEach(item -> {
item.setTranslateX(0);
item.setOpacity(1);
});
setItemListIndex(moveIndex);
update();
}
/**
* Updates the banner with the current items and index.
*
* <p>
* This method should be called whenever the items or index are changed.
* </p>
*/
public void update() {
// Clear the container
bannerContainer.getChildren().clear();
// Add the left arrow button
bannerContainer.getChildren().add(leftArrowButton);
// Add the recipes to the container
for (int i = 0; i < 3; i++) {
if (itemListIndex + i < items.size()) {
bannerContainer.getChildren().add(items.get(itemListIndex + i));
}
}
// Add the right arrow button
bannerContainer.getChildren().add(rightArrowButton);
// If the length of items is less than 3, don't show the arrow buttons
if (items.size() <= 3) {
leftArrowButton.setInactiveColor();
rightArrowButton.setInactiveColor();
} else {
leftArrowButton.setActiveColor();
rightArrowButton.setActiveColor();
}
// If the index is at the start of the list, disable the left arrow button
if (itemListIndex == 0) {
leftArrowButton.setInactiveColor();
} else {
leftArrowButton.setActiveColor();
}
// If the index is at the end of the list, disable the right arrow button
if (itemListIndex >= items.size() - 3) {
rightArrowButton.setInactiveColor();
} else {
rightArrowButton.setActiveColor();
}
// Align the contents to the center
this.alignmentProperty().setValue(javafx.geometry.Pos.CENTER);
}
public int getItemListIndex() {
return this.itemListIndex;
}
public void setItemListIndex(int index) {
this.itemListIndex = verifyIndex(index, items.size());
}
private int verifyIndex(int index, int listSize) {
if (index < 0) {
return 0;
} else if (index >= listSize - 3) {
return listSize - 3;
}
return index;
}
/**
* Set the title of the banner. This will be displayed at the top of the banner,
* and is to be used for displaying the category of the items.
*
* @param title The title of the banner
*/
public void setTitle(String title) {
bannerTitle.setText(title);
}
}