Improved the GUI manager.

* NEW: reformatted the code a little bit to make it more readable.
* NEW: added Javadoc when it was missing.
* NEW: changed the name of the fallback action method in `ActionGui`, from `onAction_unknown` to `unknown_action` (coherence of the methods names).
* NEW: the action methods (both declared and unknown) can now take an extra argument: the triggered InventoryClickEvent. This argument is completely optional, so this will not break any code, and if the event is not needed, it can just be skipped.
* NEW: renamed the setData(int, int) method to setDataShape(int, int), because this method does not actually set any data.
This commit is contained in:
Amaury Carrade 2015-09-10 00:55:10 +02:00
parent efd62283ea
commit 80089c7bf5
5 changed files with 481 additions and 232 deletions

View File

@ -18,18 +18,14 @@
package fr.moribus.imageonmap.guiproko.core;
import fr.moribus.imageonmap.PluginLogger;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import org.bukkit.Material;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import fr.moribus.imageonmap.*;
import org.bukkit.*;
import org.bukkit.event.inventory.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import java.lang.reflect.*;
import java.util.*;
/**
* This class implements an action-based GUI.
@ -38,7 +34,10 @@ import org.bukkit.inventory.meta.ItemMeta;
*
* Events handlers are (usually private) methods implemented in the derived
* class(es). They are named using the pattern 'action_[action name]', and
* are called when the associated action is triggered.
* are called when the associated action is triggered. They take an optional
* argument (add it if you need it): the {@link InventoryClickEvent} triggered.
*
* @author ProkopyL (main) and Amaury Carrade
*/
abstract public class ActionGui extends Gui
{
@ -49,7 +48,7 @@ abstract public class ActionGui extends Gui
/**
* The class of this GUI.
* Useful to retreive methods from the derived classes.
* Useful to retrieve methods from the derived classes.
*/
private final Class<? extends ActionGui> guiClass = this.getClass();
@ -58,12 +57,14 @@ abstract public class ActionGui extends Gui
* their position in the inventory.
*/
private final HashMap<Integer, Action> actions = new HashMap<>();
/* ===== Protected API ===== */
/**
* Creates a new action, represented by the given item.
* The item's metadata is changed to use the given title and lore.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param material The material used to represent the action.
@ -78,6 +79,7 @@ abstract public class ActionGui extends Gui
/**
* Creates a new action, represented by the given item.
* The item's metadata is changed to use the given title and lore.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param item The item used to represent the action.
@ -92,6 +94,7 @@ abstract public class ActionGui extends Gui
/**
* Creates a new action, represented by the given item.
* The item's metadata is changed to use the given title and lore.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param item The item used to represent the action.
@ -105,6 +108,7 @@ abstract public class ActionGui extends Gui
/**
* Creates a new action, represented by the given material.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param material The material used to represent the action.
@ -116,18 +120,21 @@ abstract public class ActionGui extends Gui
/**
* Creates a new action, represented by no item.
* This action will not be rendered to the user until {@link #updateAction(java.lang.String, org.bukkit.inventory.ItemStack, java.lang.String)
* } is called.
* This action will not be rendered to the user until
* {@link #updateAction(java.lang.String, org.bukkit.inventory.ItemStack, java.lang.String)}
* is called.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
*/
protected void action(String name, int slot)
{
action(name, slot, (ItemStack)null);
action(name, slot, (ItemStack) null);
}
/**
* Creates a new action, and adds it to the GUI.
*
* @param name The identifier of the action.
* @param slot The slot the action will be placed on.
* @param item The item used to represent the action.
@ -142,7 +149,8 @@ abstract public class ActionGui extends Gui
/**
* Adds an action to the GUI.
* @param action
*
* @param action The {@link fr.moribus.imageonmap.guiproko.core.ActionGui.Action} to register.
*/
private void action(Action action)
{
@ -151,6 +159,7 @@ abstract public class ActionGui extends Gui
/**
* Updates the action represented by the given name.
*
* @param name The name of the action to update.
* @param item The new material to affect to the action.
* @param title The new title to affect to the action.
@ -163,6 +172,7 @@ abstract public class ActionGui extends Gui
/**
* Updates the action represented by the given name.
*
* @param name The name of the action to update.
* @param item The new item to affect to the action.
* @param title The new title to affect to the action.
@ -178,7 +188,8 @@ abstract public class ActionGui extends Gui
}
/**
* Retreives the action represented by the given name.
* Retrieves the action represented by the given name.
*
* @param name The name of the action to retreive.
* @return The action represented by the given name.
* @throws IllegalArgumentException If no action has the given name.
@ -200,13 +211,28 @@ abstract public class ActionGui extends Gui
protected abstract void onUpdate();
/**
* Raised when an action whithout any event handler has been triggered.
* Raised when an action without any event handler has been triggered.
*
* @param name The name of the triggered action.
* @param slot The slot of the action.
* @param item The item of the action.
* @param event The {@link InventoryClickEvent} raised when this action was triggered.
*/
protected void unknown_action(String name, int slot, ItemStack item, InventoryClickEvent event)
{
unknown_action(name, slot, item);
}
/**
* Raised when an action without any event handler has been triggered.
*
* @param name The name of the triggered action.
* @param slot The slot of the action.
* @param item The item of the action.
*/
protected void onAction_unknown(String name, int slot, ItemStack item){}
protected void unknown_action(String name, int slot, ItemStack item) {}
@Override
public void update()
{
@ -226,11 +252,12 @@ abstract public class ActionGui extends Gui
@Override
protected void onClick(InventoryClickEvent event)
{
if(event.getRawSlot() >= event.getInventory().getSize())//The user clicked in its own inventory
if(event.getRawSlot() >= event.getInventory().getSize()) //The user clicked in its own inventory
{
if(!event.getAction().equals(InventoryAction.MOVE_TO_OTHER_INVENTORY))
return;
}
event.setCancelled(true);
callAction(actions.get(event.getRawSlot()));
@ -243,9 +270,10 @@ abstract public class ActionGui extends Gui
private void callAction(Action action)
{
if(action == null) return;
if(action.callback == null)
{
onAction_unknown(action.name, action.slot, action.item);
unknown_action(action.name, action.slot, action.item);
return;
}
@ -265,19 +293,29 @@ abstract public class ActionGui extends Gui
}
/**
* Retreives the event handler matching the given name from a class (or any of its parents).
* @param klass The class to retreive the event handler from.
* Retrieves the event handler matching the given name from a class (or any of its parents).
*
* @param klass The class to retrieve the event handler from.
* @param name The name of the action.
* @return The event handler matching the action name, or null if none was found.
*/
private Method getActionHandler(Class klass, String name)
private Method getActionHandler(Class<?> klass, String name)
{
Method callback = null;
Method callback;
do
{
try
{
callback = klass.getDeclaredMethod(ACTION_HANDLER_NAME + name);
try
{
callback = klass.getDeclaredMethod(ACTION_HANDLER_NAME + name, InventoryClickEvent.class);
}
catch(NoSuchMethodException e)
{
callback = klass.getDeclaredMethod(ACTION_HANDLER_NAME + name);
}
callback.setAccessible(true);
break;
}
@ -286,8 +324,7 @@ abstract public class ActionGui extends Gui
callback = null;
klass = klass.getSuperclass();
}
}while(klass != null);
} while (klass != null);
return callback;
}

View File

@ -27,9 +27,33 @@ import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
/**
* This class implements an exploration GUI, allowing users to see a set of data
* in a paginated view, and to manipulate it or get it (if the
* {@link fr.moribus.imageonmap.guiproko.core.ExplorerGui.Mode#CREATIVE Creative} mode
* is enabled for this GUI enabled by default).
*
* This GUI supports both one- and two-dimensional contents; two-dimensional content is
* represented by a one-dimension list and a width, or by {@link #getViewItem(int, int)}
* if you override it (in this case you need to call {@link #setDataShape(int, int)} in the
* {@link #onUpdate()} method).
*
* @param <T> The type of data this GUI will display.
*
* @author ProkopyL (main) and Amaury Carrade
*/
abstract public class ExplorerGui<T> extends ActionGui
{
static protected enum Mode {READONLY, CREATIVE};
/**
* The explorer GUI's reading mode.
*
* In creative mode (the default mode), the players are able to manipulate the content, get it
* inside their own inventory (without consumption, just like the creative mode), and whatever
* you want if you override some methods.
*
* In read-only mode, they are only able to browse the content.
*/
protected enum Mode {READONLY, CREATIVE}
private T[] data;
private boolean isData2D = false;
@ -48,21 +72,42 @@ abstract public class ExplorerGui<T> extends ActionGui
private int pageCountY;
private Mode mode = Mode.CREATIVE;
/**
* Sets the displayed data.
*
* @param data The data.
* @param dataWidth The data's width, if this data is in two dimensions.
* In this case the data array will be read like a matrix, with
* lines stored in a consecutive way; the height is automatically
* calculated.
*/
protected void setData(T[] data, int dataWidth)
{
this.data = data;
if(dataWidth > 0)
setData(dataWidth, (int) Math.ceil((double)data.length / (double)dataWidth));
setDataShape(dataWidth, (int) Math.ceil((double) data.length / (double) dataWidth));
}
protected void setData(int dataWidth, int dataHeight)
/**
* Sets the data's shape. Use this if you're providing data through
* {@link #getViewItem(int, int)}, as example.
*
* @param dataWidth The data's width.
* @param dataHeight The data's height.
*/
protected void setDataShape(int dataWidth, int dataHeight)
{
this.dataWidth = dataWidth;
this.dataHeight = dataHeight;
this.isData2D = dataWidth > 0;
}
/**
* Sets the displayed data, assuming this data is in one dimension.
*
* @param data The data.
*/
protected void setData(T[] data)
{
setData(data, 0);
@ -101,6 +146,7 @@ abstract public class ExplorerGui<T> extends ActionGui
{
int start = currentPageX * viewSize;
int max = Math.min(viewSize, data.length - start);
for(int i = 0; i < max; i++)
{
inventory.setItem(i, getViewItem(i + start));
@ -118,10 +164,11 @@ abstract public class ExplorerGui<T> extends ActionGui
{
for(int j = maxX; j --> 0;)
{
inventory.setItem(i*INVENTORY_ROW_SIZE + j, getViewItem(j + startX, i + startY));
inventory.setItem(i * INVENTORY_ROW_SIZE + j, getViewItem(j + startX, i + startY));
}
}
}
if(hasActions()) super.populate(inventory);
}
@ -130,7 +177,7 @@ abstract public class ExplorerGui<T> extends ActionGui
{
int slot = event.getRawSlot();
//Clicked in the action bar
// Clicked in the action bar
if(hasActions() &&
slot >= MAX_INVENTORY_SIZE - INVENTORY_ROW_SIZE
&& slot < MAX_INVENTORY_SIZE)
@ -146,7 +193,7 @@ abstract public class ExplorerGui<T> extends ActionGui
return;
}
if(affectsGui(event))//The user clicked in its own inventory
if(affectsGui(event)) // The user clicked in its own inventory
{
switch(event.getAction())
{
@ -154,11 +201,14 @@ abstract public class ExplorerGui<T> extends ActionGui
case HOTBAR_MOVE_AND_READD: case HOTBAR_SWAP:
case MOVE_TO_OTHER_INVENTORY:
onActionPickup(event); break;
case PLACE_ALL: case PLACE_ONE: case PLACE_SOME:
case SWAP_WITH_CURSOR:
onActionPut(event); break;
case DROP_ALL_CURSOR: case DROP_ONE_CURSOR:
break;
default:
event.setCancelled(true);
}
@ -195,11 +245,18 @@ abstract public class ExplorerGui<T> extends ActionGui
}
event.setCancelled(true);
if(mode.equals(Mode.READONLY)) return;
if(!onPutItem(event.getOldCursor())) return;
event.setCursor(new ItemStack(Material.AIR));
}
/**
* Triggered when a player clicks on the GUI to get an item.
*
* @param event The triggered event.
*/
private void onActionPickup(InventoryClickEvent event)
{
int dataIndex = getDataIndex(event.getSlot());
@ -209,16 +266,23 @@ abstract public class ExplorerGui<T> extends ActionGui
event.setCancelled(true);
return;
}
ItemStack pickedUpItem = getPickedUpItem(dataIndex);
if(pickedUpItem == null || mode.equals(Mode.READONLY))
{
event.setCancelled(true);
return;
}
event.setCurrentItem(pickedUpItem);
GuiUtils.setItemLater(this, event.getSlot(), getViewItem(dataIndex));
}
/**
* Triggered when a player clicks on the GUI to place an item.
*
* @param event The triggered event.
*/
private void onActionPut(InventoryClickEvent event)
{
event.setCancelled(true);
@ -226,7 +290,12 @@ abstract public class ExplorerGui<T> extends ActionGui
if(!onPutItem(event.getCursor())) return;
event.setCursor(new ItemStack(Material.AIR));
}
/**
* Triggered when a player moves an item on the GUI.
*
* @param event The triggered event.
*/
private void onActionMove(InventoryClickEvent event)
{
event.setCancelled(true);
@ -238,7 +307,6 @@ abstract public class ExplorerGui<T> extends ActionGui
@Override
protected void onAfterUpdate()
{
//Calculating page count
if(data != null && data.length <= 0)
{
@ -251,15 +319,17 @@ abstract public class ExplorerGui<T> extends ActionGui
}
else if(!isData2D)
{
int dataLength = (data == null) ? 0 : data.length;
viewWidth = INVENTORY_ROW_SIZE;
viewHeight = Math.min((int)Math.ceil((double)data.length / (double)viewWidth),
viewHeight = Math.min((int)Math.ceil((double)dataLength / (double)viewWidth),
MAX_INVENTORY_COLUMN_SIZE);
if(hasActions() || data.length > MAX_INVENTORY_SIZE)
if(hasActions() || dataLength > MAX_INVENTORY_SIZE)
viewHeight--;
viewSize = viewWidth * viewHeight;
pageCountX = (int)Math.ceil((double)data.length / (double)viewSize);
pageCountX = (int)Math.ceil((double)dataLength / (double)viewSize);
pageCountY = 1;
}
else
@ -329,53 +399,111 @@ abstract public class ExplorerGui<T> extends ActionGui
{
if(i < 0 || i >= data.length)
return null;
return data[i];
}
/**
* Returns the stack to display at the given index.
*
* @param i The index.
* @return The stack.
*/
protected ItemStack getViewItem(int i)
{
return getViewItem(getData(i));
}
/**
* Returns the stack to display at the given coordinates.
*
* @param x The x-coordinate (left to right).
* @param y The y-coordinate (top to bottom).
* @return The stack.
*/
protected ItemStack getViewItem(int x, int y)
{
return getViewItem(y * dataWidth + x);
}
protected ItemStack getViewItem(T data){return null;};
/**
* Returns the ItemStack representation of the given piece of data.
*
* @param data The piece of data.
* @return The piece's representation.
*/
protected ItemStack getViewItem(T data) { return null; }
private ItemStack getPickedUpItem(int dataIndex)
{
if(dataIndex < 0 || dataIndex >= data.length)
return null;
return getPickedUpItem(getData(dataIndex));
}
protected ItemStack getPickedUpItem(T data){return getViewItem(data);}
protected void onRightClick(T data){}
protected boolean onPutItem(ItemStack item){return true;}
/**
* Returns the stack the players will get when they try to take an item from
* the GUI, in {@link fr.moribus.imageonmap.guiproko.core.ExplorerGui.Mode#CREATIVE}
* mode.
*
* @param data The picked-up piece of data.
* @return The stack to pick-up ({@code null} to cancel the pick-up).
*/
protected ItemStack getPickedUpItem(T data) { return getViewItem(data); }
/**
* Triggered when the player right-clicks an item on the GUI.
*
* @param data The right-clicked piece of data.
*/
protected void onRightClick(T data) {}
/**
* Triggered when the player try to place an item inside the GUI.
*
* This will not place the item in the GUI, it's up to you to update the data and refresh
* the GUI if you need so.
*
* @param item The {@link ItemStack} the player is trying to put.
* @return {@code false} to cancel the placement; {@code true} to accept it.
*/
protected boolean onPutItem(ItemStack item) { return true; }
/**
* Displays the next horizontal page, if possible.
*/
public void next()
{
if(!canGoNext()) return;
currentPageX++;
refresh();
}
/**
* Displays the previous horizontal page, if possible.
*/
public void previous()
{
if(!canGoPrevious()) return;
currentPageX--;
refresh();
}
/**
* Displays the previous vertical page, if possible.
*/
public void up()
{
if(!canGoUp()) return;
currentPageY--;
refresh();
}
/**
* Displays the next vertical page, if possible.
*/
public void down()
{
if(!canGoDown()) return;
@ -402,17 +530,40 @@ abstract public class ExplorerGui<T> extends ActionGui
{
return currentPageY < pageCountY - 1;
}
/**
* Returns the amount of horizontal pages.
*
* @return The pages' amount.
*/
public int getPageCount()
{
return pageCountX;
}
/**
* Returns the amount of vertical pages.
* This will always be 1 if the GUI is representing a one-dimensional data set.
*
* @return The pages' amount.
*/
public int getVerticalPageCount()
{
return pageCountY;
}
protected Mode getMode() {return mode;}
protected void setMode(Mode mode) {this.mode = mode;}
/** @return The GUI's manipulation mode. */
protected Mode getMode()
{
return mode;
}
/**
* Sets the GUI's manipulation mode.
* @param mode The mode.
*/
protected void setMode(Mode mode)
{
this.mode = mode;
}
}

View File

@ -18,16 +18,14 @@
package fr.moribus.imageonmap.guiproko.core;
import java.util.HashMap;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.inventory.InventoryDragEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.plugin.Plugin;
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 java.util.*;
/**
* This class provides the basic needs for chest-type GUIs.
@ -64,7 +62,8 @@ abstract public class Gui
* If the inventory is currently open.
*/
private boolean open = false;
private void open(Player player)
{
this.player = player;
@ -73,7 +72,8 @@ abstract public class Gui
player.openInventory(inventory);
this.open = true;
}
/* ===== Public API ===== */
/**
@ -94,7 +94,8 @@ abstract public class Gui
{
inventory = Bukkit.createInventory(player, size, title);
populate(inventory);
if(isOpen())//Reopening the inventory
if(isOpen()) // Reopening the inventory
{
player.closeInventory();
player.openInventory(inventory);
@ -125,32 +126,36 @@ abstract public class Gui
/* ===== Protected API ===== */
/**
* Raised when the {@link Gui#update() } method is called.
* Raised when the {@link Gui#update()} method is called.
* Use this method to update your internal data.
*/
protected void onUpdate(){}
protected void onUpdate() {}
/**
* Raised when the {@link Gui#update() } method is called, but before the inventory is populated.
* Raised when the {@link Gui#update()} method is called, but before the inventory is populated.
* Use this method in a Gui subclass to analyze given data and set other parameters accordingly.
*/
protected void onAfterUpdate(){}
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)
@ -159,18 +164,20 @@ abstract public class Gui
}
/**
* Returns if the given event affects the GUI's inventory
* Returns if the given event affects the GUI's inventory.
*
* @param event The event to test
* @return true if the event's slot is in the GUI's inventory,
* false otherwise.
* @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
* 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.
@ -192,18 +199,19 @@ abstract public class Gui
* Raised when the GUI is being closed.
* Use this method to cleanup data.
*/
protected void onClose(){};
protected void onClose() {}
/* ===== Getters & Setters ===== */
/** @return If the GUI is currently open or not.*/
public boolean isOpen(){return open;}
/** @return If the GUI is currently open or not. */
public boolean isOpen() { return open; }
/** @return The player this Gui instance is associated to.*/
protected Player getPlayer(){return player;}
/** @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;}
/** @return The size of the inventory. */
protected int getSize() { return size; }
/**
* Sets the new size of the inventory.
@ -217,8 +225,9 @@ abstract public class Gui
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;}
/** @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.
@ -226,9 +235,10 @@ abstract public class Gui
*/
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;}
/** @return The underlying inventory, or null if the Gui has not been opened yet. */
public Inventory getInventory() { return inventory; }
/* ===== Static API ===== */
/**
@ -277,6 +287,7 @@ abstract public class Gui
{
close(owner);
((Gui)gui).open(owner);/* JAVA GENERICS Y U NO WORK */
return gui;
}
@ -287,11 +298,10 @@ abstract public class Gui
static public void close(Player owner)
{
Gui openGui = openGuis.get(owner);
if(openGui == null) return;
openGui.close();
if(openGui != null) openGui.close();
}
/**
* Implements a Bukkit listener for all GUI-related events.
*/
@ -307,9 +317,9 @@ abstract public class Gui
openGui.onDrag(event);
}
@EventHandler
public void onInventoryClick(InventoryClickEvent event)
public void onInventoryClick(InventoryClickEvent event)
{
if(!(event.getWhoClicked() instanceof Player)) return;
Player owner = (Player) event.getWhoClicked();
@ -318,9 +328,9 @@ abstract public class Gui
openGui.onClick(event);
}
@EventHandler
public void onInventoryClose(InventoryCloseEvent event)
public void onInventoryClose(InventoryCloseEvent event)
{
if(!(event.getPlayer() instanceof Player)) return;
Player owner = (Player) event.getPlayer();

View File

@ -18,131 +18,182 @@
package fr.moribus.imageonmap.guiproko.core;
import fr.moribus.imageonmap.ImageOnMap;
import fr.moribus.imageonmap.PluginLogger;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import fr.moribus.imageonmap.*;
import org.bukkit.*;
import org.bukkit.entity.*;
import org.bukkit.inventory.*;
import org.bukkit.inventory.meta.*;
import java.lang.reflect.*;
import java.util.*;
/**
* Various utility methods for GUIs.
*/
abstract public class GuiUtils
abstract public class GuiUtils
{
static private Method addItemFlagsMethod = null;
static private Object[] itemFlagValues = null;
/**
* Initializes the GUI utilities.
* This method must be called on plugin enabling.
*/
static public void init()
{
try
{
Class<?> itemFlagClass = Class.forName("org.bukkit.inventory.ItemFlag");
Method valuesMethod = itemFlagClass.getDeclaredMethod("values");
itemFlagValues = (Object[]) valuesMethod.invoke(null);
addItemFlagsMethod = ItemMeta.class.getMethod("addItemFlags", itemFlagClass);
addItemFlagsMethod.setAccessible(true);
static private Method addItemFlagsMethod = null;
static private Object[] itemFlagValues = null;
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
// Not supported :c
} catch (InvocationTargetException e) {
PluginLogger.error("Exception occurred while looking for the ItemFlag API.", e);
}
}
static public void hideItemAttributes(ItemMeta meta)
{
if(addItemFlagsMethod == null) return;
try
{
addItemFlagsMethod.invoke(meta, itemFlagValues);
}
catch (IllegalAccessException | InvocationTargetException ex)
{
PluginLogger.error("Exception occurred while invoking the ItemMeta.addItemFlags method.", ex);
}
}
/**
* Stores the ItemStack at the given index of a GUI's inventory.
* The inventory is only updated the next time the Bukkit Scheduler runs (i.e. next server tick).
*
* @param gui The GUI to update
* @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)
{
Bukkit.getScheduler().scheduleSyncDelayedTask(ImageOnMap.getPlugin(),
new CreateDisplayItemTask(gui.getInventory(), item, slot));
}
static public ItemStack makeItem(Material material)
{
return makeItem(material, null, (List<String>)null);
}
static public ItemStack makeItem(Material material, String title)
{
return makeItem(material, title, (List<String>)null);
}
static public ItemStack makeItem(Material material, String title, String... loreLines)
{
return makeItem(material, title, Arrays.asList(loreLines));
}
static public ItemStack makeItem(Material material, String title, List<String> loreLines)
{
return makeItem(new ItemStack(material), title, loreLines);
}
static public ItemStack makeItem(ItemStack itemStack, String title, List<String> loreLines)
{
ItemMeta meta = itemStack.getItemMeta();
meta.setDisplayName(title);
meta.setLore(loreLines);
if(itemStack.getType().equals(Material.MAP))
hideItemAttributes(meta);
itemStack.setItemMeta(meta);
return itemStack;
}
/**
* Implements a bukkit runnable that updates an inventory slot later.
*/
static private class CreateDisplayItemTask implements Runnable
{
private final Inventory inventory;
private final ItemStack item;
private final int slot;
public CreateDisplayItemTask(Inventory inventory, ItemStack item, int slot)
{
this.inventory = inventory;
this.item = item;
this.slot = slot;
}
@Override
public void run()
{
inventory.setItem(slot, item);
for(HumanEntity player : inventory.getViewers())
{
((Player)player).updateInventory();
}
}
}
/**
* Initializes the GUI utilities. This method must be called on plugin enabling.
*/
static public void init()
{
try
{
Class<?> itemFlagClass = Class.forName("org.bukkit.inventory.ItemFlag");
Method valuesMethod = itemFlagClass.getDeclaredMethod("values");
itemFlagValues = (Object[]) valuesMethod.invoke(null);
addItemFlagsMethod = ItemMeta.class.getMethod("addItemFlags", itemFlagClass);
addItemFlagsMethod.setAccessible(true);
}
catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e)
{
// Not supported :c
}
catch (InvocationTargetException e)
{
PluginLogger.error("Exception occurred while looking for the ItemFlag API.", e);
}
}
/**
* Hides all the item attributes of the given {@link ItemMeta}.
*
* @param meta The {@link ItemMeta} to hide attributes from.
*/
static public void hideItemAttributes(ItemMeta meta)
{
if (addItemFlagsMethod == null) return;
try
{
addItemFlagsMethod.invoke(meta, itemFlagValues);
}
catch (IllegalAccessException | InvocationTargetException ex)
{
PluginLogger.error("Exception occurred while invoking the ItemMeta.addItemFlags method.", ex);
}
}
/**
* Stores the ItemStack at the given index of a GUI's inventory. The inventory is only updated
* the next time the Bukkit Scheduler runs (i.e. next server tick).
*
* @param gui The GUI to update
* @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)
{
Bukkit.getScheduler().scheduleSyncDelayedTask(ImageOnMap.getPlugin(),
new CreateDisplayItemTask(gui.getInventory(), item, slot));
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material)
{
return makeItem(material, null, (List<String>) null);
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
* @param title The stack's title.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material, String title)
{
return makeItem(material, title, (List<String>) null);
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
* @param title The stack's title.
* @param loreLines The stack's lore lines.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material, String title, String... loreLines)
{
return makeItem(material, title, Arrays.asList(loreLines));
}
/**
* One-liner to construct an {@link ItemStack}.
*
* @param material The stack's material.
* @param title The stack's title.
* @param loreLines The stack's lore lines.
*
* @return The constructed {@link ItemStack}.
*/
static public ItemStack makeItem(Material material, String title, List<String> loreLines)
{
return makeItem(new ItemStack(material), title, loreLines);
}
/**
* One-liner to update an {@link ItemStack}'s {@link ItemMeta}.
*
* If the stack is a map, it's attributes will be hidden.
*
* @param itemStack The original {@link ItemStack}. This stack will be directly modified.
* @param title The stack's title.
* @param loreLines A list containing the stack's lines.
*
* @return The same {@link ItemStack}, but with an updated {@link ItemMeta}.
*/
static public ItemStack makeItem(ItemStack itemStack, String title, List<String> loreLines)
{
ItemMeta meta = itemStack.getItemMeta();
meta.setDisplayName(title);
meta.setLore(loreLines);
if (itemStack.getType().equals(Material.MAP))
hideItemAttributes(meta);
itemStack.setItemMeta(meta);
return itemStack;
}
/**
* Implements a bukkit runnable that updates an inventory slot later.
*/
static private class CreateDisplayItemTask implements Runnable
{
private final Inventory inventory;
private final ItemStack item;
private final int slot;
public CreateDisplayItemTask(Inventory inventory, ItemStack item, int slot)
{
this.inventory = inventory;
this.item = item;
this.slot = slot;
}
@Override
public void run()
{
inventory.setItem(slot, item);
for (HumanEntity player : inventory.getViewers())
{
((Player) player).updateInventory();
}
}
}
}

View File

@ -41,7 +41,7 @@ public class MapDetailGui extends ExplorerGui<Void>
protected void onUpdate()
{
setTitle("Details for map " + map.getName());
setData(map.getColumnCount(), map.getRowCount());
setDataShape(map.getColumnCount(), map.getRowCount());
}
}