Solved GUI pages system

Discussion in 'Plugin Development' started by DoggyCode™, Jan 22, 2017.

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

    DoggyCode™

    Hey,

    I need an effective way of creating a page system, any utils?

    It has a next and back button, the next page is generated if the inventory is full.
     
  2. Offline

    I Al Istannen

    DoggyCode™ likes this.
  3. Offline

    tyler672

    and i want to add on this, how do i create a gui per player? or in a shop gui plugin a gui for each item??
     
  4. Offline

    DoggyCode™

    Create a new instance of the inventory for each player and open it.

    I assume i will need the extends AbstractPane implements FixedPositionPane, FreeformPane? And is there any example of how i would create this pane?

    EDIT:

    Nope, that system is too big
     
    Last edited: Jan 22, 2017
    tyler672 likes this.
  5. Offline

    I Al Istannen

    @DoggyCode™
    It uses a Swing like Gui system I wrote (the base idea was from Rayzr originally).
    You would need to commit yourself to depending on PerceiveCore (it is a part of it).

    It makes working with Guis really easier though.
    A simple gui is just
    Code:java
    1. PagedPane pagedPane = new PagedPane(9, 5);
    2. Gui gui = new Gui("&6Test gui", 5, pagedPane);
    3.  
    4. pagedPane.addComponent(SimplerLabel.builder()
    5. .setText("&3&lHello World")
    6. .setColor(DisplayColor.RED)
    7. .build()
    8. );
    9.  
    10. gui.open(player);


    This opens a gui with a label "Hello World" in a Gui with the name "&6Test Gui" which has 5 rows.
    It looks like this:
    upload_2017-1-22_21-43-43.png

    upload_2017-1-22_21-44-2.png

    Buttons to the next / previous page appear as needed. You can fully customize the look of the Gui through the populator and page creator functions.

    That is a very basic example and there is a specialized class, AnvilGui, to which you can even add a type listener, which is called every time the player types anything in the anvil:
    [​IMG]

    And a few other panes, apart from PagedPane (GridPane, AnchorPane, FlowPane, TreePane).
    You can also nest panes in panes.

    I am still developing the core plugin [with hopefully some others], so the API is not set in stone, but for a private or smaller plugin it should already be totally usable.
    It has a lot of utilities though, not just GUIs. They are just one part of it.
     
    Last edited: Jan 22, 2017
    DoggyCode™ likes this.
  6. Offline

    DoggyCode™

    So a label is a button? I've worked with Java panes before, but not much enough to completely understand them. So using your Util seems like a very good idea. But PercieveCore is a big plugin, and depending on it for a small plugin like I'm creating is waste. I'd like to use that GUI pane system, but only having necessary classes


    Sent from my iPhone using Tapatalk
     
  7. @DoggyCode™
    Well, in Swing (which is what this is based off of, so I assume it's the same here) a Label is just a piece of text or an icon (or both) shown on screen. I bet there's a seperate Button class.
     
    I Al Istannen likes this.
  8. Offline

    I Al Istannen

    @DoggyCode™
    A label is a button that you can not click. A "Button" (or "SimplerButton") is what you need for a button.

    PerceiveCore is broken up into modules, but I am afraid you will still need most as they have dependencies on each other. Gui needs the "Utilities" module, which needs "Language" and "Config" for a few things. "Config" needs "Reflection" and you have nearly all modules installed.
    Here are all dependencies on the util package:
    upload_2017-1-22_21-57-37.png

    As you can see, it are quite a few.

    I am afraid, but it is either most or nothing with PerceiveCore. It is way too complex to be able to easily isolate a function.

    You can have a look at how it is done, but having a nice, easily extensible Gui system may come at the cost of many, many lines of code.

    It is the easist way I can think of to make a Gui though. All other ways I have tried in the past are extremly annoying and make changes hard.

    What you can do without:
    You can recreate just the PagedPane (though the current implementation is quite lengthy) without the whole system.
    This only really works though, if every button is only one item big (the Gui system allows you define the size).

    You need to do stuff yourself then, I know of no util. It shouldn't be incredible hard though.
    1. Have a reserved space (I used two lines) at the bottom for your controls.
    2. Have a List<Button>, where Button is a class with an ItemStack and a Runnable/Consumer<InventoryClickEvent>, which is called when the button is clicked.
    3. Display as many components as possible.
    4. Make a click listener
    5. If clicked, loop through the list and find the matching button (only works if every button is uninque. I solved it in a different way for the Gui system, but that is way too complex for this)
    6. Execute the runnable of the Button and cancel the click event
    7. If it was a next page button
      1. Clear the Inventory
      2. Add the buttons again, but now starting from the one that was just not displayed
    8. If it is a previous page button
      1. Clear the Inventory
      2. Add the buttons again, but only in the range "last displayed item index -1 - page size" to "last displayed item index - 1"
    That should work, I think. But you need to do stuff yourself.
     
    DoggyCode™ likes this.
  9. Offline

    DoggyCode™

    I got this far:
    Code:java
    1. package me.expdev.gui;
    2.  
    3. import org.bukkit.Material;
    4.  
    5. /**
    6.  * Created by mariusri on 23.01.2017.
    7.  */
    8.  
    9. public class Button {
    10.  
    11. private Material type;
    12. private Runnable clickEvent;
    13.  
    14. public Button(Material type) {
    15. this.type = type;
    16. this.clickEvent = new Runnable() {
    17. public void run() {
    18.  
    19. }
    20. };
    21. }
    22.  
    23. }


    Code:java
    1. package me.expdev.gui;
    2.  
    3. import org.bukkit.Bukkit;
    4. import org.bukkit.Material;
    5. import org.bukkit.event.EventHandler;
    6. import org.bukkit.event.Listener;
    7. import org.bukkit.event.inventory.InventoryClickEvent;
    8. import org.bukkit.inventory.Inventory;
    9. import org.bukkit.inventory.ItemStack;
    10.  
    11. import java.util.ArrayList;
    12. import java.util.List;
    13.  
    14. /**
    15.  * Created by mariusri on 23.01.2017.
    16.  */
    17.  
    18. public class PagedPane {
    19.  
    20. private String title;
    21. private Inventory controls;
    22.  
    23. private List<Button> buttons;
    24.  
    25. private static int MAX_HEIGHT = 4;
    26.  
    27. public PagedPane(String title) {
    28. this.controls = Bukkit.createInventory(null, 54, title);
    29. this.title = title;
    30. this.buttons = new ArrayList<Button>();
    31. }
    32.  
    33. public void setTitle(String title) {
    34. this.title = title;
    35. }
    36.  
    37. public void addButton(Button b) {
    38. if(buttons.size() < 9*3 + 1) {
    39. buttons.add(b);
    40. } else {
    41. PagedPane newPage = new PagedPane("New page");
    42. newPage.addButton(b);
    43. controls.setItem(54, new ItemStack(Material.ARROW));
    44. }
    45. }
    46.  
    47. public void removeButton(Button b) {
    48. buttons.remove(b);
    49. }
    50.  
    51. }


    And now I'm stuck and really can't figure out what do to next. And my IDE gave me an @since error using a Consumer.

    Also, the server using my plugin would have to install PercieveCore and use the plugin at their server for me to be able to use your GUI system? Because honestly, it looks awesome. And if you could make a seperate util alike (I know this is a lot to ask for), which devs could just include in their source, that would be awesome.
     
  10. Offline

    I Al Istannen

    @DoggyCode™
    I will look over the code later. I am currently writing another small Gui plugin (as a test mostly :p), which I would like to finish first.

    Yes, they will need to install PerceiveCore.

    You are quite right with your text in brackets. I could make it seperate (in fact, without any real problems. Copy a few classes and methods and you are done). The problem is, that I will then need to maintain two systems, which I honestly do no twant to do. I did that for MiniNBT and that was enough :p
    Using the core there is a centralized system I can maintain and update, not needing to worry about developers packaging old versions with their plugins.
     
    DoggyCode™ likes this.
  11. Offline

    DoggyCode™

    Appreciate it.
     
  12. Offline

    I Al Istannen

    @DoggyCode™
    That took a while, but it wasn't quite trivial. I wrote an implementation of a PagedPane (called "BadPagedPane"), which does not need any dependencies except Bukkit itself.
    [​IMG]

    It will also correctly handle under- and overflow, dynamically creating and deleting pages as needed, even if you add them while the gui is opened:
    [​IMG]

    I hope that is what you wanted, as it took me a bit over an hour to write...

    It is by far not as powerful as the Gui system, but it is influenced by it to a degree.

    EDIT: Source gist.
     
    DoggyCode™ likes this.
  13. Offline

    DoggyCode™

    Oh hell yeh

    EDIT: Where can i find your ItemFactory util :p
     
  14. Offline

    I Al Istannen

    @DoggyCode™
    The ItemFactory is from PerceiveCore. It is only used in the example code though, so just substitute it with the lengthy version (using ItemMeta).
     
  15. Offline

    DoggyCode™

    I have no idea what kind of magic you have done, but:

    [​IMG] https://gyazo.com/374915eba65ec7dbd7bae3cca48969be


    It does that instead of creating a new page.

    EDIT: I'm also using the latest version. So shouldn't be an issue with that. Did you try it?

    Maybe I'm creating it wrong.

    Code:java
    1. public static BadPagedPane pagedInv(Player p) {
    2. int rows = 6, items = 100;
    3. BadPagedPane pagedPane = new BadPagedPane(rows - 2, items, "&3&lTest");
    4.  
    5. for (int i = 1; i <= items; i++) {
    6. int finalI = i;
    7. pagedPane.addButton(new BadButton(
    8. new ItemStack(Material.STONE, new Random().nextInt(64)),
    9. event -> p.sendMessage("Hey: " + finalI)
    10. ));
    11. }
    12. }


    Then I'm using

    Code:java
    1. Player p = Bukkit.getPlayer("DoggyCode");
    2. pagedInv(p).open(p);



    EDIT:
    Well, it's there :p

    [​IMG]
    https://gyazo.com/6c3e62cc2a83d48b5f1e09ed481f84d4


    EDIT: It was simply because inventory size > 54, fixed :p Thanks a lot for this util @I Al Istannen . We all appreciate you!
     
    Last edited: Jan 23, 2017
  16. Offline

    I Al Istannen

    @DoggyCode™
    You pass "items" not "Rows" resulting in an inventory with 100 Rows.
     
    DoggyCode™ likes this.
  17. Offline

    DoggyCode™

    Now I'm suddenly getting ArrayOutOfBoundsExceptions for

    Code:
    int rows = 6, items = 100;
            BadPagedPane pagedPane = new BadPagedPane(items, rows - 2, "&3&lTest");
    
            for (int i = 1; i <= items; i++) {
                int finalI = i;
                pagedPane.addButton(new BadButton(
                        new ItemStack(Material.STONE, new Random().nextInt(64)),
                        event -> p.sendMessage("Hey: " + finalI)
                ));
            }
    Saying Caused by: java.lang.ArrayIndexOutOfBoundsException: 54
     
  18. Offline

    I Al Istannen

    @DoggyCode™
    Javadoc. It is not that extensive, but that is indeed mentioned.
    Inputing 100 there will make it think it can place 100 items on one page. The correct parameters are:
    BadPagedPane(rows -2, rows, title)

    EDIT: I added some checks to make it throw an error if it receives an invalid value. Makes it a bit less flexible, but if it prevents errors like this it is worth it.
    Same link, or here.
     
  19. Offline

    DoggyCode™

    I actually read that javadoc, but what I don't understand is, what would be a good pagesize? Would that be 9x4? As then you have two rows left? Is that what it means?

    Wait no, that's wrong.

    int rows = 6;
    int pageSize = rows-2;

    ?

    The reason I was confused because in the example you put 100 as page size, so I was thinking that page size was how many items I would put in, but it's not I see now.

    Thank you I see now.

    Sent from my iPhone using Tapatalk
     
    Last edited: Jan 23, 2017
  20. Offline

    I Al Istannen

    @DoggyCode™
    Where do I set it to 100?
    Code:java
    1. int rows = 6, items = 100;
    2. BadPagedPane pagedPane = new BadPagedPane(rows - 2, items, "&3&lTest");


    I always set it to rows - 2. This number is because the last two rows are typically occupied with the controls, though you can change that behaviour by subclassing the pane.
     
  21. Offline

    DoggyCode™

    I know, but you have int items = 100; which you pass in the constructor. What is this number for?


    Sent from my iPhone using Tapatalk
     
  22. Offline

    I Al Istannen

    DoggyCode™ likes this.
Thread Status:
Not open for further replies.

Share This Page