Merge pull request #395 from Protonull/remove-canvas

Yeet canvas
This commit is contained in:
okx-code
2024-04-05 00:25:14 +01:00
committed by GitHub
9 changed files with 0 additions and 967 deletions

View File

@@ -15,7 +15,6 @@ dependencies {
api("com.mojang:datafixerupper:1.0.20")
api("com.zaxxer:HikariCP:5.0.1")
api("co.aikar:taskchain-bukkit:3.7.2")
api("com.github.IPVP-MC:canvas:91ec97f076")
api("org.apache.commons:commons-lang3:3.12.0")
api("org.apache.commons:commons-collections4:4.4")
api("com.google.code.findbugs:jsr305:3.0.2")

View File

@@ -1,317 +0,0 @@
package org.ipvp.canvas.type;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import net.kyori.adventure.text.Component;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.ipvp.canvas.Menu;
import org.ipvp.canvas.slot.DefaultSlot;
import org.ipvp.canvas.slot.Slot;
import vg.civcraft.mc.civmodcore.chat.ChatUtils;
import vg.civcraft.mc.civmodcore.inventory.InventoryUtils;
import vg.civcraft.mc.civmodcore.inventory.gui.canvas.MenuUtils;
import vg.civcraft.mc.civmodcore.utilities.NullUtils;
public abstract class AbstractCivMenu extends AbstractMenu {
private static final Field VIEWERS_FIELD;
private static final Field SLOTS_FIELD;
private static final Method UPDATE_INV_CONTENTS_METHOD;
static {
VIEWERS_FIELD = FieldUtils.getDeclaredField(AbstractMenu.class, "viewers", true);
SLOTS_FIELD = FieldUtils.getDeclaredField(AbstractMenu.class, "slots", true);
UPDATE_INV_CONTENTS_METHOD = MethodUtils.getMatchingMethod(AbstractMenu.class,
"updateInventoryContents", Player.class, Inventory.class);
UPDATE_INV_CONTENTS_METHOD.setAccessible(true);
}
private final Component title;
private OpenHandler openHandler;
private UpdateHandler updateHandler;
protected boolean callCloseHandlerRegardless;
protected AbstractCivMenu(final Component title,
final int slots,
final Menu parent,
final boolean redraw) {
super(null, slots, parent, redraw);
if (!InventoryUtils.isValidChestSize(slots)) {
throw new IllegalArgumentException("Not a valid chest size.");
}
this.title = ChatUtils.isNullOrEmpty(title) ? InventoryType.CHEST.defaultTitle() : title;
}
protected AbstractCivMenu(final Component title,
final InventoryType type,
final Menu parent,
final boolean redraw) {
super(null, type, parent, redraw);
this.title = ChatUtils.isNullOrEmpty(title) ? type.defaultTitle() : title;
this.inventorySlots = type.getDefaultSize();
}
/**
* @return Returns a clone of this menu's title.
*/
protected Component getTitle() {
return this.title;
}
/**
* @return Retrieves this menu's open event handler.
*/
public Optional<OpenHandler> getOpenHandler() {
return Optional.ofNullable(this.openHandler);
}
/**
* Sets the open event handler for this menu. Use
* {@link MenuUtils#overrideOpenHandler(AbstractCivMenu, OpenHandler, boolean)} instead if you'd prefer to
* override the event handler rather than replace it.
*
* @param openHandler The open event handler to set for this menu.
*/
public void setOpenHandler(final OpenHandler openHandler) {
this.openHandler = openHandler;
}
/**
* @return Retrieves this menu's update event handler.
*/
public Optional<UpdateHandler> getUpdateHandler() {
return Optional.ofNullable(this.updateHandler);
}
/**
* Sets the update event handler for this menu. Use
* {@link MenuUtils#overrideUpdateHandler(AbstractCivMenu, UpdateHandler, boolean)} instead if you'd prefer to
* override the event handler rather than replace it.
*
* @param updateHandler The update event handler to set for this menu.
*/
public void setUpdateHandler(final UpdateHandler updateHandler) {
this.updateHandler = updateHandler;
}
/**
* Sets the close event handler for this menu. Use
* {@link MenuUtils#overrideCloseHandler(AbstractCivMenu, CloseHandler, boolean)} instead if you'd prefer to
* override the event handler rather than replace it.
*
* @param closeHandler The close event handler to set for this menu.
*/
@Override
public void setCloseHandler(final CloseHandler closeHandler) {
super.setCloseHandler(closeHandler);
}
/**
* @return Returns whether or not this menu will invoke its close event handler regardless of whether
* {@link #closedByPlayer(Player, boolean)}'s second parameter, "triggerCloseHandler", is false.
*/
public boolean isCallingCloseHandlerRegardless() {
return this.callCloseHandlerRegardless;
}
/**
* @param callCloseHandlerRegardless Set this to true if you want this menu to invoke its close event handler
* regardless of whether {@link #closedByPlayer(Player, boolean)}'s second
* parameter, "triggerCloseHandler", is false.
*/
public void setCallCloseHandlerRegardless(final boolean callCloseHandlerRegardless) {
this.callCloseHandlerRegardless = callCloseHandlerRegardless;
}
/**
* @return Returns the total slot count for this menu.
*/
protected int getSlotCount() {
return this.inventorySlots;
}
/**
* @return Returns the underlying viewers via reflection.
*/
@SuppressWarnings("unchecked")
protected final Set<MenuHolder> getRawViewers() {
try {
return (Set<MenuHolder>) FieldUtils.readField(VIEWERS_FIELD, this);
}
catch (final IllegalAccessException exception) {
throw new RuntimeException(exception);
}
}
/**
* @return Returns the underlying slots via reflection.
*/
protected final DefaultSlot[] getRawSlots() {
try {
return (DefaultSlot[]) FieldUtils.readField(SLOTS_FIELD, this);
}
catch (final IllegalAccessException exception) {
throw new RuntimeException(exception);
}
}
/**
* Switches from this menu to a given menu.
*
* @param viewer The viewer to switch.
* @param otherMenu The other menu to switch to.
*/
public void openOtherMenu(final Player viewer, final AbstractCivMenu otherMenu) {
if (otherMenu == null) {
viewer.closeInventory();
return;
}
otherMenu.open(viewer);
}
/**
* Opens this menu's parent, or closes if it doesn't exist.
*
* @param viewer The viewer to navigate.
*/
public void openParent(final Player viewer) {
final var parent = getParent().orElse(null);
if (parent == null) {
viewer.closeInventory();
return;
}
if (parent instanceof AbstractCivMenu) {
((AbstractCivMenu) parent).openOtherMenu(viewer, this);
return;
}
parent.open(viewer);
}
/**
* Basically a carbon copy of {@link AbstractMenu#closedByPlayer(Player, boolean)}, except
* for the additional logic of {@link #callCloseHandlerRegardless} to force the menu to call
* the close handler even if {@code triggerCloseHandler} is false.
*
* @param viewer The closing viewer.
* @param triggerCloseHandler Whether to call the close handler, which defers to
* {@link #callCloseHandlerRegardless}.
*/
@Override
public void closedByPlayer(final Player viewer, final boolean triggerCloseHandler) {
final var currentInventory = viewer.getOpenInventory().getTopInventory().getHolder();
final var viewers = getRawViewers();
if (!(currentInventory instanceof MenuHolder)
|| !viewers.contains(currentInventory)) {
return;
}
if (triggerCloseHandler || this.callCloseHandlerRegardless) {
getCloseHandler().ifPresent(handler -> handler.close(viewer, this));
}
viewers.remove((MenuHolder) currentInventory);
}
/**
* @param viewer The viewer to update this menu for.
*/
@Override
public void update(final Player viewer) throws IllegalStateException {
if (!isOpen(viewer)) {
return;
}
final var currentHolder = viewer.getOpenInventory().getTopInventory().getHolder();
final var currentInventory = NullUtils.isNotNull(currentHolder).getInventory();
for (final Slot slot : getRawSlots()) {
currentInventory.setItem(slot.getIndex(), slot.getItem(viewer));
}
getUpdateHandler().ifPresent(handler -> handler.handle(viewer, this));
viewer.updateInventory();
}
/**
* Basically a carbon copy of {@link AbstractMenu#open(Player)}, which is needed to override the
* {@code createInventory()} method, but it's private so the entire function needs to be copied,
* alongside its other dependent private methods.
*/
@Override
public void open(final Player viewer) {
final var currentHolder = viewer.getOpenInventory().getTopInventory().getHolder();
final MenuHolder holder;
if (currentHolder instanceof MenuHolder) {
holder = (MenuHolder) currentHolder;
final var currentMenu = holder.getMenu();
if (currentMenu == this) {
return;
}
Inventory inventory;
if (isRedraw() && Objects.equals(getDimensions(), currentMenu.getDimensions())) {
inventory = holder.getInventory();
if (currentMenu instanceof AbstractMenu) {
((AbstractMenu) currentMenu).closedByPlayer(viewer, false);
}
}
else {
currentMenu.close(viewer);
inventory = createInventory(holder);
holder.setInventory(inventory);
viewer.openInventory(inventory);
}
updateInventoryContents(viewer, inventory);
holder.setMenu(this);
}
else {
// Create new MenuHolder for the player
holder = new MenuHolder(viewer, this);
final var inventory = createInventory(holder);
updateInventoryContents(viewer, inventory);
holder.setInventory(inventory);
viewer.openInventory(inventory);
}
getRawViewers().add(holder);
getOpenHandler().ifPresent(handler -> handler.handle(viewer, this));
}
/**
* Basically a carbon copy of {@link AbstractMenu}'s version, but uses a component title instead.
*
* @param holder The menu's inventory holder.
* @return Returns the new inventory.
*/
protected Inventory createInventory(final InventoryHolder holder) {
if (this.inventoryType == null) {
return Bukkit.createInventory(holder, this.inventorySlots, this.title);
}
return Bukkit.createInventory(holder, this.inventoryType, this.title);
}
/**
* Invokes {@link AbstractMenu}'s version via reflection.
*
* @param viewer The viewer to update.
* @param inventory The inventory to update.
*/
protected void updateInventoryContents(final Player viewer, final Inventory inventory) {
try {
UPDATE_INV_CONTENTS_METHOD.invoke(this, viewer, inventory);
}
catch (final InvocationTargetException | IllegalAccessException exception) {
throw new RuntimeException(exception);
}
}
}

View File

@@ -1,91 +0,0 @@
package org.ipvp.canvas.type;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.apache.commons.lang3.StringUtils;
import org.ipvp.canvas.Menu;
import vg.civcraft.mc.civmodcore.chat.ChatUtils;
public class CivChestMenu extends AbstractCivMenu {
private Dimension dimension;
public CivChestMenu(final Component title, final int rows) {
this(title, rows, true);
}
public CivChestMenu(final Component title, final int rows, final boolean redraw) {
this(title, rows, null, redraw);
}
public CivChestMenu(final Component title, final int rows, final Menu parent, final boolean redraw) {
super(title, rows * 9, parent, redraw);
}
/**
* @return Return this chest menu's row and column counts.
*/
@Override
public Dimension getDimensions() {
if (this.dimension == null) {
this.dimension = new Dimension(getSlotCount() / 9, 9);
}
return this.dimension;
}
/**
* Returns a new builder.
*
* @param rows The amount of rows for the inventory to contain
* @throws IllegalArgumentException if rows is not between 1 and 6 inclusive
*/
public static Builder builder(final int rows) {
if (rows < 1 || rows > 6) {
throw new IllegalArgumentException("rows must be a value from 1 to 6");
}
return new Builder(rows);
}
/**
* A builder for creating a CustomChestMenu instance.
*/
public static class Builder extends AbstractMenu.Builder<Builder> {
private Component title;
Builder(final int rows) {
super(new Dimension(rows, 9));
}
@Deprecated
@Override
public Builder title(final String title) {
if (StringUtils.isBlank(title)) {
this.title = null;
return this;
}
this.title = LegacyComponentSerializer.legacyAmpersand().deserialize(title);
if (ChatUtils.isNullOrEmpty(this.title)) {
this.title = null;
}
return this;
}
public Builder title(final Component title) {
this.title = title;
return this;
}
public Component getComponentTitle() {
return this.title;
}
@Override
public CivChestMenu build() {
return new CivChestMenu(getComponentTitle(),
getDimensions().getArea(), getParent(), isRedraw());
}
}
}

View File

@@ -1,16 +0,0 @@
package org.ipvp.canvas.type;
import org.bukkit.entity.Player;
@FunctionalInterface
public interface OpenHandler {
/**
* Is called by {@link AbstractCivMenu} when one of its menus are opened by a viewer.
*
* @param viewer The viewer of the opened menu.
* @param menu The menu being opened.
*/
void handle(Player viewer, AbstractCivMenu menu);
}

View File

@@ -1,17 +0,0 @@
package org.ipvp.canvas.type;
import org.bukkit.entity.Player;
@FunctionalInterface
public interface UpdateHandler {
/**
* Is called by {@link AbstractCivMenu} when one of its menus are updated. This is called after the menu has been
* updated but before the update packets have been sent to the player.
*
* @param viewer The viewer of the updating menu.
* @param menu The menu being updated.
*/
void handle(Player viewer, AbstractCivMenu menu);
}

View File

@@ -7,7 +7,6 @@ import org.bukkit.configuration.serialization.ConfigurationSerialization;
import org.bukkit.entity.HumanEntity;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPluginLoader;
import org.ipvp.canvas.MenuFunctionListener;
import vg.civcraft.mc.civmodcore.chat.dialog.DialogManager;
import vg.civcraft.mc.civmodcore.commands.ChunkMetaCommand;
import vg.civcraft.mc.civmodcore.commands.CommandManager;
@@ -85,7 +84,6 @@ public class CivModCorePlugin extends ACivMod {
}
ScoreBoardAPI.setDefaultHeader(this.config.getScoreboardHeader());
// Register listeners
registerListener(new MenuFunctionListener());
registerListener(new ClickableInventoryListener());
registerListener(DialogManager.INSTANCE);
registerListener(new ScoreBoardListener());

View File

@@ -1,241 +0,0 @@
package vg.civcraft.mc.civmodcore.inventory.gui.canvas;
import it.unimi.dsi.fastutil.objects.Object2ObjectAVLTreeMap;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import org.apache.commons.collections4.CollectionUtils;
import org.bukkit.entity.Player;
import org.ipvp.canvas.slot.SlotSettings;
import org.ipvp.canvas.type.AbstractCivMenu;
/**
* Better version of {@link org.ipvp.canvas.paginate.PaginatedMenuBuilder} as it allows you to apply pagination to an
* already existing menu, rather than using a builder to spawn a bunch of new menu instances that'll just be used by a
* single player before promptly discarded.
*/
public final class MenuPagination {
private final AbstractCivMenu menu;
private final Map<UUID, INTERNAL_PlayerPager> playerPages;
private final List<SlotSettings> menuBar;
private final ElementSupplier supplier;
private final int buttonSlots;
private MenuPagination(final AbstractCivMenu menu,
final ElementSupplier supplier,
final List<SlotSettings> menuBar) {
this.menu = Objects.requireNonNull(menu, "Menu cannot be null");
final int rows = menu.getDimensions().getRows();
if (rows < 2 || rows > 5) {
throw new IllegalArgumentException("Row count must be between 2 and 5 inclusively");
}
this.playerPages = new Object2ObjectAVLTreeMap<>();
this.menuBar = List.copyOf(menuBar);
if (menuBar.size() > 9) {
throw new IllegalArgumentException("Bar cannot contain more than 9 buttons");
}
this.supplier = Objects.requireNonNull(supplier, "Element supplier cannot be null");
this.buttonSlots = (rows * 9) - 9;
MenuUtils.overrideOpenHandler(menu, (viewer, opened) -> {
final var pager = INTERNAL_getPlayerPager(viewer);
pager.pageNumber = 0;
pager.elements = this.supplier.generate(viewer, opened);
INTERNAL_applyPlayerPage(viewer, opened, INTERNAL_getPlayerPager(viewer));
}, true);
MenuUtils.overrideUpdateHandler(menu, (viewer, updated) -> {
INTERNAL_applyPlayerPage(viewer, updated, INTERNAL_getPlayerPager(viewer));
}, true);
menu.setCallCloseHandlerRegardless(true);
MenuUtils.overrideCloseHandler(menu, (viewer, ignored) -> {
this.playerPages.remove(viewer.getUniqueId());
}, true);
for (int i = 0; i < this.buttonSlots; i++) {
menu.getSlot(i).setClickHandler((viewer, click) -> {
final var pager = INTERNAL_getPlayerPager(viewer);
final int buttonIndex = INTERNAL_calculateElementIndex(pager, click.getClickedSlot().getIndex());
if (buttonIndex >= pager.elements.size()) {
return;
}
final var handler = pager.elements.get(buttonIndex).getClickHandler();
if (handler == null) {
return;
}
handler.click(viewer, click);
});
}
for (int i = 0; i < 9; i++) {
final var slotIndex = INTERNAL_calculateBarIndex(i);
if (i >= this.menuBar.size()) {
menu.getSlot(slotIndex).setSettings(MenuUtils.createEmptySlot());
break;
}
final var barElement = this.menuBar.get(i);
if (barElement == null) {
menu.getSlot(slotIndex).setSettings(MenuUtils.createEmptySlot());
continue;
}
menu.getSlot(slotIndex).setSettings(barElement);
}
}
/**
* @param viewer The viewer to send to the next page.
*/
public void nextPage(final Player viewer) {
final var pager = INTERNAL_getPlayerPager(viewer);
pager.pageNumber++;
this.menu.update(viewer);
}
/**
* @param viewer The viewer to send to the previous page.
*/
public void previousPage(final Player viewer) {
final var pager = INTERNAL_getPlayerPager(viewer);
pager.pageNumber--;
this.menu.update(viewer);
}
/**
* @param viewer The viewer to check if they have a next page.
* @return Returns whether the viewer has a next page.
*/
public boolean hasNextPage(final Player viewer) {
final var pager = INTERNAL_getPlayerPager(viewer);
final int elementsSize = CollectionUtils.size(pager.elements);
final int totalPages = elementsSize / this.buttonSlots;
if (totalPages == 0) {
return false;
}
return pager.pageNumber >= totalPages;
}
/**
* @param viewer The viewer to check if they have a previous page.
* @return Returns whether the viewer has a previous page.
*/
public boolean hasPreviousPage(final Player viewer) {
final var pager = INTERNAL_getPlayerPager(viewer);
final int elementsSize = CollectionUtils.size(pager.elements);
final int totalPages = elementsSize / this.buttonSlots;
if (totalPages == 0) {
return false;
}
return pager.pageNumber < totalPages;
}
protected void INTERNAL_applyPlayerPage(final Player viewer,
final AbstractCivMenu menu,
final INTERNAL_PlayerPager pager) {
pager.pageNumber = Math.max(pager.pageNumber, 0);
pager.pageNumber = Math.min(pager.pageNumber, pager.elements.size() / this.buttonSlots);
for (int i = 0; i < this.buttonSlots; i++) {
final int buttonIndex = INTERNAL_calculateElementIndex(pager, i);
if (buttonIndex >= pager.elements.size()) {
menu.getSlot(i).setRawItem(viewer, null);
continue;
}
final var template = pager.elements.get(buttonIndex).getItemTemplate();
if (template == null) {
menu.getSlot(i).setRawItem(viewer, null);
continue;
}
menu.getSlot(i).setRawItem(viewer, template.getItem(viewer));
}
}
protected INTERNAL_PlayerPager INTERNAL_getPlayerPager(final Player viewer) {
return this.playerPages.computeIfAbsent(viewer.getUniqueId(), (uuid) -> new INTERNAL_PlayerPager());
}
protected int INTERNAL_calculateElementIndex(final INTERNAL_PlayerPager pager, final int slot) {
return (pager.pageNumber * this.buttonSlots) + slot;
}
protected int INTERNAL_calculateBarIndex(final int slot) {
return this.buttonSlots + slot;
}
protected static class INTERNAL_PlayerPager {
private int pageNumber;
private List<SlotSettings> elements;
}
@FunctionalInterface
public interface ElementSupplier {
List<SlotSettings> generate(Player viewer, AbstractCivMenu menu);
}
/**
* @return Returns a new pagination builder.
*/
public static Builder builder() {
return new Builder();
}
public static class Builder {
private AbstractCivMenu menu;
private ElementSupplier supplier;
private final SlotSettings[] menuBar;
Builder() {
this.menuBar = new SlotSettings[] {
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot(),
MenuUtils.createEmptySlot()
};
}
/**
* @param menu The menu to paginate.
* @return Returns this builder.
*/
public Builder setMenu(final AbstractCivMenu menu) {
this.menu = menu;
return this;
}
/**
* @param supplier Supplier method to return all the elements to be paginated.
* @return Returns this builder.
*/
public Builder setElementSupplier(final ElementSupplier supplier) {
this.supplier = supplier;
return this;
}
/**
* @param index The index of the menu bar element from 0-8 inclusively.
* @param element The menu bar element to add.
* @return Returns this builder.
*/
public Builder setMenuBarElement(final int index, final SlotSettings element) {
this.menuBar[index] = element == null ? MenuUtils.createEmptySlot() : element;
return this;
}
/**
* @return Returns a constructed menu pagination based on this builder.
*/
public MenuPagination build() {
return new MenuPagination(this.menu, this.supplier, Arrays.asList(this.menuBar));
}
}
}

View File

@@ -1,102 +0,0 @@
package vg.civcraft.mc.civmodcore.inventory.gui.canvas;
import org.ipvp.canvas.Menu;
import org.ipvp.canvas.slot.SlotSettings;
import org.ipvp.canvas.type.AbstractCivMenu;
import org.ipvp.canvas.type.OpenHandler;
import org.ipvp.canvas.type.UpdateHandler;
public final class MenuUtils {
/**
* @return Returns an empty, unmodifiable slot.
*/
public static SlotSettings createEmptySlot() {
return SlotSettings.builder().build();
}
/**
* Overrides a given menu's open event handler, in lieu of just replacing it, meaning that you can add handlers to
* a menu without fear of removing any previously added functionality.
*
* @param menu The menu to add the handler for.
* @param handler The handler to add.
* @param callSuperBefore Whether or not to call the existing handler before or after the given handler.
*/
public static void overrideOpenHandler(final AbstractCivMenu menu,
final OpenHandler handler,
final boolean callSuperBefore) {
final var findExisting = menu.getOpenHandler();
if (findExisting.isEmpty()) {
menu.setOpenHandler(handler);
return;
}
final var existing = findExisting.get();
menu.setOpenHandler((viewer, openedMenu) -> {
if (callSuperBefore) {
existing.handle(viewer, openedMenu);
}
handler.handle(viewer, openedMenu);
if (!callSuperBefore) {
existing.handle(viewer, openedMenu);
}
});
}
/**
* Overrides a given menu's update event handler, in lieu of just replacing it, meaning that you can add handlers
* to a menu without fear of removing any previously added functionality.
*
* @param menu The menu to add the handler for.
* @param handler The handler to add.
* @param callSuperBefore Whether or not to call the existing handler before or after the given handler.
*/
public static void overrideUpdateHandler(final AbstractCivMenu menu,
final UpdateHandler handler,
final boolean callSuperBefore) {
final var findExisting = menu.getUpdateHandler();
if (findExisting.isEmpty()) {
menu.setUpdateHandler(handler);
return;
}
final var existing = findExisting.get();
menu.setUpdateHandler((viewer, openedMenu) -> {
if (callSuperBefore) {
existing.handle(viewer, openedMenu);
}
handler.handle(viewer, openedMenu);
if (!callSuperBefore) {
existing.handle(viewer, openedMenu);
}
});
}
/**
* Overrides a given menu's close event handler, in lieu of just replacing it, meaning that you can add handlers to
* a menu without fear of removing any previously added functionality.
*
* @param menu The menu to add the handler for.
* @param handler The handler to add.
* @param callSuperBefore Whether or not to call the existing handler before or after the given handler.
*/
public static void overrideCloseHandler(final AbstractCivMenu menu,
final Menu.CloseHandler handler,
final boolean callSuperBefore) {
final var findExisting = menu.getCloseHandler();
if (findExisting.isEmpty()) {
menu.setCloseHandler(handler);
return;
}
final var existing = findExisting.get();
menu.setCloseHandler((viewer, openedMenu) -> {
if (callSuperBefore) {
existing.close(viewer, openedMenu);
}
handler.close(viewer, openedMenu);
if (!callSuperBefore) {
existing.close(viewer, openedMenu);
}
});
}
}

View File

@@ -1,180 +0,0 @@
package vg.civcraft.mc.civmodcore.inventory.gui.canvas;
import java.util.List;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.ipvp.canvas.slot.SlotSettings;
import org.ipvp.canvas.type.AbstractCivMenu;
import org.ipvp.canvas.type.CivChestMenu;
import vg.civcraft.mc.civmodcore.inventory.InventoryUtils;
import vg.civcraft.mc.civmodcore.inventory.items.ItemUtils;
/**
* Utility to automate creating views, which have multiple pages and automatically adjust their size
*
*/
public class MultiPageCanvas {
private final Player viewer;
private final Component title;
private final List<SlotSettings> slots;
private final boolean adjustSize;
private final SlotSettings[] extraMenuItems;
private AbstractCivMenu window;
private int currentPage;
public MultiPageCanvas(@Nonnull final Player viewer,
@Nullable final Component title,
@Nonnull final List<SlotSettings> slots,
final boolean adjustSize) {
this.viewer = Objects.requireNonNull(viewer);
this.title = title;
this.slots = List.copyOf(slots);
this.adjustSize = adjustSize;
this.extraMenuItems = new SlotSettings[7];
this.currentPage = 0;
}
private void INTERNAL_constructInventory() {
// Clamp the total page within the valid range
if (this.currentPage < 0) {
this.currentPage = 0;
}
final int totalPages = INTERNAL_calculateTotalPages();
if (this.currentPage >= totalPages) {
this.currentPage = totalPages - 1;
}
// Update the window instance if necessary
final int totalRows = INTERNAL_calculateTotalRows();
if (this.window == null) {
this.window = new CivChestMenu(this.title, totalRows);
}
else {
this.window.clear();
if (this.adjustSize && totalRows != this.window.getDimensions().getRows()) {
this.window = new CivChestMenu(this.title, totalRows);
}
}
// Add all content slots
final int contentOffset = this.currentPage * InventoryUtils.CHEST_5_ROWS;
final int contentLength = Math.min(this.slots.size() - contentOffset, InventoryUtils.CHEST_5_ROWS);
for (int i = 0; i < contentLength; i++) {
this.window.getSlot(i).setSettings(this.slots.get(contentOffset + i));
}
final int totalSlots = this.window.getDimensions().getArea();
// Back button
if (this.currentPage > 0) {
this.window.getSlot(totalSlots - 10).setSettings(SlotSettings.builder()
.itemTemplate((ignored) -> {
final var item = new ItemStack(Material.ARROW);
ItemUtils.handleItemMeta(item, (ItemMeta meta) -> {
meta.displayName(Component.text()
.color(NamedTextColor.GOLD)
.content("Go to previous page")
.build());
return true;
});
return item;
})
.clickHandler((clicker, click) -> {
if (MultiPageCanvas.this.currentPage > 0) {
MultiPageCanvas.this.currentPage--;
}
showScreen();
})
.build());
}
// Next button
if (this.currentPage < totalPages) {
this.window.getSlot(totalSlots - 1).setSettings(SlotSettings.builder()
.itemTemplate((ignored) -> {
final var item = new ItemStack(Material.ARROW);
ItemUtils.handleItemMeta(item, (ItemMeta meta) -> {
meta.displayName(Component.text()
.color(NamedTextColor.GOLD)
.content("Go to next page")
.build());
return true;
});
return item;
})
.clickHandler((clicker, click) -> {
if (MultiPageCanvas.this.currentPage < totalPages) {
MultiPageCanvas.this.currentPage++;
}
showScreen();
})
.build());
}
// Other menu items
for (int i = 0; i < this.extraMenuItems.length; i++) {
final SlotSettings slot = this.extraMenuItems[i];
if (slot == null) {
continue;
}
this.window.getSlot(totalSlots - 9 + i).setSettings(slot);
}
}
protected int INTERNAL_calculateTotalPages() {
final int size = this.slots.size();
int pages = size / InventoryUtils.CHEST_5_ROWS;
if ((size % InventoryUtils.CHEST_5_ROWS) > 0) {
pages++;
}
return Math.max(pages, 0);
}
private int INTERNAL_calculateTotalRows() {
if (!this.adjustSize) {
return 6;
}
final int clicks = this.slots.size();
if (clicks >= InventoryUtils.CHEST_5_ROWS) {
return 6;
}
final int rows = (clicks / 9) + ((clicks % 9) > 0 ? 1 : 0);
return Math.max(rows, 2); // Always have at least 2 rows
}
/**
* Shows the current page
*/
public void showScreen() {
INTERNAL_constructInventory();
this.window.open(this.viewer);
}
/**
* Allows setting a menu slot at the bottom of the GUI. Will not update the inventory, you'll need to do that
* manually with {@link #showScreen()}.
*
* @param slot The slot to set.
* @param index The index of the slot. Must be between 0 and 6 inclusively.
*/
public void setMenuSlot(@Nullable final SlotSettings slot, final int index) {
if (index < 0 || index > 6) {
throw new IllegalArgumentException("Index for MultiPageView menu must be between 0 and 6");
}
this.extraMenuItems[index] = slot;
}
/**
* Forcibly changes the page of this view. Will not update the inventory, you'll need to do that manually with
* {@link #showScreen()}.
*
* @param page The page to set.
*/
public void setPage(final int page) {
this.currentPage = page;
}
}