From 48dbdbd9d42ed5539ac427c3edc1746ff3a2165d Mon Sep 17 00:00:00 2001 From: Adrien Prokopowicz Date: Mon, 21 Sep 2015 06:37:10 +0200 Subject: [PATCH] Separated the Inventory GUI from the common GUI. * NEW: Added new InventoryGui class. This class only manages inventory-based GUI. * NEW: Gui: Added the registerListener() method, which allows any Gui subclass to lazily register their own GUI Listeners. --- .../imageonmap/gui/list/MapDetailGui.java | 1 - .../imageonmap/guiproko/core/ActionGui.java | 2 +- .../moribus/imageonmap/guiproko/core/Gui.java | 298 +++++------------- .../imageonmap/guiproko/core/GuiUtils.java | 2 +- .../guiproko/core/InventoryGui.java | 239 ++++++++++++++ 5 files changed, 317 insertions(+), 225 deletions(-) create mode 100644 src/main/java/fr/moribus/imageonmap/guiproko/core/InventoryGui.java diff --git a/src/main/java/fr/moribus/imageonmap/gui/list/MapDetailGui.java b/src/main/java/fr/moribus/imageonmap/gui/list/MapDetailGui.java index ed4377c..2c47b57 100644 --- a/src/main/java/fr/moribus/imageonmap/gui/list/MapDetailGui.java +++ b/src/main/java/fr/moribus/imageonmap/gui/list/MapDetailGui.java @@ -20,7 +20,6 @@ package fr.moribus.imageonmap.gui.list; import fr.moribus.imageonmap.gui.core.AbstractGui; import fr.moribus.imageonmap.gui.core.GuiManager; -import fr.moribus.imageonmap.guiproko.core.Gui; import fr.moribus.imageonmap.map.ImageMap; import fr.moribus.imageonmap.map.PosterMap; import fr.moribus.imageonmap.ui.MapItemManager; diff --git a/src/main/java/fr/moribus/imageonmap/guiproko/core/ActionGui.java b/src/main/java/fr/moribus/imageonmap/guiproko/core/ActionGui.java index 362db81..d887126 100644 --- a/src/main/java/fr/moribus/imageonmap/guiproko/core/ActionGui.java +++ b/src/main/java/fr/moribus/imageonmap/guiproko/core/ActionGui.java @@ -39,7 +39,7 @@ import java.util.*; * * @author ProkopyL (main) and Amaury Carrade */ -abstract public class ActionGui extends Gui +abstract public class ActionGui extends InventoryGui { /** * The prefix for action handlers. diff --git a/src/main/java/fr/moribus/imageonmap/guiproko/core/Gui.java b/src/main/java/fr/moribus/imageonmap/guiproko/core/Gui.java index 5183229..75f71cd 100644 --- a/src/main/java/fr/moribus/imageonmap/guiproko/core/Gui.java +++ b/src/main/java/fr/moribus/imageonmap/guiproko/core/Gui.java @@ -18,109 +18,45 @@ package fr.moribus.imageonmap.guiproko.core; -import org.bukkit.*; -import org.bukkit.entity.*; -import org.bukkit.event.*; -import org.bukkit.event.inventory.*; -import org.bukkit.inventory.*; -import org.bukkit.plugin.*; +import fr.moribus.imageonmap.PluginLogger; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.Listener; +import org.bukkit.plugin.Plugin; -import java.util.*; - -/** - * This class provides the basic needs for chest-type GUIs. - * It allows you to create custom GUIs by simply providing an inventory - * to fill, as well as rerouting basic events to it. - */ abstract public class Gui { - static protected final int INVENTORY_ROW_SIZE = 9; - static protected final int MAX_INVENTORY_COLUMN_SIZE = 6; - static protected final int MAX_INVENTORY_SIZE = INVENTORY_ROW_SIZE * MAX_INVENTORY_COLUMN_SIZE; - /** * The player this Gui instance is associated to. */ private Player player; - /** - * The size of the inventory. - */ - private int size = 0; - - /** - * The title of the inventory. - */ - private String title; - - /** - * The current Bukkit inventory. - */ - private Inventory inventory; /** * If the inventory is currently open. */ private boolean open = false; - - - private void open(Player player) - { - this.player = player; - openGuis.put(player, this); - this.update(); - player.openInventory(inventory); - this.open = true; - } - - - /* ===== Public API ===== */ - - /** - * Asks the GUI to update its data, and refresh its view accordingly. - * The inventory may be regenerated when calling this method. - */ - public void update() - { - onUpdate(); - onAfterUpdate(); - - //If inventory does not need to be regenerated - if(inventory != null && inventory.getTitle().equals(title) && inventory.getSize() == size) - { - refresh(); - } - else - { - inventory = Bukkit.createInventory(player, size, title); - populate(inventory); - - if(isOpen()) // Reopening the inventory - { - player.closeInventory(); - player.openInventory(inventory); - } - } - } - - /** - * Asks the GUI to recreate its view. - * The inventory is cleared, but never regenerated when calling this method. - */ - public void refresh() - { - inventory.clear(); - populate(inventory); - } /** * Closes this inventory. */ public void close() { - this.open = false; - player.closeInventory(); - openGuis.remove(player); + setClosed(); + } + + + /* ===== Public API ===== */ + + /** + * Asks the GUI to update its data, and refresh its view accordingly. + */ + public void update() + { + onUpdate(); + onAfterUpdate(); } /* ===== Protected API ===== */ @@ -137,108 +73,30 @@ abstract public class Gui */ protected void onAfterUpdate() {} - /** - * Called when the inventory needs to be (re)populated. - * - * @param inventory The inventory to populate - */ - abstract protected void populate(Inventory inventory); - /** - * Raised when an action is performed on an item in the inventory. - * - * @param event The click event data. - */ - abstract protected void onClick(InventoryClickEvent event); - - - /** - * Raised when an drag is performed on the inventory. - * The default behaviour is to cancel any event that affects the GUI. - * - * @param event The drag event data. - */ - protected void onDrag(InventoryDragEvent event) + protected final void setClosed() { - if(affectsGui(event)) event.setCancelled(true); + if(open == false) return; + open = false; + openGuis.remove(player); } - /** - * Returns if the given event affects the GUI's inventory. - * - * @param event The event to test - * @return {@code true} if the event's slot is in the GUI's inventory, - * {@code false} otherwise. - */ - static protected boolean affectsGui(InventoryClickEvent event) + protected void open(Player player) { - return event.getRawSlot() < event.getInventory().getSize(); - } - - /** - * Returns if the given event affects the GUI's inventory. - * - * @param event The event to test - * @return true if any of the event's slots is in the GUI's inventory, - * false otherwise. - */ - static protected boolean affectsGui(InventoryDragEvent event) - { - for(int slot : event.getRawSlots()) - { - if(slot < event.getInventory().getSize()) - { - return true; - } - } - return false; + this.player = player; + openGuis.put(player, this); + update(); + open = true; } - - /** - * Raised when the GUI is being closed. - * Use this method to cleanup data. - */ - protected void onClose() {} - - /* ===== Getters & Setters ===== */ /** @return If the GUI is currently open or not. */ - public boolean isOpen() { return open; } + public final boolean isOpen() { return open; } /** @return The player this Gui instance is associated to. */ - protected Player getPlayer() { return player; } + protected final Player getPlayer() { return player; } - /** @return The size of the inventory. */ - protected int getSize() { return size; } - - /** - * Sets the new size of the inventory. - * The given value is raised to be a multiple of the size of an inventory's - * row, and is capped to the maximal size of an inventory. - * It will be applied on the next GUI update. - * @param size The new size of the inventory. - */ - protected void setSize(int size) - { - this.size = Math.min(((int)(Math.ceil((double) size / INVENTORY_ROW_SIZE))) * INVENTORY_ROW_SIZE, MAX_INVENTORY_SIZE); - } - - /** @return The title of the inventory. */ - protected String getTitle() { return title; } - - /** - * Sets the new title of the inventory. - * It will be applied on the next GUI update. - * @param title The new title of the inventory - */ - protected void setTitle(String title){this.title = title;} - - /** @return The underlying inventory, or null if the Gui has not been opened yet. */ - public Inventory getInventory() { return inventory; } - - /* ===== Static API ===== */ /** @@ -248,20 +106,25 @@ abstract public class Gui static private HashMap openGuis = null; /** - * The Bukkit listener for all GUI-related events. + * A map of all the currently registered GUIs listeners. */ - static private GuiListener listener = null; + static private HashMap, Listener> guiListeners = null; + + /** + * The plugin that uses the GUI API. + */ + static private Plugin plugin = null; /** * Initializes the GUI listeners. * This method must be called on plugin enabling. * @param plugin The plugin the GUI listeners will be registered on */ - static public void init(Plugin plugin) + static public final void init(Plugin plugin) { openGuis = new HashMap<>(); - listener = new GuiListener(); - plugin.getServer().getPluginManager().registerEvents(listener, plugin); + guiListeners = new HashMap<>(); + Gui.plugin = plugin; GuiUtils.init(); } @@ -269,11 +132,31 @@ abstract public class Gui * Cleans up the GUI states. * This method must be called on plugin disabling. */ - static public void exit() + static public final void exit() { openGuis.clear(); + guiListeners.clear(); openGuis = null; - listener = null; + guiListeners = null; + } + + static protected final void registerListener(Class listenerClass) + { + if(guiListeners == null || guiListeners.containsKey(listenerClass)) + return; + + try + { + Constructor constructor = listenerClass.getDeclaredConstructor(); + constructor.setAccessible(true); + Listener listener = constructor.newInstance(); + guiListeners.put(listenerClass, listener); + plugin.getServer().getPluginManager().registerEvents(listener, plugin); + } + catch(Throwable ex) + { + PluginLogger.error("Could not register listener for GUI", ex); + } } /** @@ -283,7 +166,7 @@ abstract public class Gui * @param gui The GUI. * @return The opened GUI. */ - static public T open(Player owner, T gui) + static public final T open(Player owner, T gui) { close(owner); ((Gui)gui).open(owner);/* JAVA GENERICS Y U NO WORK */ @@ -295,51 +178,22 @@ abstract public class Gui * Closes any open GUI for a given player. * @param owner The player. */ - static public void close(Player owner) + static public final void close(Player owner) { Gui openGui = openGuis.get(owner); if(openGui != null) openGui.close(); } - - - /** - * Implements a Bukkit listener for all GUI-related events. - */ - static private class GuiListener implements Listener + + static public final Gui getOpenGui(HumanEntity entity) { - @EventHandler - public void onInventoryDrag(InventoryDragEvent event) - { - if(!(event.getWhoClicked() instanceof Player)) return; - Player owner = (Player) event.getWhoClicked(); - Gui openGui = openGuis.get(owner); - if(openGui == null) return; - - openGui.onDrag(event); - } - - @EventHandler - public void onInventoryClick(InventoryClickEvent event) - { - if(!(event.getWhoClicked() instanceof Player)) return; - Player owner = (Player) event.getWhoClicked(); - Gui openGui = openGuis.get(owner); - if(openGui == null) return; - - openGui.onClick(event); - } - - @EventHandler - public void onInventoryClose(InventoryCloseEvent event) - { - if(!(event.getPlayer() instanceof Player)) return; - Player owner = (Player) event.getPlayer(); - Gui openGui = openGuis.get(owner); - if(openGui == null) return; - if(!openGui.isOpen()) return; - - openGui.onClose(); - openGuis.remove(owner); - } + if(!(entity instanceof Player)) return null; + return openGuis.get((Player) entity); + } + + static public final T getOpenGui(HumanEntity entity, Class guiClass) + { + Gui openGui = getOpenGui(entity); + if(!guiClass.isAssignableFrom(openGui.getClass())) return null; + return (T) openGui; } } diff --git a/src/main/java/fr/moribus/imageonmap/guiproko/core/GuiUtils.java b/src/main/java/fr/moribus/imageonmap/guiproko/core/GuiUtils.java index 022633e..05f51fa 100644 --- a/src/main/java/fr/moribus/imageonmap/guiproko/core/GuiUtils.java +++ b/src/main/java/fr/moribus/imageonmap/guiproko/core/GuiUtils.java @@ -86,7 +86,7 @@ abstract public class GuiUtils * @param slot The slot where to put the ItemStack * @param item The ItemStack to set */ - static public void setItemLater(Gui gui, int slot, ItemStack item) + static public void setItemLater(InventoryGui gui, int slot, ItemStack item) { Bukkit.getScheduler().scheduleSyncDelayedTask(ImageOnMap.getPlugin(), new CreateDisplayItemTask(gui.getInventory(), item, slot)); diff --git a/src/main/java/fr/moribus/imageonmap/guiproko/core/InventoryGui.java b/src/main/java/fr/moribus/imageonmap/guiproko/core/InventoryGui.java new file mode 100644 index 0000000..f2c88f0 --- /dev/null +++ b/src/main/java/fr/moribus/imageonmap/guiproko/core/InventoryGui.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2013 Moribus + * Copyright (C) 2015 ProkopyL + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package fr.moribus.imageonmap.guiproko.core; + +import static fr.moribus.imageonmap.guiproko.core.Gui.getOpenGui; +import org.bukkit.*; +import org.bukkit.entity.*; +import org.bukkit.event.*; +import org.bukkit.event.inventory.*; +import org.bukkit.inventory.*; + +/** + * This class provides the basic needs for chest-type GUIs. + * It allows you to create custom GUIs by simply providing an inventory + * to fill, as well as rerouting basic events to it. + */ +abstract public class InventoryGui extends Gui +{ + static protected final int INVENTORY_ROW_SIZE = 9; + static protected final int MAX_INVENTORY_COLUMN_SIZE = 6; + static protected final int MAX_INVENTORY_SIZE = INVENTORY_ROW_SIZE * MAX_INVENTORY_COLUMN_SIZE; + + public InventoryGui() + { + registerListener(GuiListener.class); + } + + /** + * The size of the inventory. + */ + private int size = 0; + + /** + * The title of the inventory. + */ + private String title; + + /** + * The current Bukkit inventory. + */ + private Inventory inventory; + + /* ===== Public API ===== */ + + /** + * Asks the GUI to update its data, and refresh its view accordingly. + * The inventory may be regenerated when calling this method. + */ + @Override + public void update() + { + super.update(); + Player player = getPlayer(); + + //If inventory does not need to be regenerated + if(inventory != null && inventory.getTitle().equals(title) && inventory.getSize() == size) + { + refresh(); + } + else + { + inventory = Bukkit.createInventory(player, size, title); + populate(inventory); + + if(isOpen()) // Reopening the inventory + { + player.closeInventory(); + player.openInventory(inventory); + } + } + } + + /** + * Asks the GUI to recreate its view. + * The inventory is cleared, but never regenerated when calling this method. + */ + public void refresh() + { + inventory.clear(); + populate(inventory); + } + + /* ===== Protected API ===== */ + + @Override + protected void open(Player player) + { + super.open(player); + player.openInventory(inventory); + } + + /** + * Closes this inventory. + */ + @Override + public void close() + { + super.close(); + getPlayer().closeInventory(); + } + + /** + * Called when the inventory needs to be (re)populated. + * + * @param inventory The inventory to populate + */ + abstract protected void populate(Inventory inventory); + + /** + * Raised when an action is performed on an item in the inventory. + * + * @param event The click event data. + */ + abstract protected void onClick(InventoryClickEvent event); + + + /** + * Raised when an drag is performed on the inventory. + * The default behaviour is to cancel any event that affects the GUI. + * + * @param event The drag event data. + */ + protected void onDrag(InventoryDragEvent event) + { + if(affectsGui(event)) event.setCancelled(true); + } + + /** + * Returns if the given event affects the GUI's inventory. + * + * @param event The event to test + * @return {@code true} if the event's slot is in the GUI's inventory, + * {@code false} otherwise. + */ + static protected boolean affectsGui(InventoryClickEvent event) + { + return event.getRawSlot() < event.getInventory().getSize(); + } + + /** + * Returns if the given event affects the GUI's inventory. + * + * @param event The event to test + * @return true if any of the event's slots is in the GUI's inventory, + * false otherwise. + */ + static protected boolean affectsGui(InventoryDragEvent event) + { + for(int slot : event.getRawSlots()) + { + if(slot < event.getInventory().getSize()) + { + return true; + } + } + return false; + } + + /* ===== Getters & Setters ===== */ + + /** @return The size of the inventory. */ + protected int getSize() { return size; } + + /** + * Sets the new size of the inventory. + * The given value is raised to be a multiple of the size of an inventory's + * row, and is capped to the maximal size of an inventory. + * It will be applied on the next GUI update. + * @param size The new size of the inventory. + */ + protected void setSize(int size) + { + this.size = Math.min(((int)(Math.ceil((double) size / INVENTORY_ROW_SIZE))) * INVENTORY_ROW_SIZE, MAX_INVENTORY_SIZE); + } + + /** @return The title of the inventory. */ + protected String getTitle() { return title; } + + /** + * Sets the new title of the inventory. + * It will be applied on the next GUI update. + * @param title The new title of the inventory + */ + protected void setTitle(String title){this.title = title;} + + /** @return The underlying inventory, or null if the Gui has not been opened yet. */ + public Inventory getInventory() { return inventory; } + + /** + * Implements a Bukkit listener for all GUI-related events. + */ + static private class GuiListener implements Listener + { + @EventHandler + public void onInventoryDrag(InventoryDragEvent event) + { + InventoryGui openGui = getOpenGui(event.getWhoClicked(), InventoryGui.class); + if(openGui == null) return; + + openGui.onDrag(event); + } + + @EventHandler + public void onInventoryClick(InventoryClickEvent event) + { + InventoryGui openGui = getOpenGui(event.getWhoClicked(), InventoryGui.class); + if(openGui == null) return; + + openGui.onClick(event); + } + + @EventHandler + public void onInventoryClose(InventoryCloseEvent event) + { + Gui openGui = getOpenGui(event.getPlayer()); + if(openGui == null) return; + + if(!openGui.isOpen()); + openGui.setClosed(); + } + } + +}