mirror of
https://github.com/plexusorg/Plex.git
synced 2026-06-04 05:26:55 +00:00
Overhaul menu system and simplify it heavily, separating Pageable Menus and regular Menus into two different implementations
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
package dev.plex.menu.pagination;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import dev.plex.menu.AbstractMenu;
|
||||
import dev.plex.util.PlexUtils;
|
||||
import dev.plex.util.function.ConditionalQuadConsumer;
|
||||
import dev.plex.util.item.ItemBuilder;
|
||||
import dev.plex.util.minimessage.SafeMiniMessage;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryView;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Taah
|
||||
* @since 8:04 AM [02-09-2023]
|
||||
*/
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(fluent = true)
|
||||
public abstract class PageableMenu<T>
|
||||
{
|
||||
public static final ItemStack NEXT = new ItemBuilder(Material.FEATHER).displayName("<light_purple>Next Page").build();
|
||||
public static final ItemStack PREVIOUS = new ItemBuilder(Material.FEATHER).displayName("<light_purple>Previous Page").build();
|
||||
public static final ItemStack CLOSE = new ItemBuilder(Material.BARRIER).displayName("<red>Close").build();
|
||||
protected final Map<Integer, Page> pages = Maps.newHashMap();
|
||||
private final Component name;
|
||||
private final AbstractMenu.Rows rows;
|
||||
private final Inventory inventory;
|
||||
private int currentPage;
|
||||
private boolean initialized;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
private ConditionalQuadConsumer<InventoryView, Inventory, Player, ItemStack> onClick;
|
||||
|
||||
public PageableMenu(Component name, AbstractMenu.Rows rows)
|
||||
{
|
||||
this.name = name;
|
||||
this.rows = rows;
|
||||
this.inventory = Bukkit.createInventory(null, rows.slots(), name);
|
||||
|
||||
if (rows.slots() < AbstractMenu.Rows.TWO.slots())
|
||||
{
|
||||
throw new RuntimeException("A pageable menu must have at least two rows to compensate for the navigation!");
|
||||
}
|
||||
}
|
||||
|
||||
protected void init()
|
||||
{
|
||||
this.initialized = true;
|
||||
// Preload all needed pages, a better solution is probably just to dynamically create the pages as they go but no!
|
||||
if (list().size() < (rows.slots() - 9))
|
||||
{
|
||||
final Page page = new Page(name.append(Component.space()).append(PlexUtils.mmDeserialize("(1)")), rows);
|
||||
page.parent = this;
|
||||
page.inventory().setItem(rows.slots() - 5, CLOSE);
|
||||
this.pages.put(0, page);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < Math.ceil(list().size() / (double) (rows.slots() - 9)); i++)
|
||||
{
|
||||
final Page page = new Page(name.append(Component.space()).append(PlexUtils.mmDeserialize("(" + (i + 1) + ")")), rows);
|
||||
page.parent = this;
|
||||
if (i > 0) // If not first page set previous page button
|
||||
{
|
||||
page.inventory().setItem(rows.slots() - 6, PREVIOUS);
|
||||
}
|
||||
page.inventory().setItem(rows.slots() - 5, CLOSE);
|
||||
if (i < (list().size() / (rows.slots() - 9)) - 1) // If not last page set next page button
|
||||
{
|
||||
page.inventory().setItem(rows.slots() - 4, NEXT);
|
||||
}
|
||||
this.pages.put(i, page);
|
||||
}
|
||||
}
|
||||
|
||||
int currentSlotIndex = 0;
|
||||
int currentPageIndex = 0;
|
||||
int currentIndex = 0;
|
||||
while (currentSlotIndex < rows.slots() - 9 && currentIndex < list().size())
|
||||
{
|
||||
final Page page = this.pages.get(currentPageIndex);
|
||||
if (page == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
page.inventory().setItem(currentSlotIndex, toItem(list().get(currentIndex)));
|
||||
currentIndex++;
|
||||
currentSlotIndex++;
|
||||
if (currentSlotIndex == rows.slots() - 9)
|
||||
{
|
||||
currentSlotIndex = 0;
|
||||
currentPageIndex++;
|
||||
}
|
||||
}
|
||||
if (onClick != null) // To make sure this wasn't declared already
|
||||
{
|
||||
this.pages.forEach((integer, page) -> page.onClick(this.onClick));
|
||||
}
|
||||
}
|
||||
|
||||
public PageableMenu(String name, AbstractMenu.Rows rows)
|
||||
{
|
||||
this(SafeMiniMessage.mmDeserializeWithoutEvents(name), rows);
|
||||
}
|
||||
|
||||
public void open(Player player)
|
||||
{
|
||||
open(player, 0);
|
||||
}
|
||||
|
||||
public void open(Player player, int pageNum)
|
||||
{
|
||||
if (!this.initialized)
|
||||
{
|
||||
player.sendMessage(Component.text("Looks like this inventory was not initialized! Please contact a developer to report this.").color(NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
final Page page = this.pages.get(pageNum);
|
||||
if (page == null)
|
||||
{
|
||||
player.sendMessage(Component.text("Could not find a page to open").color(NamedTextColor.RED));
|
||||
return;
|
||||
}
|
||||
player.openInventory(page.inventory());
|
||||
}
|
||||
|
||||
public void onClick(ConditionalQuadConsumer<InventoryView, Inventory, Player, ItemStack> onClick)
|
||||
{
|
||||
this.onClick = onClick;
|
||||
this.pages.forEach((integer, page) -> page.onClick(this.onClick));
|
||||
}
|
||||
|
||||
protected abstract ItemStack toItem(T object);
|
||||
|
||||
protected abstract List<T> list();
|
||||
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Accessors(fluent = true)
|
||||
public static class Page extends AbstractMenu
|
||||
{
|
||||
private ConditionalQuadConsumer<InventoryView, Inventory, Player, ItemStack> onClick;
|
||||
private PageableMenu<?> parent;
|
||||
|
||||
private Page(Component name, Rows rows)
|
||||
{
|
||||
super(name, rows);
|
||||
}
|
||||
|
||||
private Page(String name, AbstractMenu.Rows rows)
|
||||
{
|
||||
this(SafeMiniMessage.mmDeserializeWithoutEvents(name), rows);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onClick(InventoryView view, Inventory inventory, Player player, ItemStack clicked)
|
||||
{
|
||||
return onClick.accept(view, inventory, player, clicked);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user