Example

Below is an example of a Tubing GUI. It uses Freemarker templates and shows a GUI where you can manage a protected area. A staff member can delete an area, or teleport to the area.

First let's see what the normal XML would be for this.

Raw Tubing GUI XML
<TubingGui size="54" id="area-detail">
    <title class="gui-title">Protected area: testarea</title>

    <GuiItem
            id="protected-area-info"
            class="protected-area-info"
            slot="13"
            material="PAPER">
        <name class="item-name" color="&3">testarea</name>
        <Lore>
            <LoreLine>&bId: &61</LoreLine>
            <LoreLine>&bName: &6testarea</LoreLine>
            <LoreLine>&bWorld: &6world</LoreLine>
            <LoreLine>&bCorner point 1: &6X: -173 Y: 71 Z: 211</LoreLine>
            <LoreLine>&bCorner point 2: &6X: -192 Y: 71 Z: 192</LoreLine>
        </Lore>
    </GuiItem>

    <GuiItem slot="30"
             id="delete-30"
             class="protected-area-delete"
             material="RED_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/delete?areaId=1">
        <name class="item-name">Delete protected area</name>
        <Lore>
            <LoreLine>Click to delete this area</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="31"
             id="delete-31"
             class="protected-area-delete"
             material="RED_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/delete?areaId=1">
        <name class="item-name">Delete protected area</name>
        <Lore>
            <LoreLine>Click to delete this area</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="32"
             id="delete-32"
             class="protected-area-delete"
             material="RED_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/delete?areaId=1">
        <name class="item-name">Delete protected area</name>
        <Lore>
            <LoreLine>Click to delete this area</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="39"
             id="delete-39"
             class="protected-area-delete"
             material="RED_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/delete?areaId=1">
        <name class="item-name">Delete protected area</name>
        <Lore>
            <LoreLine>Click to delete this area</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="40"
             id="delete-40"
             class="protected-area-delete"
             material="RED_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/delete?areaId=1">
        <name class="item-name">Delete protected area</name>
        <Lore>
            <LoreLine>Click to delete this area</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="41"
             id="delete-41"
             class="protected-area-delete"
             material="RED_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/delete?areaId=1">
        <name class="item-name">Delete protected area</name>
        <Lore>
            <LoreLine>Click to delete this area</LoreLine>
        </Lore>
    </GuiItem>

    <GuiItem slot="34"
             id="teleport-34"
             class="protected-area-teleport"
             material="ORANGE_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/teleport?areaId=1">
        <name class="item-name">Teleport</name>
        <Lore>
            <LoreLine>Click to teleport to where this area was created</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="35"
             id="teleport-35"
             class="protected-area-teleport"
             material="ORANGE_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/teleport?areaId=1">
        <name class="item-name">Teleport</name>
        <Lore>
            <LoreLine>Click to teleport to where this area was created</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="43"
             id="teleport-43"
             class="protected-area-teleport"
             material="ORANGE_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/teleport?areaId=1">
        <name class="item-name">Teleport</name>
        <Lore>
            <LoreLine>Click to teleport to where this area was created</LoreLine>
        </Lore>
    </GuiItem>
    <GuiItem slot="44"
             id="teleport-44"
             class="protected-area-teleport"
             material="ORANGE_STAINED_GLASS_PANE"
             onLeftClick="protected-areas/teleport?areaId=1">
        <name class="item-name">Teleport</name>
        <Lore>
            <LoreLine>Click to teleport to where this area was created</LoreLine>
        </Lore>
    </GuiItem>

    <GuiItem slot="49"
             id="back-button"
             class="back-button"
             onLeftClick="$BACK"
             material="SPRUCE_DOOR">
        <name class="item-name">Back</name>
    </GuiItem>
</TubingGui>

Obviously the above XML would be rather difficult to create and has a lot of duplication in it. That's why we use Freemarker to help us out with creating this xml file.

The template file looks like this:

<TubingGui size="54" id="area-detail">
    <title class="gui-title">Protected area: ${area.name}</title>

    <GuiItem
        id="protected-area-info"
        class="protected-area-info"
        slot="13"
        material="PAPER">
        <name class="item-name" color="&3">${area.name}</name>
        <Lore>
            <LoreLine>&bId: &6${area.id}</LoreLine>
            <LoreLine>&bName: &6${area.name}</LoreLine>
            <LoreLine>&bWorld: &6${area.cornerPoint1.world.name}</LoreLine>
            <LoreLine>&bCorner point 1:  &6X: ${area.cornerPoint1.blockX} Y: ${area.cornerPoint1.blockY} Z: ${area.cornerPoint1.blockZ}</LoreLine>
            <LoreLine>&bCorner point 2:  &6X: ${area.cornerPoint2.blockX} Y: ${area.cornerPoint2.blockY} Z: ${area.cornerPoint2.blockZ}</LoreLine>
        </Lore>
    </GuiItem>

    <#list [30,31,32,39,40,41] as slot>
        <GuiItem slot="${slot}"
                 id="delete-${slot}"
                 class="protected-area-delete"
                 material="RED_STAINED_GLASS_PANE"
                 onLeftClick="protected-areas/delete?areaId=${area.id}">
            <name class="item-name">Delete protected area</name>
            <Lore>
                <LoreLine>Click to delete this area</LoreLine>
            </Lore>
        </GuiItem>
    </#list>

    <#list [34,35,43,44] as slot>
        <GuiItem slot="${slot}"
                 id="teleport-${slot}"
                 class="protected-area-teleport"
                 material="ORANGE_STAINED_GLASS_PANE"
                 onLeftClick="protected-areas/teleport?areaId=${area.id}">
            <name class="item-name">Teleport</name>
            <Lore>
                <LoreLine>Click to teleport to where this area was created</LoreLine>
            </Lore>
        </GuiItem>
    </#list>

    <GuiItem slot="49"
         id="back-button"
         class="back-button"
         onLeftClick="$BACK"
         material="SPRUCE_DOOR">
        <name class="item-name">Go back</name>
    </GuiItem>
</TubingGui>

So the above is definitely already a lot better. We went from 128 lines to 52 lines.

Removing duplication

Duplicated GUI items can be reduced by using Freemarker list method.

<#list [34,35,43,44] as slot>
        <GuiItem slot="${slot}"
                id="teleport-${slot}"
                class="protected-area-teleport"
                material="ORANGE_STAINED_GLASS_PANE"
                onLeftClick="protected-areas/teleport?areaId=${area.id}">
                  ...
        </GuiItem>
</#list>

By doing this we can set the same item on multiple locations. Its an easy way to fill multiple spots at one.

Using macros

The back button is something you probably are going to use in multiple GUIs. With Freemarker you can define macros. Small reusable pieces of code. I will do that for my back button. I create a second file called commons.ftl inside the resources/gui/commons directory

<#macro backButton backSlot=49>
    <GuiItem slot="${backSlot}"
             id="back-button"
             class="back-button"
             onLeftClick="$BACK"
             material="SPRUCE_DOOR">
        <name class="item-name">Go Back</name>
    </GuiItem>
</#macro>

Now this macro can be used anywhere you'd like. In my protecred area template I can now replace the backButton with this macro.

<#import "/gui/commons/commons.ftl" as commons/>
<TubingGui size="54" id="area-detail">
    <title class="gui-title">Protected area: ${area.name}</title>
    ...

    <@commons.backButton/>
</TubingGui>

Notice how I need to import the commons ftl file. This gives us the ability to use backButton on any GUI easily.

Protected area item

The protected area is another GUI item which could be useful to reuse. We can reuse it in a GUI that is giving an overview of all the protected areas.

<#macro areaitem slot area itemId="protected-area-info" onRightClick="$NOOP" onLeftClick="$NOOP" onMiddleClick="$NOOP" actions=[]>
    <GuiItem
        id="${itemId}"
        class="protected-area-info"
        slot="${slot}"
        onLeftClick="${onLeftClick}"
        onRightClick="${onRightClick}"
        onMiddleClick="${onMiddleClick}"
        material="PAPER">
        <name class="item-name" color="&3">${area.name}</name>
        <Lore>
            <LoreLine>&bId: &6${area.id}</LoreLine>
            <LoreLine>&bName: &6${area.name}</LoreLine>
            <LoreLine>&bWorld: &6${area.cornerPoint1.world.name}</LoreLine>
            <LoreLine>&bCorner point 1:  &6X: ${area.cornerPoint1.blockX} Y: ${area.cornerPoint1.blockY} Z: ${area.cornerPoint1.blockZ}</LoreLine>
            <LoreLine>&bCorner point 2:  &6X: ${area.cornerPoint2.blockX} Y: ${area.cornerPoint2.blockY} Z: ${area.cornerPoint2.blockZ}</LoreLine>
        </Lore>
    </GuiItem>
</#macro>

Now we can import this file as well.

<#import "protected-areas-commons.ftl" as areacommon/>
<TubingGui size="54" id="area-detail">
    <title class="gui-title">Protected area: ${area.name}</title>

    <@areacommon.areaitem slot=13 area=area/>
    ...
</TubingGui>

Finally

After our refactoring we have 2 reusable components. And our final template for the detail GUI is:

<#import "protected-areas-commons.ftl" as areacommon/>
<#import "/gui/commons/commons.ftl" as commons/>

<TubingGui size="54" id="area-detail">
    <title class="gui-title">Protected area: ${area.name}</title>

    <@areacommon.areaitem slot=13 area=area/>

    <#list [30,31,32,39,40,41] as slot>
        <GuiItem slot="${slot}"
                 id="delete-${slot}"
                 class="protected-area-delete"
                 material="RED_STAINED_GLASS_PANE"
                 onLeftClick="protected-areas/delete?areaId=${area.id}">
            <name class="item-name">Delete protected area</name>
            <Lore>
                <LoreLine>Click to delete this area</LoreLine>
            </Lore>
        </GuiItem>
    </#list>

    <#list [34,35,43,44] as slot>
        <GuiItem slot="${slot}"
                 id="teleport-${slot}"
                 class="protected-area-teleport"
                 material="ORANGE_STAINED_GLASS_PANE"
                 onLeftClick="protected-areas/teleport?areaId=${area.id}">
            <name class="item-name">Teleport</name>
            <Lore>
                <LoreLine>Click to teleport to where this area was created</LoreLine>
            </Lore>
        </GuiItem>
    </#list>

    <@commons.backButton/>
</TubingGui>

Last updated