| GitHub |
This library is a lightweight framework that can be used to create inventory menus. The largest consideration of the library is to provide an interface around Sponge’s Inventory API where developers can easily create inventory views without having to work through the complexity of the process alone. It is not intended that this is to be an all-encompassing library; rather the goal is to keep things lightweight, simple, and account for the majority of common use cases.
The Inventory Library was one of the initial libraries in TeslaLibs and was improved in PR #5 by Simon_Flash.
Element [link] is an individual component of a GUI that contains an item and click action. Creating an
Element can be done quite simply:
ItemStack item = ItemStack.of(ItemTypes.BLAZE_POWDER, 1); Consumer<Action.Click> action = action -> action.getPlayer().sendMessage(Text.of("Elements are awesome!")); Element element = Element.of(item, action);
Note that because
Elements are immutable, the item is stored as an
ItemStackSnapshot and modifying the
ItemStack will not have any effect on it’s state. An
Element with no item or action can be retrieved using
Element.EMPTY, which can be used to ‘reset’ a slot and is non-operational.
Before we can talk about
Views, we need to start with a way to define where an
Element is positioned in an inventory. To do this, we use the
Layout [link] class to design where
Elements go in the final view. Let’s take a look at a simple
Layout with only a couple elements.
Element stone = Element.of(ItemStack.of(ItemTypes.STONE, 1)); Element dirt = Element.of(ItemStack.of(ItemTypes.DIRT, 2)); Element grass = Element.of(ItemStack.of(ItemTypes.GRASS, 3)); Layout layout = Layout.builder() .set(stone, 0) .set(dirt, 1, 9) .set(grass, 2, 10, 18) .build();
Layout.Builder class also has a number of different utility methods for common design patterns. Many of these make use of the layout’s dimension (defaults to a 6 by 9 double chest), which can be changed using the
dimension method. Here’s a simple example using some of the available methods.
Layout layout = Layout.builder() .dimension(InventoryDimension.of(9, 3)) .border(stone) .center(grass) .fill(dirt) .build();
At long last, we’ve made it! The
View [link] class is the heart of the library and manages the inventory, delegates click events to the appropriate element, and handles a variety of other actions. Once created, a
View may be shared between players and any changes (such as through
View#update) will be displayed automatically. To create a
View, you’ll need an
PluginContainer, and a
InventoryArchetype archetype; PluginContainer container; Layout layout; View view = View.of(archetype, container).define(layout);
View.Builder class contains additional methods to further customize your view by setting properties for the inventory or an action to fire when the view closes - just be careful if you’re canceling the close event to ensure the player doesn’t get trapped!
View view = View.builder() .archetype(archetype) .property(InventoryTitle.of(Text.of("Title"))) .onClose(action -> action.getPlayer().sendMessage(Text.of("Goodbye!"))) .build(container);
At times, you’ll come across cases where you need to add a variable number of
Elements into a
View to be displayed. This can be pretty straightforward for small numbers, but it quickly becomes a hassle if you need more than one view to do so. To handle this, the
Page [link] class can be used to paginate a list of
Elements and handle all of the page controls for you.
Trying to do this in a customizable way isn’t easy, but the
Page class does it’s best by working off of a
Layout template using
Element placeholders. To create our template, we’ll need to use the static
Elements to define where we want our navigation icons to go.
Layout layout = Layout.builder() .set(Element.empty(), 45, 46, 52, 53) .set(Page.FIRST, 47) .set(Page.PREVIOUS, 48) .set(Page.CURRENT, 49) .set(Page.NEXT, 50) .set(Page.LAST, 51) .build(); Page page = Page.builder().layout(layout).build(container);
The key thing to note here is how we’re adding the static elements for the different page positions. The way this works is a bit of a mess, but all you need to know is that they’ll be replaced with an
Element displaying the page number that will have the appropriate click action when the page is defined. With the
Page build, we can
define it using a
List<Element> as follows.
List<Element> elements = IntStream.rangeClosed(1, 64) .mapToObj(i -> Element.of(ItemStack.of(ItemTypes.SLIME_BALL, i))) .collect(Collectors.toList()); page.define(elements);