Skip to content

Introduction

SteveTheEngineer edited this page Apr 26, 2022 · 9 revisions

!!! WARNING !!!

This page has not yet been updated to the latest versions of SS-GuiLibrary! The documentation below is only applicable to version 11!

Introduction

Welcome to the introduction of Steve's Series | Inventory GUI Library! This guide is intended for the ones who wish to use the library in their project, but don't yet know how to use it! The library is quite powerful in terms of inventory GUIs, especially since version 5 that introduced the new widget API.

The Widgets

A widget is a basic building block for your GUI. A GUI in the library is essentially a tree of widgets. Widgets may or may not be reusable, it depends on how you make them. Let's say you have a label widget, and it displays whatever text you provide it with. If you make the text a parameter, you will be able to have multiple instances of the widget with different texts. That would make the widget reusable. If you hard-code the text in the widget, every instance you create will have the same text, and you can't change it, so it can only be used for one purpose. That would be a non-reusable widget. GUIs are usually non-reusable, and utility widgets are usually reusable

A basic GUI

So let's say we want a GUI with a toggle button and a radio select button. We won't be using child widgets in this example. The first thing we want to do is to create a class that extends Widget, create a constructor that calls super and implement the necessary methods:

public class MyGUI extends Widget {
    public MyGUI() {
        super(0, 0, 9, 1); // The first two integers is the offset x and y of the widget. For GUIs we want them both to be 0. The second two are width and height. Chest GUIs always have a width of 9, and 1 is simply the height of the slots. If it was a widget for use in other widgets, we'd want to pass those values in the constructor for more flexibility
    }

    @Override
    public void render(GridInventory grid) {
        
    }
}

And now we've created our basic GUI. If we open it for a player using GUIManager#open(Player, Widget), the player will see a 9x1 empty inventory, and the title will be "MyGUI". But what if we want to have our own title, and not the class name? In this case, we'll also have to override createInventory. This is needed only if your Widget is intended to be used as a GUI:

@Override
public Inventory createInventory() {
    return Bukkit.createInventory(null, this.getWidth() * this.getHeight(), "Custom Title"); // The first argument is null, because we don't want the inventory to have an owner. The second one is the inventory size. This is simply width multiplied by height. And, finally, the last argument is our inventory title
}

Now if we test it again, we'll see that the inventory now has got a custom title! I recommend to override that method regardless of whether you want to have a title that is not the class name or not. Now, you ask, how do we display something in the inventory? That's actually really simple to do:

@Override
public void render(GridInventory grid) {
    grid.set(this.getRealX(), this.getRealY(), new ItemStack(Material.LIME_CONCRETE)); // Sets the item in the slot at [0, 0] to a lime concrete. You may use 0, 0 instead of this.getRealX(), this.getRealY(), but for widgets that are meant to be children of other widgets, you always want to use this.getRealX() and this.getRealY(), because using 0, 0 will set the slot in the top left corner of the inventory, regardless of the widget offset and parent element position
}

If we test it now, a lime concrete appears on the left of the inventory. Now, what if we want to do something when a player clicks on it? That's where the event handlers come in. The onClick handler is probably the one that you will use the most of them all:

@Override
public void onClick(int x, int y, ClickType type, GridInventory grid) {
    if(type == ClickType.DOUBLE_CLICK) {
        return; // You want to ignore double clicks in most circumstances. If you need to also handle double clicks, just handle them separately here. Or if you've got your own way to do that, then go on!
    }

    if(x == 0 && y == 0) { // We want to execute the code only if the clicked slot is [0, 0]. That's where we've put our lime concrete 
        this.getPlayer().sendMessage("Hello, world!"); // Send a message to the player to make sure it works. this.getPlayer() returns the player who currently sees the widget. That's how you get the player who has the GUI open
    }
}

Now if we click on the lime concrete, we'll see a Hello, world! message in the chat. Okay, now we want to change the lime concrete to red and back when it's clicked. First we want to add a field to the GUI class:

private boolean state = false;

Next we want to show the item depending on the current state:

@Override
public void render(GridInventory grid) {
    grid.set(this.getRealX(), this.getRealY(), new ItemStack(this.state ? Material.LIME_CONCRETE : Material.RED_CONCRETE)); // Lime concrete for true, red concrete for false
}

And finally invert the boolean whenever we click the concrete:

@Override
public void onClick(int x, int y, ClickType type, GridInventory grid) {
    if(type == ClickType.DOUBLE_CLICK) {
        return;
    }

    if(x == 0 && y == 0) {
        this.state = !this.state; // Invert the boolean
        this.rerender(); // You have to call this.rerender() whenever something has been changed and needs updating on the screen. In this case, we've inverted the state field that controls the color of the concrete
    }
}

And we've created our basic toggle switch! Now let's also make the radio button as we wanted. The field:

private boolean state = false;
private int option = 0;

The radio buttons:

@Override
public void render(GridInventory grid) {
    grid.set(this.getRealX(), this.getRealY(), new ItemStack(this.state ? Material.LIME_CONCRETE : Material.RED_CONCRETE));

    for(int i = 0; i < 3; i++) { // Do 3 times
        grid.set(this.getRealX() + 2 + i, this.getRealY(), new ItemStack(this.option == i ? Material.GRAY_CONCRETE : Material.WHITE_CONCRETE)); // Add the button. Set the material to gray concrete if it's selected and white otherwise, with the x position being 2 + option number
    }
}

And the click handler:

@Override
public void onClick(int x, int y, ClickType type, GridInventory grid) {
    if(type == ClickType.DOUBLE_CLICK) {
        return;
    }

    if(x == 0 && y == 0) {
        this.state = !this.state;
        this.rerender();
    } else if (y == 0) { // Do only if the y is 0
        for(int i = 0; i < 3; i++) { // Do 3 times
            if(x == 2 + i) { // Check if the x is 2 + option number
                this.option = i; // Set the option to option number
                this.rerender(); // Re-render the GUI
                break;
            }
        }
    }
}

And there we have it! Congratulations, you've now learned how to create widgets and basic GUIs

Advanced GUIs

If you've got a complex GUI with a lot of different GUI elements, you'll have to organize them to keep everything clean. That's where widgets actually come in. In this section I will be only giving a brief explanation for what's going on. If you're a good developer, you should be capable of figuring out the rest yourself. In this example we'll be using a built-in widget, the ButtonWidget:

public class MyGUI extends Widget {
    public MyGUI() {
        super(0, 0, 9, 1);

        MyGUI self = this;
        this.addChild(new ButtonWidget(0, 0, 1, 1) { // Reusable widgets usually accept their offset and dimensions as constructor parameters
            @Override
            protected ItemStack getItem() {
                return new ItemStack(Material.DIAMOND);
            }

            @Override
            protected void click() {
                self.getPlayer().sendMessage("Click!");
            }
        });
    }

    @Override
    public void render(GridInventory grid) {}

    @Override
    public Inventory createInventory() {
        return Bukkit.createInventory(null, this.getWidth() * this.getHeight(), "My GUI");
    }
}

As for creating custom reusable widgets, refer to one of the builtin widgets to see how it's done.

The End

At this point you should be able to fully make use of the library. I hope you've understood everything you've done during this tutorial. For additional help you can also refer to the javadoc or the source code

Clone this wiki locally