Inventory Library
| 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
An 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 Element
s 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.
Layout
Before we can talk about View
s, 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 Element
s 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();
The 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();
View
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 InventoryArchetype
, your PluginContainer
, and a Layout
.
InventoryArchetype archetype;
PluginContainer container;
Layout layout;
View view = View.of(archetype, container).define(layout);
The 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);
Page
At times, you’ll come across cases where you need to add a variable number of Element
s 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 Element
s 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 Element
s 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);