Skip to content

Instantly share code, notes, and snippets.

@I-Al-Istannen
Last active February 23, 2024 15:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save I-Al-Istannen/eba17ebda69b03443b175a4e20712359 to your computer and use it in GitHub Desktop.
Save I-Al-Istannen/eba17ebda69b03443b175a4e20712359 to your computer and use it in GitHub Desktop.
A BadPagedPane without any dependencies
package me.ialistannen.modularcoretest.doggy;
import java.util.Objects;
import java.util.function.Consumer;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.ItemStack;
/**
* A bad button
*/
public class BadButton {
private static int counter;
private final int ID = counter++;
private ItemStack itemStack;
private Consumer<InventoryClickEvent> action;
/**
* @param itemStack The Item
*/
@SuppressWarnings("unused")
public BadButton(ItemStack itemStack) {
this(itemStack, event -> {
});
}
/**
* @param itemStack The Item
* @param action The action
*/
public BadButton(ItemStack itemStack, Consumer<InventoryClickEvent> action) {
this.itemStack = itemStack;
this.action = action;
}
/**
* @return The icon
*/
@SuppressWarnings("WeakerAccess")
public ItemStack getItemStack() {
return itemStack;
}
/**
* @param action The new action
*/
@SuppressWarnings("unused")
public void setAction(Consumer<InventoryClickEvent> action) {
this.action = action;
}
/**
* @param event The event that triggered it
*/
@SuppressWarnings("WeakerAccess")
public void onClick(InventoryClickEvent event) {
action.accept(event);
}
// We do not want equals collisions. The default hashcode would not fulfil this contract.
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof BadButton)) {
return false;
}
BadButton button = (BadButton) o;
return ID == button.ID;
}
@Override
public int hashCode() {
return Objects.hash(ID);
}
}
package me.ialistannen.modularcoretest.doggy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
/**
* A bad paged pane
*/
public class BadPagedPane implements InventoryHolder {
private Inventory inventory;
private SortedMap<Integer, Page> pages = new TreeMap<>();
private int currentIndex;
private int pageSize;
@SuppressWarnings("WeakerAccess")
protected BadButton controlBack;
@SuppressWarnings("WeakerAccess")
protected BadButton controlNext;
/**
* @param pageSize The page size. inventory rows - 2
*/
public BadPagedPane(int pageSize, int rows, String title) {
Objects.requireNonNull(title, "title can not be null!");
if (rows < 3) {
throw new IllegalArgumentException("Rows must be >= 3, got " + rows);
}
if (rows > 6) {
throw new IllegalArgumentException("Rows must be <= 6, got " + rows);
}
if (pageSize > 6) {
throw new IllegalArgumentException("Page size must be <= 6, got" + pageSize);
}
this.pageSize = pageSize;
inventory = Bukkit.createInventory(this, rows * 9, color(title));
pages.put(0, new Page(pageSize));
}
/**
* @param button The button to add
*/
public void addButton(BadButton button) {
for (Entry<Integer, Page> entry : pages.entrySet()) {
if (entry.getValue().addButton(button)) {
if (entry.getKey() == currentIndex) {
reRender();
}
return;
}
}
Page page = new Page(pageSize);
page.addButton(button);
pages.put(pages.lastKey() + 1, page);
reRender();
}
/**
* @param button The Button to remove
*/
@SuppressWarnings("unused")
public void removeButton(BadButton button) {
for (Iterator<Entry<Integer, Page>> iterator = pages.entrySet().iterator(); iterator.hasNext(); ) {
Entry<Integer, Page> entry = iterator.next();
if (entry.getValue().removeButton(button)) {
// we may need to delete the page
if (entry.getValue().isEmpty()) {
// we have more than one page, so delete it
if (pages.size() > 1) {
iterator.remove();
}
// the currentIndex now points to a page that does not exist. Correct it.
if (currentIndex >= pages.size()) {
currentIndex--;
}
}
// if we modified the current one, re-render
// if we deleted the current page, re-render too
if (entry.getKey() >= currentIndex) {
reRender();
}
return;
}
}
}
/**
* @return The amount of pages
*/
@SuppressWarnings("WeakerAccess")
public int getPageAmount() {
return pages.size();
}
/**
* @return The number of the current page (1 based)
*/
@SuppressWarnings("WeakerAccess")
public int getCurrentPage() {
return currentIndex + 1;
}
/**
* @param index The index of the new page
*/
@SuppressWarnings("WeakerAccess")
public void selectPage(int index) {
if (index < 0 || index >= getPageAmount()) {
throw new IllegalArgumentException(
"Index out of bounds: " + index + " [" + 0 + " " + getPageAmount() + ")"
);
}
if (index == currentIndex) {
return;
}
currentIndex = index;
reRender();
}
/**
* Renders the inventory again
*/
@SuppressWarnings("WeakerAccess")
public void reRender() {
inventory.clear();
pages.get(currentIndex).render(inventory);
controlBack = null;
controlNext = null;
createControls(inventory);
}
/**
* @param event The {@link InventoryClickEvent}
*/
@SuppressWarnings("WeakerAccess")
public void onClick(InventoryClickEvent event) {
event.setCancelled(true);
// back item
if (event.getSlot() == inventory.getSize() - 8) {
if (controlBack != null) {
controlBack.onClick(event);
}
return;
}
// next item
else if (event.getSlot() == inventory.getSize() - 2) {
if (controlNext != null) {
controlNext.onClick(event);
}
return;
}
pages.get(currentIndex).handleClick(event);
}
/**
* Get the object's inventory.
*
* @return The inventory.
*/
@Override
public Inventory getInventory() {
return inventory;
}
/**
* Creates the controls
*
* @param inventory The inventory
*/
@SuppressWarnings("WeakerAccess")
protected void createControls(Inventory inventory) {
// create separator
fillRow(
inventory.getSize() / 9 - 2,
getItemStack(Material.STAINED_GLASS_PANE, 15, ""),
inventory
);
if (getCurrentPage() > 1) {
String name = String.format(
Locale.ROOT,
"&3&lPage &a&l%d &7/ &c&l%d",
getCurrentPage() - 1, getPageAmount()
);
String lore = String.format(
Locale.ROOT,
"&7Brings you back to the page &c%d",
getCurrentPage() - 1
);
ItemStack itemStack = getItemStack(Material.COAL_BLOCK, 0, name, lore);
controlBack = new BadButton(itemStack, event -> selectPage(currentIndex - 1));
inventory.setItem(inventory.getSize() - 8, itemStack);
}
if (getCurrentPage() < getPageAmount()) {
String name = String.format(
Locale.ROOT,
"&3&lPage &a&l%d &7/ &c&l%d",
getCurrentPage() + 1, getPageAmount()
);
String lore = String.format(
Locale.ROOT,
"&7Brings you to the page &c%d",
getCurrentPage() + 1
);
ItemStack itemStack = getItemStack(Material.IRON_BLOCK, 0, name, lore);
controlNext = new BadButton(itemStack, event -> selectPage(getCurrentPage()));
inventory.setItem(inventory.getSize() - 2, itemStack);
}
{
String name = String.format(
Locale.ROOT,
"&3&lPage &a&l%d &7/ &c&l%d",
getCurrentPage(), getPageAmount()
);
String lore = String.format(
Locale.ROOT,
"&7You are on page &a%d &7/ &c%d",
getCurrentPage(), getPageAmount()
);
ItemStack itemStack = getItemStack(Material.BOOK, 0, name, lore);
inventory.setItem(inventory.getSize() - 5, itemStack);
}
}
private void fillRow(int rowIndex, ItemStack itemStack, Inventory inventory) {
int yMod = rowIndex * 9;
for (int i = 0; i < 9; i++) {
int slot = yMod + i;
inventory.setItem(slot, itemStack);
}
}
/**
* @param type The {@link Material} of the {@link ItemStack}
* @param durability The durability
* @param name The name. May be null.
* @param lore The lore. May be null.
*
* @return The item
*/
@SuppressWarnings("WeakerAccess")
protected ItemStack getItemStack(Material type, int durability, String name, String... lore) {
ItemStack itemStack = new ItemStack(type, 1, (short) durability);
ItemMeta itemMeta = itemStack.getItemMeta();
if (name != null) {
itemMeta.setDisplayName(color(name));
}
if (lore != null && lore.length != 0) {
itemMeta.setLore(Arrays.stream(lore).map(this::color).collect(Collectors.toList()));
}
itemStack.setItemMeta(itemMeta);
return itemStack;
}
@SuppressWarnings("WeakerAccess")
protected String color(String input) {
return ChatColor.translateAlternateColorCodes('&', input);
}
/**
* @param player The {@link Player} to open it for
*/
public void open(Player player) {
reRender();
player.openInventory(getInventory());
}
private static class Page {
private List<BadButton> buttons = new ArrayList<>();
private int maxSize;
Page(int maxSize) {
this.maxSize = maxSize;
}
/**
* @param event The click event
*/
void handleClick(InventoryClickEvent event) {
// user clicked in his own inventory. Silently drop it
if (event.getRawSlot() > event.getInventory().getSize()) {
return;
}
if (event.getSlot() >= buttons.size()) {
return;
}
BadButton button = buttons.get(event.getSlot());
button.onClick(event);
}
/**
* @return True if there is space left
*/
boolean hasSpace() {
return buttons.size() < maxSize * 9;
}
/**
* @param button The {@link BadButton} to add
*
* @return True if the button was added, false if there was no space
*/
boolean addButton(BadButton button) {
if (!hasSpace()) {
return false;
}
buttons.add(button);
return true;
}
/**
* @param button The {@link BadButton} to remove
*
* @return True if the button was removed
*/
boolean removeButton(BadButton button) {
return buttons.remove(button);
}
/**
* @param inventory The inventory to render in
*/
void render(Inventory inventory) {
for (int i = 0; i < buttons.size(); i++) {
BadButton button = buttons.get(i);
inventory.setItem(i, button.getItemStack());
}
}
/**
* @return True if this page is empty
*/
boolean isEmpty() {
return buttons.isEmpty();
}
}
}
package me.ialistannen.modularcoretest.doggy;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.InventoryHolder;
/**
* Listens for click events for the {@link BadPagedPane}
*/
public class BadPagedPaneListener implements Listener {
@EventHandler
public void onClick(InventoryClickEvent event) {
InventoryHolder holder = event.getInventory().getHolder();
if (holder instanceof BadPagedPane) {
((BadPagedPane) holder).onClick(event);
}
}
}
package me.ialistannen.modularcoretest.doggy;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import com.perceivedev.perceivecore.utilities.item.ItemFactory;
import me.ialistannen.modularcoretest.ModularCoreTest;
public class Usage {
public static void main(String[] args, Player player) {
// set items to 36 to see how the pane handles dynamically creating and removing pages when opened
// This is because the two Runnables will then add a Button (==> overflow ==> new page) and then remove it again
int rows = 6, items = 100;
BadPagedPane pagedPane = new BadPagedPane(rows - 2, rows, "&3&lTest");
for (int i = 1; i <= items; i++) {
int finalI = i;
pagedPane.addButton(new BadButton(
ItemFactory.builder(Material.STONE).setName("&6Hey: &a" + i).build(),
event -> player.sendMessage("Hey: " + finalI)
));
}
pagedPane.open(player);
String hey = "Cool!";
BadButton button = new BadButton(
ItemFactory.builder(Material.OBSIDIAN).setName("&6Hey: &a" + hey).build(),
event -> player.sendMessage("Hey: " + hey)
);
new BukkitRunnable() {
@Override
public void run() {
pagedPane.addButton(button);
}
}.runTaskLater(ModularCoreTest.getInstance(), 20 * 5);
new BukkitRunnable() {
@Override
public void run() {
pagedPane.removeButton(button);
}
}.runTaskLater(ModularCoreTest.getInstance(), 20 * 10);
// YOU NEED TO REGISTER THE BadPagedPaneListener. You can do so at startup, it will call the right methods
// at the right time, no matter when you register it. It can be registered the whole time without any problems.
}
}
@DrOreo002
Copy link

Omg. I just found this on random spigot forums 0.o, thanks for making it!

@jwpjrdev
Copy link

This is amazing. Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment