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.
This commit is contained in:
Adrien Prokopowicz 2015-09-21 06:37:10 +02:00
parent f29e972be6
commit 48dbdbd9d4
5 changed files with 317 additions and 225 deletions

View File

@ -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;

View File

@ -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.

View File

@ -18,59 +18,33 @@
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)
/**
* Closes this inventory.
*/
public void close()
{
this.player = player;
openGuis.put(player, this);
this.update();
player.openInventory(inventory);
this.open = true;
setClosed();
}
@ -78,49 +52,11 @@ abstract public class Gui
/**
* 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);
}
/* ===== Protected API ===== */
@ -137,107 +73,29 @@ 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();
this.player = player;
openGuis.put(player, this);
update();
open = true;
}
/**
* 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;
}
/**
* 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; }
/** @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; }
protected final Player getPlayer() { return player; }
/* ===== Static API ===== */
@ -248,20 +106,25 @@ abstract public class Gui
static private HashMap<Player, Gui> 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<Class<? extends Listener>, 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<? extends Listener> listenerClass)
{
if(guiListeners == null || guiListeners.containsKey(listenerClass))
return;
try
{
Constructor<? extends Listener> 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 extends Gui> T open(Player owner, T gui)
static public final <T extends Gui> 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(!(entity instanceof Player)) return null;
return openGuis.get((Player) entity);
}
static public final <T extends Gui> T getOpenGui(HumanEntity entity, Class<T> guiClass)
{
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);
}
Gui openGui = getOpenGui(entity);
if(!guiClass.isAssignableFrom(openGui.getClass())) return null;
return (T) openGui;
}
}

View File

@ -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));

View File

@ -0,0 +1,239 @@
/*
* Copyright (C) 2013 Moribus
* Copyright (C) 2015 ProkopyL <prokopylmc@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
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();
}
}
}