GUI
A GUI (pronounced gee-you-eye) is one of the most important parts of the user experience for people using your plugin. Whether it be server administrators or players, GUI’s are much more user friendly than commands.
Since GUI’s are so vital, Quartz provides first-class support for all types of GUI’s. Let’s make a GUI together:
Menu menu = Menu.Inventory .chest(2) // A 'Chest' GUI with 2 rows .name(Component.empty()) // Without a name .button(Button.builder() // That has a button .item(ItemStack.of(Material.LIME_WOOL)) .slot(3) .leftClicked(ctx -> { ctx.player().sendMessage("left click"); })) .build(); // Always at the end to finish the GUIA Menu is a wrapper over traditional GUI’s that is the foundation of Quartz’s approach to user interfaces.
In the above code, we created a relatively simple menu, though it can be a little hard to read at first glance; let’s break it down step by step:
Menu.Inventory.chest(2)creates a generic (A chest) menu builder that is 2 rows tall..name(Component.empty())is the name of the menu. Menus must always have a name, and the builder takes in aComponent, which in this case, is empty..button()lets us add a button to the menu, either with aButtonor aButtonBuilder. The button builder is self-explanatory, and should not need clarification..build()builds theMenuBuilderinto aMenu, which can be shown to players using theshow(Player)method.
Let’s show this menu to a player:
Player player = // some reference to a player....menu.show(player);Great. Now, time to go through some necessary definitions:
Context: An object that is created when an action occurs in a menu.
State: Something that can change in a
Menu.
Let’s create a simple toggle button:
final Menu invMenu = Menu.Inventory .chest(2) .name(Component.empty()) .state( State.state("clicked", false) ) .button(Button.builder() .item(ItemStack.of(Material.RED_WOOL)) .slot(3) .leftClicked(ctx -> { final Menu menu = ctx.menu(); final State<Boolean> clicked = menu.getState("clicked");
clicked.setValue(!clicked.getValue()); ctx.button().setItem( clicked.getValue() ? ItemStack.of(Material.LIME_WOOL) : ItemStack.of(Material.RED_WOOL) ); })) .build();As you can see, State is not type-safe. If you value type-safety over brevity, you can simply extend
one of the Menu classes in order to keep type safety. Let’s created the same thing, but type-safe:
public class TypeSafeMenu extends InventoryMenu {
public TypeSafeMenu(Component name) { super(MenuType.GENERIC_3X3, name); // A 3 rows tall chest GUI
final State<Boolean> clicked = State.state("clicked", false); this.addState(clicked);
this.addButton( Button.builder() .item(ItemStack.of(Material.RED_WOOL)) .slot(3) .leftClicked(ctx -> {
clicked.setValue(!clicked.getValue()); ctx.button().setItem( clicked.getValue() ? ItemStack.of(Material.LIME_WOOL) : ItemStack.of(Material.RED_WOOL) ); }) .build() ); }}As a matter of fact, you don’t even need State when you are in the constructor. You can simply
use variables, like this:
public class TypeSafeMenu extends InventoryMenu {
public TypeSafeMenu(Component name) { super(MenuType.GENERIC_3X3, name); // A 3 rows tall chest GUI
boolean[] clicked = { false };
this.addButton( Button.builder() .item(ItemStack.of(Material.RED_WOOL)) .slot(3) .leftClicked(ctx -> { clicked[0] = !clicked[0];
ctx.button().setItem( clicked[0] ? ItemStack.of(Material.LIME_WOOL) : ItemStack.of(Material.RED_WOOL) ); }) .build() ); }}We use an array with the variable since variables used in lambdas must be effectively final.
Alternatively, you can manually construct a Button and use setters.