Icon Menu

Discussion in 'Resources' started by nisovin, Oct 30, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    nisovin

    With the new ability to rename items, it is possible to create a user-friendly GUI menu, using an inventory. I've created a nifty class to handle this:
    Code:
    import java.util.Arrays;
    
    import org.bukkit.Bukkit;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.EventPriority;
    import org.bukkit.event.HandlerList;
    import org.bukkit.event.Listener;
    import org.bukkit.event.inventory.InventoryClickEvent;
    import org.bukkit.inventory.Inventory;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.inventory.meta.ItemMeta;
    import org.bukkit.plugin.Plugin;
     
    public class IconMenu implements Listener {
     
        private String name;
        private int size;
        private OptionClickEventHandler handler;
        private Plugin plugin;
       
        private String[] optionNames;
        private ItemStack[] optionIcons;
       
        public IconMenu(String name, int size, OptionClickEventHandler handler, Plugin plugin) {
            this.name = name;
            this.size = size;
            this.handler = handler;
            this.plugin = plugin;
            this.optionNames = new String[size];
            this.optionIcons = new ItemStack[size];
            plugin.getServer().getPluginManager().registerEvents(this, plugin);
        }
       
        public IconMenu setOption(int position, ItemStack icon, String name, String... info) {
            optionNames[position] = name;
            optionIcons[position] = setItemNameAndLore(icon, name, info);
            return this;
        }
       
        public void open(Player player) {
            Inventory inventory = Bukkit.createInventory(player, size, name);
            for (int i = 0; i < optionIcons.length; i++) {
                if (optionIcons[i] != null) {
                    inventory.setItem(i, optionIcons[i]);
                }
            }
            player.openInventory(inventory);
        }
       
        public void destroy() {
            HandlerList.unregisterAll(this);
            handler = null;
            plugin = null;
            optionNames = null;
            optionIcons = null;
        }
       
        @EventHandler(priority=EventPriority.MONITOR)
        void onInventoryClick(InventoryClickEvent event) {
            if (event.getInventory().getTitle().equals(name)) {
                event.setCancelled(true);
                int slot = event.getRawSlot();
                if (slot >= 0 && slot < size && optionNames[slot] != null) {
                    Plugin plugin = this.plugin;
                    OptionClickEvent e = new OptionClickEvent((Player)event.getWhoClicked(), slot, optionNames[slot]);
                    handler.onOptionClick(e);
                    if (e.willClose()) {
                        final Player p = (Player)event.getWhoClicked();
                        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
                            public void run() {
                                p.closeInventory();
                            }
                        }, 1);
                    }
                    if (e.willDestroy()) {
                        destroy();
                    }
                }
            }
        }
        
        public interface OptionClickEventHandler {
            public void onOptionClick(OptionClickEvent event);       
        }
        
        public class OptionClickEvent {
            private Player player;
            private int position;
            private String name;
            private boolean close;
            private boolean destroy;
           
            public OptionClickEvent(Player player, int position, String name) {
                this.player = player;
                this.position = position;
                this.name = name;
                this.close = true;
                this.destroy = false;
            }
           
            public Player getPlayer() {
                return player;
            }
           
            public int getPosition() {
                return position;
            }
           
            public String getName() {
                return name;
            }
           
            public boolean willClose() {
                return close;
            }
           
            public boolean willDestroy() {
                return destroy;
            }
           
            public void setWillClose(boolean close) {
                this.close = close;
            }
           
            public void setWillDestroy(boolean destroy) {
                this.destroy = destroy;
            }
        }
       
        private ItemStack setItemNameAndLore(ItemStack item, String name, String[] lore) {
            ItemMeta im = item.getItemMeta();
                im.setDisplayName(name);
                im.setLore(Arrays.asList(lore));
            item.setItemMeta(im);
            return item;
        }
       
    }
    To use it, first you need to create an IconMenu object:
    Code:
            IconMenu menu = new IconMenu("My Fancy Menu", 9, new IconMenu.OptionClickEventHandler() {
                @Override
                public void onOptionClick(IconMenu.OptionClickEvent event) {
                    event.getPlayer().sendMessage("You have chosen " + event.getName());
                    event.setWillClose(true);
                }
            }, plugin)
            .setOption(3, new ItemStack(Material.APPLE, 1), "Food", "The food is delicious")
            .setOption(4, new ItemStack(Material.IRON_SWORD, 1), "Weapon", "Weapons are for awesome people")
            .setOption(5, new ItemStack(Material.EMERALD, 1), "Money", "Money brings happiness");
    
    Then, you can open the menu for any player:
    Code:
    menu.open(player);
    
    When setting up the menu, remember that the menu size must be a multiple of 9. Also, the menu name should be unique, since the listener uses it to determine if the menu option clicked applies to the menu it is listening for. You will also need to pass an instance of your plugin into the constructor.

    This is built with the idea that you initialize one menu object, and use it multiple times. The best idea would be to initialize the menu in your onEnable method, then use it throughout the plugin. If you no longer need to use a given menu object, you can call menu.destroy() to unregister the listener, or event.setWillDestroy(true) from within the option click listener.

    Please feel free to use this in any of your projects, I've posted it here so that everyone can use it. If you use this extensively, you may want to make some modifications, which is fine!

    Edit: Updated to the new ItemMeta code, (thanks to ftbastler for updating it)
     
  2. Offline

    hawkfalcon

    ?!
    Photos of this in action?
     
    Scizzr likes this.
  3. Offline

    lucasdidur

    Cool, I like :D
     
  4. Offline

    Sehales

    sounds cool but doesn't work for me, I am getting a NPE at the IconMenu class at the line where you are registering the listener it would be awesome if I could use this but I don't know whats wrong, the instance of the plugin is valid and I don't think that you are making it wrong:
    plugin.getServer().getPluginManager().registerEvents(this, plugin);
     
  5. Offline

    nisovin

    The only way for that line to throw an NPE is if plugin is null.
     
  6. Offline

    Sehales

    that is the crazy thing cause I can make a simple print() with plugin.getName() for example and it works!?! I am really confused
    I have a really cool idea for my plugin will there be a way that you help me with that? If yes pm me I will explain you my plans. I think I am tired and have mistyped something...it will work tomorrow I think!
     
  7. Offline

    stirante

    Could you post some screenshots?
    Btw. [.syntax=java]code[./syntax] would be much better
     
  8. Offline

    Sehales

    EDIT: It now crashes with a NPE at setOption() at this line:
    Code:
    optionIcons[position] = setItemNameAndLore(icon, name, info);
    It is 1 on 1 your code I have nothing edited at the moment.....
     
  9. Offline

    nisovin

    I assure you it does work, so you must be doing something wrong. And as a Java programmer, it seems like you should be able to debug an NPE on your own. However, if you post your code, I'll have a look.
    I disagree. I don't see any benefit to having syntax highlighting on this, if you want to see it, just paste it into your code editor. And the syntax tag tends to screw up formatting when you edit the post.
     
  10. Offline

    Sehales

    I have no time to debug this because this should only be a funny feature, not a main feature and I am rewriting my whole plugin at the moment. But yes you are right! I should have to be able to debug it by myself. It would be very cool if you look over the code.
    Btw do you speak german?
    Here are my classes:

    CommandExecutor:
    Code:
    import net.sehales.secon.SeCon;
    import net.sehales.secon.objects.IconMenu;
     
    import org.bukkit.Material;
    import org.bukkit.command.Command;
    import org.bukkit.command.CommandExecutor;
    import org.bukkit.command.CommandSender;
    import org.bukkit.entity.Player;
    import org.bukkit.inventory.ItemStack;
     
    public class CmdTest implements CommandExecutor {
     
        @Override
        public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
            if (sender instanceof Player) {
                Player p = ((Player) sender).getPlayer();
                IconMenu menu = new IconMenu("Menu", 27, new IconMenu.OptionClickEventHandler() {
     
                    @Override
                    public void onOptionClick(IconMenu.OptionClickEvent event) {
                        event.getPlayer().sendMessage("You have chosen " + event.getName());
                        event.setWillClose(true);
                    }
                }, SeCon.plugin);
                menu.setOption(3, new ItemStack(Material.APPLE, 1), "Food", "The food is delicious");
                menu.setOption(4, new ItemStack(Material.IRON_SWORD, 1), "Weapon", "Weapons are for awesome people");
                menu.setOption(5, new ItemStack(Material.EMERALD, 1), "Money", "Money brings happiness");
                menu.open(p);
                return true;
            }
            return false;
        }
    }
    IconMenu:
    Code:
    import net.minecraft.server.NBTTagCompound;
    import net.minecraft.server.NBTTagList;
    import net.minecraft.server.NBTTagString;
     
    import org.bukkit.Bukkit;
    import org.bukkit.craftbukkit.inventory.CraftItemStack;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.EventPriority;
    import org.bukkit.event.HandlerList;
    import org.bukkit.event.Listener;
    import org.bukkit.event.inventory.InventoryClickEvent;
    import org.bukkit.inventory.Inventory;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.plugin.Plugin;
     
    public class IconMenu implements Listener {
     
        private String                  name;
        private int                    size;
        private OptionClickEventHandler handler;
        private Plugin                  plugin;
     
        private String[]                optionNames;
        private ItemStack[]            optionIcons;
     
        public IconMenu(String name, int size, OptionClickEventHandler handler, Plugin plugin) {
            this.name = name;
            this.size = size;
            this.handler = handler;
            this.plugin = plugin;
            this.optionNames = new String[size];
        }
     
        public IconMenu setOption(int position, ItemStack icon, String name, String... info) {
            optionNames[position] = name;
            optionIcons[position] = setItemNameAndLore(icon, name, info);
            return this;
        }
     
        public void open(Player player) {
            Inventory inventory = Bukkit.createInventory(player, this.size, this.name);
            for (int i = 0; i < optionIcons.length; i++) {
                if (optionIcons[i] != null) {
                    inventory.setItem(i, optionIcons[i]);
                }
            }
            player.openInventory(inventory);
        }
     
        public void destroy() {
            HandlerList.unregisterAll(this);
            handler = null;
            plugin = null;
            optionNames = null;
            optionIcons = null;
        }
     
        @EventHandler(priority = EventPriority.MONITOR)
        void onInventoryClick(InventoryClickEvent event) {
            if (event.getInventory().getTitle().equals(name)) {
                event.setCancelled(true);
                int slot = event.getRawSlot();
                if (slot >= 0 && slot < size && optionNames[slot] != null) {
                    Plugin plugin = this.plugin;
                    OptionClickEvent e = new OptionClickEvent((Player) event.getWhoClicked(), slot, optionNames[slot]);
                    handler.onOptionClick(e);
                    if (e.willClose()) {
                        final Player p = (Player) event.getWhoClicked();
                        Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
     
                            public void run() {
                                p.closeInventory();
                            }
                        }, 1);
                    }
                    if (e.willDestroy()) {
                        destroy();
                    }
                }
            }
        }
     
        public interface OptionClickEventHandler {
     
            public void onOptionClick(OptionClickEvent event);
        }
     
        public class OptionClickEvent {
     
            private Player  player;
            private int    position;
            private String  name;
            private boolean close;
            private boolean destroy;
     
            public OptionClickEvent(Player player, int position, String name) {
                this.player = player;
                this.position = position;
                this.name = name;
                this.close = true;
                this.destroy = false;
            }
     
            public Player getPlayer() {
                return player;
            }
     
            public int getPosition() {
                return position;
            }
     
            public String getName() {
                return name;
            }
     
            public boolean willClose() {
                return close;
            }
     
            public boolean willDestroy() {
                return destroy;
            }
     
            public void setWillClose(boolean close) {
                this.close = close;
            }
     
            public void setWillDestroy(boolean destroy) {
                this.destroy = destroy;
            }
        }
     
        private ItemStack setItemNameAndLore(ItemStack item, String name, String[] lore) {
            CraftItemStack craftItem;
            if (item instanceof CraftItemStack) {
                craftItem = (CraftItemStack) item;
            } else {
                craftItem = new CraftItemStack(item);
            }
     
            NBTTagCompound tag = craftItem.getHandle().tag;
            if (tag == null) {
                tag = new NBTTagCompound();
                craftItem.getHandle().tag = tag;
            }
            NBTTagCompound disp = tag.getCompound("display");
            if (disp == null) {
                disp = new NBTTagCompound("display");
            }
     
            disp.setString("Name", name);
     
            if (lore != null && lore.length > 0) {
                NBTTagList list = new NBTTagList("Lore");
                disp.set("Lore", list);
                for (String l : lore) {
                    list.add(new NBTTagString("", l));
                }
            }
     
            tag.setCompound("display", disp);
     
            return craftItem;
        }
     
    }
     
  11. Offline

    nisovin

    Sehales You seem to have modified that class, a couple lines are missing from the constructor.
     
  12. Offline

    Sehales

    oh yes I see it but why??? I have only copied and pasted it... lol okay thx :D I think I am going crazy someday....
     
  13. Offline

    Sehales

    okay now it works fine. Here are some screenshots:
    [​IMG]
    [​IMG]
    [​IMG]
     

    Attached Files:

  14. Offline

    WarmakerT

    I'm still waiting for someone to play Youtube videos with this :p !
     
  15. Offline

    Sehales

    Ask sk89q he has made it possible to watch youtube videos on a crafted minecraft map :D
     
  16. Offline

    desht

    A clever, inventive, use of inventories!

    I have some ideas about incorporating this into ScrollingMenuSign as a new view type... nisovin do you mind if I use some of the ideas here? You will of course be credited.
     
  17. Offline

    nisovin

    Go ahead!
     
  18. Offline

    Sehales

    I am doing a custom configuration and command system with it!
    That is awesome what you could do with it.
     
  19. Offline

    JeroenV

    Do you need the latest craftbukkit for this?
     
  20. Offline

    evilmidget38 Retired Staff

    I would like to say this is one of the most intuitive and amazing new ideas I've seen here. As I've gotten more time lately I can finally continue to work on Bukkit Arena, and I'd love to add this feature for class selection. This would not only allow for visual representation of what items each class has, but it would just be really easy to use. Do you mind if I use this? I'd of course credit you.

    EDIT: Just thought I'd mention that there is no way I can physically display in text how excited I am about this. Visual menus such as this are absolutely amazing, and a huge step forwards in making plugins user friendly.
     
    Hoolean and lol768 like this.
  21. Offline

    Sehales

    Yes you are right evilmidget38 :D
    I cannot physically display in text how amazing that is...
     
  22. Offline

    ftbastler

    Looks very cool. Might use it for The BukkitGames!
     
  23. Offline

    desht

    iPhysX and blackwolf12333 like this.
  24. Offline

    ftbastler

  25. Offline

    bobacadodl

    This is great! I'll definitely be using this in some of my plugins!
     
  26. Offline

    Cammy_the_block

    I'm confused on how this works. So you have a chest like looking gui and when you take an item it triggers some code?

    Thanks
    CAMMY
    PS: This looks awesome and might try using it.
     
  27. Offline

    desht

    Yep, that's pretty much it. There are a couple of screenshots posted in the thread. Note that you don't actually get to take any item - the InventoryClickEvent is always cancelled, so you can't pick the item up (but it twitches a little, which has the nice effect of some visual feedback when you click). When the item is clicked, it calls the overridden onOptionClick() method that you passed when you created the IconMenu object.
     
  28. Offline

    LaxWasHere

    Interesting. Gonna try and use this. One command for everything.

    I'm loving this.
    [​IMG]
     
  29. Offline

    md_5

    You should update this to the new ItemMeta api. Also saw LaxWasHere 's thing, amazing!
     
  30. Offline

    ftbastler

    Just got some time so I updated it. Hope it is ok.

    PHP:
    package utilities;

    import java.util.Arrays;

    import org.bukkit.Bukkit;
    import org.bukkit.entity.Player;
    import org.bukkit.event.EventHandler;
    import org.bukkit.event.EventPriority;
    import org.bukkit.event.HandlerList;
    import org.bukkit.event.Listener;
    import org.bukkit.event.inventory.InventoryClickEvent;
    import org.bukkit.inventory.Inventory;
    import org.bukkit.inventory.ItemStack;
    import org.bukkit.inventory.meta.ItemMeta;
    import org.bukkit.plugin.Plugin;
     
    public class 
    IconMenu implements Listener {
     
        private 
    String name;
        private 
    int size;
        private 
    OptionClickEventHandler handler;
        private 
    Plugin plugin;
       
        private 
    String[] optionNames;
        private 
    ItemStack[] optionIcons;
       
        public 
    IconMenu(String nameint sizeOptionClickEventHandler handlerPlugin plugin) {
            
    this.name name;
            
    this.size size;
            
    this.handler handler;
            
    this.plugin plugin;
            
    this.optionNames = new String[size];
            
    this.optionIcons = new ItemStack[size];
            
    plugin.getServer().getPluginManager().registerEvents(thisplugin);
        }
       
        public 
    IconMenu setOption(int positionItemStack iconString nameString... info) {
            
    optionNames[position] = name;
            
    optionIcons[position] = setItemNameAndLore(iconnameinfo);
            return 
    this;
        }
       
        public 
    void open(Player player) {
            
    Inventory inventory Bukkit.createInventory(playersizename);
            for (
    int i 0optionIcons.lengthi++) {
                if (
    optionIcons[i] != null) {
                    
    inventory.setItem(ioptionIcons[i]);
                }
            }
            
    player.openInventory(inventory);
        }
       
        public 
    void destroy() {
            
    HandlerList.unregisterAll(this);
            
    handler null;
            
    plugin null;
            
    optionNames null;
            
    optionIcons null;
        }
       
        @
    EventHandler(priority=EventPriority.MONITOR)
        
    void onInventoryClick(InventoryClickEvent event) {
            if (
    event.getInventory().getTitle().equals(name)) {
                
    event.setCancelled(true);
                
    int slot event.getRawSlot();
                if (
    slot >= && slot size && optionNames[slot] != null) {
                    
    Plugin plugin this.plugin;
                    
    OptionClickEvent e = new OptionClickEvent((Player)event.getWhoClicked(), slotoptionNames[slot]);
                    
    handler.onOptionClick(e);
                    if (
    e.willClose()) {
                        final 
    Player p = (Player)event.getWhoClicked();
                        
    Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {
                            public 
    void run() {
                                
    p.closeInventory();
                            }
                        }, 
    1);
                    }
                    if (
    e.willDestroy()) {
                        
    destroy();
                    }
                }
            }
        }
        
        public interface 
    OptionClickEventHandler {
            public 
    void onOptionClick(OptionClickEvent event);       
        }
        
        public class 
    OptionClickEvent {
            private 
    Player player;
            private 
    int position;
            private 
    String name;
            private 
    boolean close;
            private 
    boolean destroy;
           
            public 
    OptionClickEvent(Player playerint positionString name) {
                
    this.player player;
                
    this.position position;
                
    this.name name;
                
    this.close true;
                
    this.destroy false;
            }
           
            public 
    Player getPlayer() {
                return 
    player;
            }
           
            public 
    int getPosition() {
                return 
    position;
            }
           
            public 
    String getName() {
                return 
    name;
            }
           
            public 
    boolean willClose() {
                return 
    close;
            }
           
            public 
    boolean willDestroy() {
                return 
    destroy;
            }
           
            public 
    void setWillClose(boolean close) {
                
    this.close close;
            }
           
            public 
    void setWillDestroy(boolean destroy) {
                
    this.destroy destroy;
            }
        }
       
        private 
    ItemStack setItemNameAndLore(ItemStack itemString nameString[] lore) {
            
    ItemMeta im item.getItemMeta();
                
    im.setDisplayName(name);
                
    im.setLore(Arrays.asList(lore));
            
    item.setItemMeta(im);
            return 
    item;
        }
       
    }
     
Thread Status:
Not open for further replies.

Share This Page