Panel 1.9-SNAPSHOT

A powerful inventory management system designed for making UIs in Minecraft.

  1. CovertLizard
    logo.png


    UPDATE:
    Panel 2.0 is in progress; the update should be rolling out within the week! MASSIVE improvements and stability, as well as a TON of new features!







    About:
    Panel is a library that allows you to create advanced inventories with a multitude of options. It provides many functions that assist in inventory creation and manipulation.

    Features:
    • Action Based Event System
    • Inventory Hierarchy
    • Inventory Layouts
    • Easy Animation Implementation
    • Automatic Inventory Modification Prevention
    • Easy Panel Switching
    • Shaded and Plugin Library Branches
    Plugins:
    These are some plugins that use the Panel API:
    • Comment your plugin if you wish it to be added to this list!
    Animations:
    Here are some cool animations done in inventories using the Panel API:
    arrow.gif
    loading.gif

    Installation:

    Panel features a Shaded branch of the library, which is entirely optional. Installation methods could be done using one of these two methods:

    Plugin (Recommended):
    Since you're using the plugin typed branch of Panel you're going to need to add the jar file from this website into your plugins folder, as well as adding the following to your plugin.yml file:
    Code (Text):
    depend: [Panel]
    After this, you can add the library to your project using either of the following methods:
    Maven (Recommended):

    Using maven with this project is simple, just add the following to your pom.xml file:
    Code (Text):

    <repository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </repository>

    <dependency>
       <groupId>com.github.covertlizard</groupId>
       <artifactId>panel</artifactId>
       <version>1.9-SNAPSHOT</version>
       <scope>compile</scope>
    </dependency>
     

    Build Path:
    If you wish to use this library without a dependency manager: simply download the "panel-1.9-SNAPSHOT.jar" from this site and add it to your project's build path. (Please ensure you don't download the "panel-1.9s-SNAPSHOT.jar" as it's the shaded version of the library.

    Shaded:
    Installation of Panel using the shaded version is very similar to the plugin method, however there is one additional step to using this branch. First, you must add the library to your project using either methods:

    Maven (Recommended):
    Using maven with this project is simple, just add the following to your pom.xml file:

    Code (Text):

    <repository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </repository>

    <dependency>
       <groupId>com.github.covertlizard</groupId>
       <artifactId>panel</artifactId>
       <version>1.9s-SNAPSHOT</version>
       <scope>compile</scope>
    </dependency>
     

    Build Path:
    If you wish to use this library without a dependency manager: simply download the "panel-1.9s-SNAPSHOT.jar" from this site (in version history) and add it to your project's build path. (Please ensure you don't download the "panel-1.9-SNAPSHOT.jar" as it's the non-shaded version of the library.

    After you have completed one of the previous steps, you are going to want to call the registry function in your plugin's onEnable function:
    Code (Text):

    @Override
    public void onEnable()
    {
         Panel.Registry.register(this); //registers the listener with this plugin
    }
     
    This will enable the ActionAPI to function, which is completely optional; although I recommend its use.

    Usage:
    After you have finished the installation instructions you're all set to start using the Panel API!
    To begin using Panel we must first be introduced with the Hierarchy:
    Code (Text):
    PanelGroup -> Panel -> Layout -> Component

    Now, before we elaborate on what a PanelGroup is, I'm going to explain the most fundamental object on the hierarchy: the Panel.

    What is a panel?
    Think of a panel as an "Advanced" inventory. Rather than containing items, it contains Layouts. By default, panels come with a layout "0" which is the "Main" layout. Each panel is equipped with an inventory instance that is created when a new instance of a Panel is constructed.

    What is a layout?
    A layout contains a bunch of components or items that will be applied to the Panel. The most important feature of layouts, is the ability to change them on the fly. This feature enables you to have easier animation implementation. A layout can be added to the panel and switched at any time, giving you the ability to change how the inventory looks.

    Panel example #1:
    To create a panel you can do the following:
    Code (Text):
    Panel myPanel = new Panel(ChatColor.RED + "My Panel", 3);
    The first parameter for this specific constructor is the Title that is displayed when a player views the Panel. The second parameter is the amount of rows to include, so the actual size of the inventory would be (rows * 9). This is to prevent accidental inventory sizes that aren't multiples of 9.

    Let's say we want to add a diamond in the center of this Panel that sends the player a cool message.
    First we must add this diamond to the newly created 'myPanel' object. We can do this by doing the following:
    Code (Text):
    myPanel.getCurrent().introduce(4, new ItemStack(Material.DIAMOND));
    What we're doing here is getting the default/main layout "0" and adding a diamond item at the fifth slot (the center of the inventory) to it.
    Now this diamond does nothing if it is clicked, so let's add some functionality:
    Code (Text):
    myPanel.getCurrent().component(4).action(event -> event.getWhoClicked().sendMessage("JUST DO IT!"));
    The example above uses Java 8 (Which I highly recommend you update to, although it isn't required!). So what's going on here? Well we're telling the layout that the item at the fifth slot (remember the first is 0) when clicked should execute the Player#sendMessage function. In the action parameter we're creating a new interface (Panel.Action) instance and overriding the click(InventoryClickEvent event) function.

    After we have done all this we must finally apply the main layout to the panel by calling the following function:
    Code (Text):
    myPanel.update();
    After you've done that the Panel is all set to be displayed! Wait! How do we display it you asked? Simply call the function 'display' like so:
    Code (Text):
    myPanel.display(player);
    "player" is the Player (or HumanEntity) that you would like to display the Panel to.

    That would conclude this example, if you have any questions please feel free to leave a comment in this thread. Thanks for giving Panel a try! :)
    Panel Example #2:
    Now that we've got down the basics, we're gonna try creating a Panel with more advanced concepts. In this example I am going to cover animations, and player-specific panels.

    Let's say we want the player to type in a command called "info". What this command will do is open a Panel and display a sweet loading animation, then change the layout to display information about them, such as their rank and kill count. Let's begin!


    First and foremost, let's register our command in our JavaPlugin class:
    Code (Text):

    @Override
    public void onEnable()
    {
         this.getCommand("info").setExecutor(new CommandInfo(this));
    }
     
    Now that we've done this, we need someway of creating information for the player, so we're going to use a Listener and also register it:
    Code (Text):

    @Override
    public void onDisable()
    {
         this.getCommand("info").setExecutor(new CommandInfo(this)); // the command we registered above
         this.getServer().getPluginManager().registerEvents(new ListenerInfo(this), this); // our new listener
    }
     
    With these two classes registered we can now start expanding upon them. We will begin with the Listener, as it would make more sense later on.


    The listener is going to be pretty simple. Metadata will be inserted upon the PlayerJoinEvent; inserting a random amount of kills and the default rank "vip":
    Code (Text):

    public class ListenerInfo implements Listener
    {
         private final JavaPlugin plugin;
         public ListenerInfo(JavaPlugin plugin)
         {
              this.plugin = plugin;
              // this is our plugin instance that we passed through the constructor in the onEnable function
         }

         @EventHandler
         public void onPlayerJoinEvent(PlayerJoinEvent event)
         {
              Player player = event.getPlayer(); // gets player instance
              player.setMetadata("kills", new FixedMetadataValue(this.plugin, new Random().nextInt(100)));
              // inserts the metadata "kills" into the player with a random count between 0-100
              player.setMetadata("rank", new FixedMetadataValue(this.plugin, "vip");
              // inserts the metadata "rank" into the player with the default value of "vip"
         }
    }
     
    With that, we can start working on our command.
    First, we will add the necessary functions for our CommandExecutor:
    Code (Text):

    public class CommandInfo implements CommandExecutor
    {
         private final JavaPlugin plugin;
         public CommandInfo(JavaPlugin plugin)
         {
              this.plugin = plugin;
              // this is our plugin instance we passed through the constructor in the onEnable function
         }

         //override the onCommand function
         @Override
         public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
         {
              if(args.length != 0)
              {
                   sender.sendMessage(ChatColor.RED + "Please type: /info");
                   // if there is any arguments we are going to cancel the command
                   return true;
              }
              return true;
         }
    }
     
    With that out of the way, let's begin to add Panel functionality.
    We're going to create a new class called PanelInfo:
    Code (Text):

    public class PanelInfo extends Panel implements Runnable
    {
        private int id = -1; // our schedular task id
        private int count = 0; // second countdown reference
        private final Layout infoLayout; // the player information layout

        public PanelInfo(Player player)
        {
            super(player, ChatColor.DARK_GREEN + "Please wait...", 3);
            // create's a panel with the player as the holder and a size of 3 rows (27 slots) with a display name of "Please wait..."
            infoLayout = new Layout();
            // creates a new layout that will contain the player's information
            super.introduce(this.infoLayout, 1);
            // adds the layout to the panel with an id of "1"
            infoLayout.introduce(4, new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 14));
            // adds a red stained glass pain in the first row at the center slot
            infoLayout.introduce(22, new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 14));
            // adds a red stained glass pain in the last row at the center slot
            ItemStack playerHead = new ItemStack(Material.SKULL_ITEM, 1, (short) 3);
            SkullMeta skullMeta = (SkullMeta) playerHead.getItemMeta();
            skullMeta.setOwner(player.getName());
            skullMeta.setDisplayName(ChatColor.BLUE + player.getName());
            // creates a new player head with the display name of the player
            ArrayList<String> lore = new ArrayList<>();
            lore.add(ChatColor.RED + "Kills: " + ChatColor.DARK_RED + player.getMetadata("kills").get(0).asInt());
            // the amount of kills the player has (random between 0-100)
            lore.add(ChatColor.DARK_AQUA + "Rank: " + ChatColor.AQUA + player.getMetadata("rank").get(0).asString());
            // the player's default rank (vip)
            skullMeta.setLore(lore);
            // sets the lore
            playerHead.setItemMeta(skullMeta);
            // sets the metdata
            infoLayout.introduce(13, playerHead);
            // adds the player head in the center row in the center slot
            super.fill(1, new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 1));
            // fills the empty slots in the info layout with orange stained glass

            Layout current = super.getCurrent();
            // gets the current default layout instance
            ItemStack timeStack = new ItemStack(Material.SIGN);
            ItemMeta meta = timeStack.getItemMeta();
            meta.setDisplayName(ChatColor.RED + "Loading...");
            timeStack.setItemMeta(meta);
            current.introduce(4, timeStack);
            //introduces a new ItemStack with a display name of loading at the center of the first row in the inventory
            for(int index0 = 0; index0 < 9; index0 += index0 == 3 ? 2 : 1) current.introduce(index0, new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 7));
            // adds gray stained glass panes from slots 0-3 and 5-9
            for(int index1 = 9; index1 < 18; index1++) current.introduce(index1, new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 3));
            // adds light blue stained glass panes from slots 9-17 (loading bar)
            for(int index2 = 18; index2 < 27; index2++)  current.introduce(index2, new ItemStack(Material.STAINED_GLASS_PANE, 1, (short) 7));
            // adds gray stained glass panes from slots 18-26
            super.update();
            // updates the changes we made to the current layout

        }
        // begins/resumes loading their data...
        public void load(JavaPlugin plugin)
        {
            if(this.id != -1 && this.id < 8) this.id = Bukkit.getServer().getScheduler().runTaskTimer(plugin, this, 0, 20).getTaskId();
            // starts this as a timer set to call the "run" method every second IF the timer hasn't started OR been ran already
        }

        @Override
        public void run()
        {
            if(this.count == 8)
            {
                Bukkit.getServer().getScheduler().cancelTask(this.id);
                //cancels this task if it has reached the peak value (8)
                super.update(1);
                // updates the inventory with the infolayout now that the loading has stopped
                return;
            }
            ItemStack stack = super.getCurrent().stack(this.count + 9);
            // have to add 9 to the count so we can get the item in the center row of the panel
            stack.setDurability((short) 11);
            //replaces the light blue stained glass with a dark blue stained glass pane, giving the loading animation
            this.count++;
        }
    }
     
    This may seem complicated at first, but with practice it will come to you with ease.
    First we are creating the Panel that displays the information about the player. It would look something like this:

    Screenshot_1.png
    Hovering over the player's head would display the information. (Rank, Kills)
    Afterwards, we're creating the "Loading" animation panel which looks like this:
    Screenshot_2.png

    With all of the layouts we created, we're ready to start displaying them to the player!
    Let's go back to our Command class, we need to implement the displaying and creation of our new panel:
    Code (Text):

    public class CommandInfo implements CommandExecutor
    {
         private final JavaPlugin plugin;
         public CommandInfo(JavaPlugin plugin)
         {
              this.plugin = plugin;
              // this is our plugin instance we passed through the constructor in the onEnable function
         }

         //override the onCommand function
         @Override
         public boolean onCommand(CommandSender sender, Command command, String label, String[] args)
         {
              if(args.length != 0)
              {
                   sender.sendMessage(ChatColor.RED + "Please type: /info");
                   // if there is any arguments we are going to cancel the command
                   return true;
              }
              if(!(sender instance of Player))
              {
                   sender.sendMessage(ChatColor.RED + "Silly you! Only players can open Panels!");
                   // if the sender isn't a player they cannot open/create a panel
                   return true;
              }
              Player player = (Player) sender; // cast the sender to a player
              if(!player.hasMetadata("panel_info"))
              {
                   PanelInfo panel = new PanelInfo(player);
                   player.setMetadata("panel_info", new FixedMetadataValue(this.plugin, panel));
                   //creates the new panel for the player since they didn't have one
                   panel.load(this.plugin);
                   // begins the loading animation
              }
              Panel.class.cast(player.getMetadata("panel_info").get(0).value()).display(player);
              //finally displays the panel to the player
              return true;
         }
    }
     
    And that's it! You should now have a basic idea of animations and how to implement them, as well as knowing how to create player specific panels. If you have any questions please feel free to leave a question in this thread! ;)

    Source:
    This project is open sourced! You may view the source code at this website: https://github.com/CovertLizard/Panel
    zThana likes this.

Recent Updates

  1. Fixed metadata file.
  2. Fixed shaded version.
  3. Shaded Library Version

Recent Reviews

  1. Freack100
    Freack100
    5/5,
    Version: 1.9-SNAPSHOT
    I'm completely in love with this! <3 The way it works is so awesome in my opinion! But it looks like this isn't maintained anymore ;-;