Improved GUIBuilder

This commit is contained in:
NichtStudioCode 2021-02-01 11:56:13 +01:00
parent 5eb859e6c9
commit 73d5b0779d
10 changed files with 324 additions and 380 deletions

@ -2,16 +2,47 @@ package de.studiocode.invgui.gui.builder;
import de.studiocode.invgui.gui.GUI; import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.SlotElement; import de.studiocode.invgui.gui.SlotElement;
import de.studiocode.invgui.gui.impl.SimpleGUI;
import de.studiocode.invgui.item.Item; import de.studiocode.invgui.item.Item;
import org.bukkit.inventory.ShapedRecipe; import org.bukkit.inventory.ShapedRecipe;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** /**
* A utility class to easily construct {@link GUI}s.<br> * A builder class to easily construct {@link GUI}s.<br>
* It provides similar functionality to Bukkit's {@link ShapedRecipe}, as it * It provides similar functionality to Bukkit's {@link ShapedRecipe}, as it
* allows for a structure String which defines the layout of the {@link GUI}. * allows for a structure String which defines the layout of the {@link GUI}.
*/ */
public interface GUIBuilder { public class GUIBuilder {
protected final int width;
protected final int height;
private final int size;
private final Map<Character, Ingredient> ingredientMap = new HashMap<>();
private String structure;
public GUIBuilder(int width, int height) {
this.width = width;
this.height = height;
this.size = width * height;
}
/**
* Builds the {@link GUI}.
*
* @return The built {@link GUI}
*/
public GUI build() {
SimpleGUI gui = new SimpleGUI(width, height);
setSlotElements(gui);
return gui;
}
/** /**
* Sets the structure of the {@link GUI}. * Sets the structure of the {@link GUI}.
@ -20,7 +51,13 @@ public interface GUIBuilder {
* *
* @param structure The structure {@link String} * @param structure The structure {@link String}
*/ */
void setStructure(@NotNull String structure); public GUIBuilder setStructure(@NotNull String structure) {
String cleanedStructure = structure.replace(" ", "").replace("\n", "");
if (cleanedStructure.length() != size) throw new IllegalArgumentException("Structure length does not match size");
this.structure = cleanedStructure;
return this;
}
/** /**
* Sets an ingredient for the structure String, which will later be * Sets an ingredient for the structure String, which will later be
@ -29,7 +66,10 @@ public interface GUIBuilder {
* @param key The ingredient key * @param key The ingredient key
* @param item The {@link Item} * @param item The {@link Item}
*/ */
void setIngredient(char key, @NotNull Item item); public GUIBuilder setIngredient(char key, @NotNull Item item) {
ingredientMap.put(key, new Ingredient(item));
return this;
}
/** /**
* Sets an ingredient for the structure String, which will later be * Sets an ingredient for the structure String, which will later be
@ -38,13 +78,88 @@ public interface GUIBuilder {
* @param key The ingredient key * @param key The ingredient key
* @param slotElement The {@link SlotElement} * @param slotElement The {@link SlotElement}
*/ */
void setIngredient(char key, @NotNull SlotElement slotElement); public GUIBuilder setIngredient(char key, @NotNull SlotElement slotElement) {
ingredientMap.put(key, new Ingredient(slotElement));
return this;
}
/** protected void setIngredient(char key, int special) {
* Builds the {@link GUI}. ingredientMap.put(key, new Ingredient(special));
* }
* @return The built {@link GUI}
*/ private Ingredient[] parseStructure() {
GUI build(); if (structure == null) throw new IllegalStateException("Structure has not been set yet.");
Ingredient[] ingredients = new Ingredient[size];
int i = 0;
for (char c : structure.toCharArray()) {
if (c != '.') ingredients[i] = this.ingredientMap.get(c);
i++;
}
return ingredients;
}
protected void setSlotElements(GUI gui) {
Ingredient[] ingredients = parseStructure();
for (int i = 0; i < gui.getSize(); i++) {
Ingredient ingredient = ingredients[i];
if (ingredient != null && ingredient.isSlotElement())
gui.setSlotElement(i, ingredient.getSlotElement());
}
}
protected int[] findIndicesOf(int special) {
List<Integer> indices = new ArrayList<>();
Ingredient[] ingredients = parseStructure();
for (int i = 0; i < size; i++) {
Ingredient ingredient = ingredients[i];
if (ingredient != null && ingredient.isSpecial() && ingredient.getSpecial() == special)
indices.add(i);
}
return indices.stream().mapToInt(Integer::intValue).toArray();
}
static class Ingredient {
private final SlotElement slotElement;
private final int special;
public Ingredient(Item item) {
this.slotElement = new SlotElement.ItemSlotElement(item);
this.special = -1;
}
public Ingredient(SlotElement slotElement) {
this.slotElement = slotElement;
this.special = -1;
}
public Ingredient(int special) {
this.special = special;
this.slotElement = null;
}
public SlotElement getSlotElement() {
if (isSpecial()) throw new IllegalStateException("Ingredient is special");
return slotElement;
}
public int getSpecial() {
if (isSlotElement()) throw new IllegalStateException("Ingredient is item");
return special;
}
public boolean isSlotElement() {
return slotElement != null;
}
public boolean isSpecial() {
return slotElement == null;
}
}
} }

@ -0,0 +1,119 @@
package de.studiocode.invgui.gui.builder;
import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.SlotElement;
import de.studiocode.invgui.gui.impl.PagedGUI;
import de.studiocode.invgui.gui.impl.SimplePagedGUIs;
import de.studiocode.invgui.gui.impl.SimplePagedItemsGUI;
import de.studiocode.invgui.item.Item;
import de.studiocode.invgui.item.itembuilder.ItemBuilder;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.function.Function;
public class PagedGUIBuilder extends GUIBuilder {
private final ContentType contentType;
private Function<PagedGUI, ItemBuilder> backFunction;
private Function<PagedGUI, ItemBuilder> forwardFunction;
private int[] listSlots;
private List<?> content;
public PagedGUIBuilder(ContentType contentType, int width, int height) {
super(width, height);
this.contentType = contentType;
}
@SuppressWarnings("unchecked")
@Override
public PagedGUI build() {
int[] listSlots = this.listSlots != null ? this.listSlots : findIndicesOf(2);
int[] backSlots = findIndicesOf(0);
int[] forwardSlots = findIndicesOf(1);
if (listSlots.length == 0) throw new IllegalStateException("No list slots have been set.");
if (backSlots.length == 0) throw new IllegalStateException("Not back slot has been set.");
if (forwardSlots.length == 0) throw new IllegalStateException("No forward slot has been set.");
if (content == null) throw new IllegalStateException("Content has not been set.");
if (forwardFunction == null) throw new IllegalStateException("Forward function has not been set.");
if (backFunction == null) throw new IllegalStateException("Back function has not been set");
int backSlot = backSlots[0];
int forwardSlot = forwardSlots[0];
PagedGUI gui;
if (contentType == ContentType.GUI) {
gui = new SimplePagedGUIs(width, height, backSlot, backFunction,
forwardSlot, forwardFunction, (List<GUI>) content, listSlots);
} else {
gui = new SimplePagedItemsGUI(width, height, backSlot, backFunction,
forwardSlot, forwardFunction, (List<Item>) content, listSlots);
}
setSlotElements(gui);
return gui;
}
public PagedGUIBuilder setGUIs(List<GUI> guis) {
if (contentType != ContentType.GUI)
throw new IllegalStateException("Can't set guis if ContentType is not GUI");
content = guis;
return this;
}
public PagedGUIBuilder setItems(List<Item> items) {
if (contentType != ContentType.SINGLE_ITEMS)
throw new IllegalStateException("Can't set items if ContentType is not SINGLE_ITEMS");
content = items;
return this;
}
public PagedGUIBuilder setListSlots(int[] listSlots) {
this.listSlots = listSlots;
return this;
}
public PagedGUIBuilder setListSlotIngredient(char key) {
setIngredient(key, 2);
return this;
}
public PagedGUIBuilder setBackItem(char key, Function<PagedGUI, ItemBuilder> backFunction) {
setIngredient(key, 0);
this.backFunction = backFunction;
return this;
}
public PagedGUIBuilder setForwardItem(char key, Function<PagedGUI, ItemBuilder> forwardFunction) {
setIngredient(key, 1);
this.forwardFunction = forwardFunction;
return this;
}
@Override
public PagedGUIBuilder setStructure(@NotNull String structure) {
return (PagedGUIBuilder) super.setStructure(structure);
}
@Override
public PagedGUIBuilder setIngredient(char key, @NotNull Item item) {
return (PagedGUIBuilder) super.setIngredient(key, item);
}
@Override
public PagedGUIBuilder setIngredient(char key, @NotNull SlotElement slotElement) {
return (PagedGUIBuilder) super.setIngredient(key, slotElement);
}
public enum ContentType {
SINGLE_ITEMS,
GUI
}
}

@ -0,0 +1,79 @@
package de.studiocode.invgui.gui.builder;
import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.SlotElement;
import de.studiocode.invgui.gui.impl.SimpleTabGUI;
import de.studiocode.invgui.gui.impl.TabGUI;
import de.studiocode.invgui.item.Item;
import de.studiocode.invgui.item.itembuilder.ItemBuilder;
import org.jetbrains.annotations.NotNull;
import java.util.LinkedHashMap;
import java.util.function.Function;
public class TabGUIBuilder extends GUIBuilder {
private final LinkedHashMap<GUI, Function<TabGUI, ItemBuilder>> tabs = new LinkedHashMap<>();
private int[] listSlots;
private int[] tabItemSlots;
public TabGUIBuilder(int width, int height) {
super(width, height);
}
@Override
public TabGUI build() {
int[] listSlots = this.listSlots != null ? this.listSlots : findIndicesOf(0);
int[] tabItemSlots = this.tabItemSlots != null ? this.tabItemSlots : findIndicesOf(1);
if (listSlots.length == 0) throw new IllegalStateException("No list slots have been set.");
if (tabItemSlots.length != tabs.size()) throw new IllegalStateException("TabItemSlots length has to be the same as tabs size");
SimpleTabGUI tabGUI = new SimpleTabGUI(width, height, tabs, listSlots, tabItemSlots);
setSlotElements(tabGUI);
return tabGUI;
}
public TabGUIBuilder addTab(GUI gui, Function<TabGUI, ItemBuilder> builderFunction) {
tabs.put(gui, builderFunction);
return this;
}
public TabGUIBuilder setListSlotIngredient(char key) {
setIngredient(key, 0);
return this;
}
public TabGUIBuilder setTabSlotIngredient(char key) {
setIngredient(key, 1);
return this;
}
public TabGUIBuilder setListSlots(int[] listSlots) {
this.listSlots = listSlots;
return this;
}
public TabGUIBuilder setTabItemSlots(int[] tabItemSlots) {
this.tabItemSlots = tabItemSlots;
return this;
}
@Override
public TabGUIBuilder setStructure(@NotNull String structure) {
return (TabGUIBuilder) super.setStructure(structure);
}
@Override
public TabGUIBuilder setIngredient(char key, @NotNull Item item) {
return (TabGUIBuilder) super.setIngredient(key, item);
}
@Override
public TabGUIBuilder setIngredient(char key, @NotNull SlotElement slotElement) {
return (TabGUIBuilder) super.setIngredient(key, slotElement);
}
}

@ -1,136 +0,0 @@
package de.studiocode.invgui.gui.builder.impl;
import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.SlotElement;
import de.studiocode.invgui.gui.builder.GUIBuilder;
import de.studiocode.invgui.item.Item;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public abstract class BaseGUIBuilder implements GUIBuilder {
private final int width;
private final int height;
private final int size;
private final Map<Character, Ingredient> ingredientMap = new HashMap<>();
private String structure;
public BaseGUIBuilder(int width, int height) {
this.width = width;
this.height = height;
this.size = width * height;
}
@Override
public void setStructure(@NotNull String structure) {
structure = structure.replace(" ", "");
if (structure.length() != size) throw new IllegalArgumentException("Structure length does not match size");
this.structure = structure;
}
@Override
public void setIngredient(char key, @NotNull Item item) {
ingredientMap.put(key, new Ingredient(item));
}
@Override
public void setIngredient(char key, @NotNull SlotElement slotElement) {
ingredientMap.put(key, new Ingredient(slotElement));
}
public void setIngredient(char key, int special) {
ingredientMap.put(key, new Ingredient(special));
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
private Ingredient[] parseStructure() {
if (structure == null) throw new IllegalStateException("Structure has not been set yet.");
Ingredient[] ingredients = new Ingredient[size];
int i = 0;
for (char c : structure.toCharArray()) {
if (c != '.') ingredients[i] = this.ingredientMap.get(c);
i++;
}
return ingredients;
}
protected void setSlotElements(GUI gui) {
Ingredient[] ingredients = parseStructure();
for (int i = 0; i < gui.getSize(); i++) {
Ingredient ingredient = ingredients[i];
if (ingredient != null && ingredient.isSlotElement())
gui.setSlotElement(i, ingredient.getSlotElement());
}
}
public List<Integer> findIndicesOf(int special) {
List<Integer> indices = new ArrayList<>();
Ingredient[] ingredients = parseStructure();
for (int i = 0; i < size; i++) {
Ingredient ingredient = ingredients[i];
if (ingredient != null && ingredient.isSpecial() && ingredient.getSpecial() == special)
indices.add(i);
}
return indices;
}
public abstract GUI build();
static class Ingredient {
private final SlotElement slotElement;
private final int special;
public Ingredient(Item item) {
this.slotElement = new SlotElement.ItemSlotElement(item);
this.special = -1;
}
public Ingredient(SlotElement slotElement) {
this.slotElement = slotElement;
this.special = -1;
}
public Ingredient(int special) {
this.special = special;
this.slotElement = null;
}
public SlotElement getSlotElement() {
if (isSpecial()) throw new IllegalStateException("Ingredient is special");
return slotElement;
}
public int getSpecial() {
if (isSlotElement()) throw new IllegalStateException("Ingredient is item");
return special;
}
public boolean isSlotElement() {
return slotElement != null;
}
public boolean isSpecial() {
return slotElement == null;
}
}
}

@ -1,67 +0,0 @@
package de.studiocode.invgui.gui.builder.impl;
import de.studiocode.invgui.gui.impl.PagedGUI;
import de.studiocode.invgui.item.itembuilder.ItemBuilder;
import java.util.List;
import java.util.function.Function;
public abstract class PagedGUIBuilder extends BaseGUIBuilder {
private Function<PagedGUI, ItemBuilder> backFunction;
private Function<PagedGUI, ItemBuilder> forwardFunction;
private int[] listSlots;
public PagedGUIBuilder(int width, int height) {
super(width, height);
}
protected int getBackItemIndex() {
List<Integer> indices = findIndicesOf(0);
if (indices.isEmpty())
throw new IllegalStateException("BackItem index is not set");
return indices.get(0);
}
protected int getForwardItemIndex() {
List<Integer> indices = findIndicesOf(1);
if (indices.isEmpty())
throw new IllegalStateException("ForwardItem index is not set");
return indices.get(0);
}
protected int[] getListSlots() {
if (listSlots == null)
return findIndicesOf(2).stream().mapToInt(Integer::intValue).toArray();
else return listSlots;
}
public void setListSlots(int[] listSlots) {
this.listSlots = listSlots;
}
public void setListSlotIngredient(char key) {
setIngredient(key, 2);
}
public void setBackItem(char key, Function<PagedGUI, ItemBuilder> backFunction) {
setIngredient(key, 0);
this.backFunction = backFunction;
}
protected Function<PagedGUI, ItemBuilder> getBackFunction() {
return backFunction;
}
public void setForwardItem(char key, Function<PagedGUI, ItemBuilder> forwardFunction) {
setIngredient(key, 1);
this.forwardFunction = forwardFunction;
}
protected Function<PagedGUI, ItemBuilder> getForwardFunction() {
return forwardFunction;
}
}

@ -1,18 +0,0 @@
package de.studiocode.invgui.gui.builder.impl;
import de.studiocode.invgui.gui.impl.SimpleGUI;
public class SimpleGUIBuilder extends BaseGUIBuilder {
public SimpleGUIBuilder(int width, int height) {
super(width, height);
}
@Override
public SimpleGUI build() {
SimpleGUI gui = new SimpleGUI(getWidth(), getHeight());
setSlotElements(gui);
return gui;
}
}

@ -1,34 +0,0 @@
package de.studiocode.invgui.gui.builder.impl;
import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.impl.SimplePagedGUIs;
import java.util.List;
public class SimplePagedGUIsBuilder extends PagedGUIBuilder {
private List<GUI> guis;
public SimplePagedGUIsBuilder(int width, int height) {
super(width, height);
}
@Override
public SimplePagedGUIs build() {
if (getBackFunction() == null || getForwardFunction() == null)
throw new IllegalStateException("BackBuilder or ForwardBuilder haven't been set yet");
if (guis == null)
throw new IllegalStateException("GUIs haven't been set yet");
SimplePagedGUIs gui = new SimplePagedGUIs(getWidth(), getHeight(), getBackItemIndex(), getBackFunction(),
getForwardItemIndex(), getForwardFunction(), guis, getListSlots());
setSlotElements(gui);
return gui;
}
public void setGuis(List<GUI> guis) {
this.guis = guis;
}
}

@ -1,34 +0,0 @@
package de.studiocode.invgui.gui.builder.impl;
import de.studiocode.invgui.gui.impl.SimplePagedItemsGUI;
import de.studiocode.invgui.item.Item;
import java.util.List;
public class SimplePagedItemsGUIBuilder extends PagedGUIBuilder {
private List<Item> items;
public SimplePagedItemsGUIBuilder(int width, int height) {
super(width, height);
}
@Override
public SimplePagedItemsGUI build() {
if (getBackFunction() == null || getForwardFunction() == null)
throw new IllegalStateException("BackBuilder or ForwardBuilder haven't been set yet");
if (items == null)
throw new IllegalStateException("Items haven't been set yet");
SimplePagedItemsGUI gui = new SimplePagedItemsGUI(getWidth(), getHeight(), getBackItemIndex(), getBackFunction(),
getForwardItemIndex(), getForwardFunction(), items, getListSlots());
setSlotElements(gui);
return gui;
}
public void setItems(List<Item> items) {
this.items = items;
}
}

@ -1,23 +0,0 @@
package de.studiocode.invgui.gui.builder.impl;
import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.impl.SimpleTabGUI;
public class SimpleTabGUIBuilder extends TabGUIBuilder {
public SimpleTabGUIBuilder(int width, int height) {
super(width, height);
}
@Override
public GUI build() {
if (getTabItemSlots().length != getTabs().size())
throw new IllegalStateException("TabItemSlots length has to be the same as tabs size");
SimpleTabGUI tabGUI = new SimpleTabGUI(getWidth(), getHeight(), getTabs(), getListSlots(), getTabItemSlots());
setSlotElements(tabGUI);
return tabGUI;
}
}

@ -1,57 +0,0 @@
package de.studiocode.invgui.gui.builder.impl;
import de.studiocode.invgui.gui.GUI;
import de.studiocode.invgui.gui.impl.TabGUI;
import de.studiocode.invgui.item.itembuilder.ItemBuilder;
import java.util.LinkedHashMap;
import java.util.function.Function;
public abstract class TabGUIBuilder extends BaseGUIBuilder {
private final LinkedHashMap<GUI, Function<TabGUI, ItemBuilder>> tabs = new LinkedHashMap<>();
private int[] listSlots;
private int[] tabItemSlots;
public TabGUIBuilder(int width, int height) {
super(width, height);
}
public void addTab(GUI gui, Function<TabGUI, ItemBuilder> builderFunction) {
tabs.put(gui, builderFunction);
}
public void setListSlotIngredient(char key) {
setIngredient(key, 0);
}
public void setTabSlotIngredient(char key) {
setIngredient(key, 1);
}
public void setListSlots(int[] listSlots) {
this.listSlots = listSlots;
}
public void setTabItemSlots(int[] tabItemSlots) {
this.tabItemSlots = tabItemSlots;
}
protected int[] getListSlots() {
if (listSlots == null) {
return findIndicesOf(0).stream().mapToInt(Integer::intValue).toArray();
} else return listSlots;
}
protected int[] getTabItemSlots() {
if (tabItemSlots == null) {
return findIndicesOf(1).stream().mapToInt(Integer::intValue).toArray();
} else return tabItemSlots;
}
protected LinkedHashMap<GUI, Function<TabGUI, ItemBuilder>> getTabs() {
return tabs;
}
}