# A Data-Driven GUI Library for Minecraft Mapmaking

> **AVAILABLE ON 26.2**
>
> **Click [here](https://github.com/AjjMC/ajjgui/archive/refs/heads/main.zip) to download.**
>
> **Also available on [Modrinth](https://modrinth.com/datapack/ajjgui).**
>
> **For any queries, please use the issues section.**

<p align = "center">
  <img src="repo/gui.gif" width="350">
</p>

<p align = "center">
  <i>A GUI made entirely in-game in under 5 minutes.</i>
</p>

## Overview

This datapack allows mapmakers to create and maintain complex and robust item-based GUIs in-game. This is achieved by dragging and dropping items with custom SNBT in containers within a world, which the datapack can use to generate GUIs. It functions as a black box that treats each item as a graphical widget with predefined properties, and the user is not expected to modify any files. Two types of GUIs can be created:

* **Block Entity GUIs:** Placed at a fixed position and shared by all players. These are generated upon compilation.
* **Chest Boat GUIs:** Retrieved from a database and accessible anywhere by specified players only. These are obtained directly from the above by [porting GUIs to players](#porting-guis-to-players).

Benefits:

* SNBT standard for quick and easy creation of advanced item-based GUIs.
* Completely in-game workflow, with the entire datapack being a black box that the mapmaker can ignore.
* Robust design, support for multiplayer, including personalized GUIs, and no interference with player inventories.
* Complete documentation, in-game tutorial, and demos.
* 5+ years of continuous maintenance, built with the most up-to-date practices, and powering prominent maps officially featured on Minecraft Realms.

## Installing

After this datapack has been added to the "datapacks" folder of a Minecraft world, ``/reload`` needs to be run in-game. A list of the datapack's commands is available via ``/function ajjgui:__help``. By convention, all functions run directly by the mapmaker start with two underscores. Functions starting with a single underscore are aliases that do not give any feedback messages in the chat. These are meant to be used by the mapmaker as part of their own map's datapack. Any functions not listed here are internal and are not meant to be used.

| Function                                                       | Description                                           |
|:---------------------------------------------------------------|:------------------------------------------------------|
| ``/function ajjgui:__compile``                                 | Compiles GUI                                          |
| ``/function ajjgui:__decompile``                               | Decompiles nearest GUI                                |
| ``/function ajjgui:__help``                                    | Displays datapack command list                        |
| ``/function ajjgui:__install``                                 | Installs datapack                                     |
| ``/function ajjgui:__kit``                                     | Gives GUI design and compilation kit                  |
| ``/function ajjgui:__license``                                 | Displays datapack license information                 |
| ``/function ajjgui:__manual``                                  | Displays datapack manual link                         |
| ``/function ajjgui:__open {player:<player_uuid>,id:<gui_id>}`` | Opens ported GUI                                      |
| ``/function ajjgui:__openself {id:<gui_id>}``                  | Opens ported GUI of executing player                  |
| ``/function ajjgui:__port {player:<player_uuid>,id:<gui_id>}`` | Ports nearest GUI                                     |
| ``/function ajjgui:__portself {id:<gui_id>}``                  | Ports nearest GUI to executing player                 |
| ``/function ajjgui:__reload``                                  | Reloads GUIs                                          |
| ``/function ajjgui:__tutorial``                                | Displays GUI design and compilation tutorial          |
| ``/function ajjgui:__uninstall``                               | Uninstalls datapack                                   |
| ``/function ajjgui:__version``                                 | Displays datapack version                             |
| ``/function ajjgui:__widget/``                                 | Gives GUI demo widgets                                |
| ``/function ajjgui:_open {player:<player_uuid>,id:<gui_id>}``  | Runs ``/function ajjgui:__open`` without feedback     |
| ``/function ajjgui:_openself {id:<gui_id>}``                   | Runs ``/function ajjgui:__openself`` without feedback |
| ``/function ajjgui:_port {player:<player_uuid>,id:<gui_id>}``  | Runs ``/function ajjgui:__port`` without feedback     |
| ``/function ajjgui:_portself {id:<gui_id>}``                   | Runs ``/function ajjgui:__portself`` without feedback |
| ``/function ajjgui:_reload``                                   | Runs ``/function ajjgui:__reload`` without feedback   |

The datapack can be installed by running ``/function ajjgui:__install`` at any location in the world, which generates a shulker box. This needs to be located in a loaded chunk and cannot be destroyed. The shulker box can be relocated by repeating the installation command elsewhere. Any updated versions of the datapack are automatically installed at the same location upon reloading the world. The datapack can be uninstalled using ``/function ajjgui:__uninstall``, which removes all data associated with it from the world and decompiles any existing GUIs.

## Creating a GUI

The datapack adopts the concept of [graphical widgets](https://en.wikipedia.org/wiki/Graphical_widget), present in real-world user interfaces. Within the scope of Minecraft's item-based GUIs, and this datapack specifically, every item in a GUI corresponds to an interactive element (e.g., a button).

There are 8 types of GUI widgets available:

* [Placeholder](#placeholder)
* [Button](#button)
* [Counter](#counter)
* [Switch](#switch)
* [Radiobutton](#radiobutton)
* [Itembin](#itembin)
* [Itemslot](#itemslot)
* [Scrollbutton](#scrollbutton)

An in-game tutorial on how to create a GUI is available via ``/function ajjgui:__tutorial``. The tutorial provides the player with premade demo widgets to experiment with. Multiple examples are given, both here and in-game, to help provide a better understanding of their custom SNBT. The following section explains all the different types of widgets available and how they can be customized. Once obtained, these items can be placed inside shulker boxes, with each shulker box corresponding to a different GUI page. The shulker boxes can be arranged based on their page number and compiled to build a functional GUI in-game. This manual can be accessed with ``/function ajjgui:__manual``.

## List of GUI Widgets

> [!TIP]
> Some of the following commands are too long to fit in the chat box and need to be executed using a command block.

> [!IMPORTANT]
> For custom SNBT, it is important to check that the right data types are being used (e.g., ``{ajjgui:{exit:1b}}`` and not ``{ajjgui:{exit:1}}``), that values are within the specified range (e.g., ``{ajjgui:{exit:1b}}`` and not ``{ajjgui:{exit:2b}}``, where ``ajjgui.exit`` here can only be ``0b`` or ``1b``). The GUI compiler is only capable of initializing required SNBT with default values and does not correct errors. While there are cases where errors in custom SNBT, such as incorrect data types, may be internally resolved by the datapack at later stages, this behavior is inconsistent and must not be assumed.

> [!NOTE]
> The ``ajjgui.command``, ``ajjgui.exit``, ``ajjgui.fixed``, ``ajjgui.lock``, ``ajjgui.page``, and ``ajjgui.relative`` SNBT are covered separately in later sections.

### Placeholder

The *placeholder* is a widget that cannot be interacted with and is used to display an item.

| Item SNBT         | Default                             | Type           |
|:------------------|:------------------------------------|:---------------|
| ``ajjgui.fixed``  | ``0b``                              | Byte (Boolean) |
| ``ajjgui.widget`` | Required (``"placeholder"`` or N/A) | String         |

#### Usage

```
/give @p <item_id>[minecraft:custom_data={ajjgui:{widget:"placeholder",<optional_ajjgui_snbt>}}]
```

#### Example

A *placeholder*:

```
/give @p minecraft:black_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"placeholder"}},minecraft:tooltip_display={hide_tooltip:1b}]
```

> [!TIP]
> If a GUI slot is empty, it is set to a light gray stained glass pane *placeholder* with a hidden tooltip.

> [!TIP]
> If the ``ajjgui.widget`` SNBT of any item is not specified, it is set to ``"placeholder"`` by default. Therefore, the entire ``ajjgui`` argument for any *placeholder* is optional.

### Button

The *button* is a widget that changes the GUI page, exits the GUI and/or runs a GUI command when clicked. More information can be found in the following sections.

| Item SNBT           | Default                 | Type           |
|:--------------------|:------------------------|:---------------|
| ``ajjgui.command``  | N/A                     | String         |
| ``ajjgui.exit``     | ``0b``                  | Byte (Boolean) |
| ``ajjgui.fixed``    | ``0b``                  | Byte (Boolean) |
| ``ajjgui.lock``     | N/A                     | String         |
| ``ajjgui.page``     | N/A                     | Byte           |
| ``ajjgui.relative`` | ``0b``                  | Byte (Boolean) |
| ``ajjgui.widget``   | Required (``"button"``) | String         |

#### Usage

```
/give @p <item_id>[minecraft:custom_data={ajjgui:{widget:"button",<optional_ajjgui_snbt>}}]
```

#### Examples

More information about [changing GUI pages](#changing-gui-pages), [exiting GUIs](#exiting-guis), and [running GUI commands](#running-gui-commands-and-accessing-data).

### Counter

The *counter* is a widget that changes to a different count of the same item when clicked, following a value sequence. The value sequence is specified in the ``ajjgui.values`` SNBT. The default value is the one initially used upon creation of the widget. Once a *counter* in its default state is clicked, it changes to the second value in the list and so on. Hence, the first one is not used until the end of the first cycle. After one cycle, the first value is always used instead of the default one. The current state of a *counter* is stored in the ``ajjgui.state`` SNBT.

| Item SNBT           | Default                  | Type           |
|:--------------------|:-------------------------|:---------------|
| ``ajjgui.command``  | N/A                      | String         |
| ``ajjgui.exit``     | ``0b``                   | Byte (Boolean) |
| ``ajjgui.fixed``    | ``0b``                   | Byte (Boolean) |
| ``ajjgui.lock``     | N/A                      | String         |
| ``ajjgui.page``     | N/A                      | Byte           |
| ``ajjgui.relative`` | ``0b``                   | Byte (Boolean) |
| ``ajjgui.state``    | ``0``                    | Int            |
| ``ajjgui.values``   | Required                 | Int List       |
| ``ajjgui.widget``   | Required (``"counter"``) | String         |

#### Usage

```
/give @p <item_id>[minecraft:custom_data={ajjgui:{widget:"counter",values:[<value_1>,<value_2>,…,<value_N>],<optional_ajjgui_snbt>}}] <default_value>
```

where N is the number of states.

#### Examples

1. A *counter* repeating the sequence 1 to 16, starting with 1. The default value is the same as the first value in ``ajjgui.values``:

    ```
    /give @p minecraft:black_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"counter",values:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}},minecraft:item_name={text:"Counter Value"}] 1
    ```

2. A *counter* repeating the sequence 1 to 4, starting with 1. The default value is the same as the first value in ``ajjgui.values``:

    ```
    /give @p minecraft:black_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"counter",values:[1,2,3,4]}},minecraft:item_name={text:"Counter Value"}] 1
    ```

3. A *counter* beginning with a default value of 64, followed by the sequence 2 to 16, that continues by repeating the sequence 1 to 16:

    ```
    /give @p minecraft:black_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"counter",values:[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16]}},minecraft:item_name={text:"Counter Value"}] 64
    ```

4. A *counter* repeating the sequence 1 to 16, starting with 16. The default value is the same as the first value in ``ajjgui.values`` (now rearranged). The value of ``ajjgui.state`` is set to ``15`` to match the state:

    ```
    /give @p minecraft:black_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"counter",values:[16,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15],state:15}},minecraft:item_name={text:"Counter Value"}] 16
    ```

<p align = "center">
  <img src="repo/counter.gif" width="350">
</p>

<p align = "center">
  <i>Widget shown in Example 1.</i>
</p>

### Switch

The *switch* is a widget that changes to a different item when clicked, following an item sequence. The item sequence is specified in the ``ajjgui.items`` SNBT. The default item is the one initially used upon creation of the widget. Once a *switch* in its default state is clicked, it changes to the second item in the list and so on. Hence, the first one is not used until the end of the first cycle. After one cycle, the first item is always used instead of the default one. The current state of a *switch* is stored in the ``ajjgui.state`` SNBT.

| Item SNBT           | Default                 | Type           |
|:--------------------|:------------------------|:---------------|
| ``ajjgui.command``  | N/A                     | String         |
| ``ajjgui.exit``     | ``0b``                  | Byte (Boolean) |
| ``ajjgui.fixed``    | ``0b``                  | Byte (Boolean) |
| ``ajjgui.items``    | Required                | Compound List  |
| ``ajjgui.lock``     | N/A                     | String         |
| ``ajjgui.page``     | N/A                     | Byte           |
| ``ajjgui.relative`` | ``0b``                  | Byte (Boolean) |
| ``ajjgui.state``    | ``0``                   | Int            |
| ``ajjgui.widget``   | Required (``"switch"``) | String         |

#### Usage

```
/give @p <default_item_id>[minecraft:custom_data={ajjgui:{widget:"switch",items:[<item_1>,<item_2>,…,<item_N>],<optional_ajjgui_snbt>}}]
```

where N is the number of states.

#### Examples

1. A *switch* changing between a "State 0" and a "State 1" state, starting with "State 0". The default item is the same as the first item in ``ajjgui.items``:

    ```
    /give @p minecraft:gray_dye[minecraft:custom_data={ajjgui:{widget:"switch",items:[{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"State 0"}}},{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"State 1"}}}]}},minecraft:item_name={text:"State 0"}]
    ```

2. A *switch* changing between a "State 0", a "State 1", and a "State 2" state, starting with "State 0". The default item is the same as the first item in ``ajjgui.items``:

    ```
    /give @p minecraft:gray_dye[minecraft:custom_data={ajjgui:{widget:"switch",items:[{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"State 0"}}},{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"State 1"}}},{id:"minecraft:purple_dye",count:1,components:{"minecraft:item_name":{text:"State 2"}}}]}},minecraft:item_name={text:"State 0"}]
    ```

3. A *switch* beginning with a default state, "Default", that continues by changing between a "State 0" and a "State 1" state, starting with "State 1":

    ```
    /give @p minecraft:magenta_dye[minecraft:custom_data={ajjgui:{widget:"switch",items:[{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"State 0"}}},{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"State 1"}}}]}},minecraft:item_name={text:"Default"}]
    ```

4. A *switch* changing between a "State 0" and a "State 1" state, starting with "State 1". The default item is the same as the first item in ``ajjgui.items`` (now rearranged). The value of ``ajjgui.state`` is set to ``1`` to match the state:

    ```
    /give @p minecraft:lime_dye[minecraft:custom_data={ajjgui:{widget:"switch",items:[{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"State 1"}}},{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"State 0"}}},{id:"minecraft:purple_dye",count:1,components:{"minecraft:item_name":{text:"State 2"}}}],state:1}},minecraft:item_name={text:"State 1"}]
    ```

<p align = "center">
  <img src="repo/switch.gif" width="350">
</p>

<p align = "center">
  <i>Widget shown in Example 1.</i>
</p>

### Radiobutton

The *radiobutton* is a widget that changes between a disabled and an enabled state item when clicked. It comes in groups in which only one widget can be enabled at a time, with the rest being disabled. Each item is specified in the ``ajjgui.disabled`` and ``ajjgui.enabled`` SNBT. The default item is the one initially used upon creation of the widget. Once a *radiobutton* is clicked, it changes to the item corresponding to its enabled state, and all the other *radiobutton* widgets with the same group ID change to their disabled state. The group ID of a *radiobutton* is stored in the ``ajjgui.group`` SNBT. The current state of a *radiobutton* is stored in the ``ajjgui.state`` SNBT.

| Item SNBT           | Default                      | Type           |
|:--------------------|:-----------------------------|:---------------|
| ``ajjgui.command``  | N/A                          | String         |
| ``ajjgui.disabled`` | Required                     | Compound       |
| ``ajjgui.enabled``  | Required                     | Compound       |
| ``ajjgui.exit``     | ``0b``                       | Byte (Boolean) |
| ``ajjgui.fixed``    | ``0b``                       | Byte (Boolean) |
| ``ajjgui.group``    | ``0b``                       | Byte           |
| ``ajjgui.lock``     | N/A                          | String         |
| ``ajjgui.page``     | N/A                          | Byte           |
| ``ajjgui.relative`` | ``0b``                       | Byte (Boolean) |
| ``ajjgui.state``    | ``0``                        | Int            |
| ``ajjgui.widget``   | Required (``"radiobutton"``) | String         |

#### Usage

```
/give @p <default_item_id>[minecraft:custom_data={ajjgui:{widget:"radiobutton",off:<off_item>,on:<on_item>,<optional_ajjgui_snbt>}}]
```

#### Examples

1. A *radiobutton* on group 0 changing between a "Disabled" and an "Enabled" state, starting with "Disabled":

    ```
    /give @p minecraft:gray_dye[minecraft:custom_data={ajjgui:{widget:"radiobutton",disabled:{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"Disabled"}}},enabled:{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"Enabled"}}},group:0b}},minecraft:item_name={text:"Disabled"}]
    ```

2. A *radiobutton* on group 0 beginning with a default state, "Default", that continues by changing between a "Disabled" and an "Enabled" state, starting with "Enabled":

    ```
    /give @p minecraft:magenta_dye[minecraft:custom_data={ajjgui:{widget:"radiobutton",disabled:{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"Disabled"}}},enabled:{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"Enabled"}}},group:0b}},minecraft:item_name={text:"Default"}]
    ```

3. A *radiobutton* on group 0 changing between a "Disabled" and an "Enabled" state, starting with "Enabled". The value of ``ajjgui.state`` is set to ``1`` to match the state:

    ```
    /give @p minecraft:lime_dye[minecraft:custom_data={ajjgui:{widget:"radiobutton",disabled:{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"Disabled"}}},enabled:{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"Enabled"}}},group:0b,state:1}},minecraft:item_name={text:"Enabled"}]
    ```

<p align = "center">
  <img src="repo/radiobutton.gif" width="350">
</p>

<p align = "center">
  <i>Widget shown in Example 1.</i>
</p>

### Itembin

The *itembin* is a widget that clears all items inserted by the player in a particular slot in the GUI.

| Item SNBT           | Default                  | Type           |
|:--------------------|:-------------------------|:---------------|
| ``ajjgui.command``  | N/A                      | String         |
| ``ajjgui.exit``     | ``0b``                   | Byte (Boolean) |
| ``ajjgui.fixed``    | ``0b``                   | Byte (Boolean) |
| ``ajjgui.lock``     | N/A                      | String         |
| ``ajjgui.page``     | N/A                      | Byte           |
| ``ajjgui.relative`` | ``0b``                   | Byte (Boolean) |
| ``ajjgui.widget``   | Required (``"itembin"``) | String         |

#### Usage

```
/give @p <item_id>[minecraft:custom_data={ajjgui:{widget:"itembin",<optional_ajjgui_snbt>}}]
```

#### Example

An *itembin*:

```
/give @p minecraft:bucket[minecraft:custom_data={ajjgui:{widget:"itembin"}},minecraft:item_name={text:"Item Bin"}]
```

<p align = "center">
  <img src="repo/itembin.gif" width="350">
</p>

<p align = "center">
  <i>Widget shown in Example.</i>
</p>

> [!IMPORTANT]
> The *itembin* has a built-in cooldown of 0.4s.

### Itemslot

The *itemslot* is a widget that stores items inserted by the player in a particular slot in the GUI. Once one or more stacked items are inserted, the current ones occupying the slot (if any) are replaced and returned to the player's inventory. When the *itemslot* is not being used, a placeholder item occupies the slot. This is stored in the ``ajjgui.placeholder`` SNBT. The maximum number of items in an *itemslot* is stored in the ``ajjgui.size`` SNBT, which cannot be larger than ``99``. Any excess items are returned to the player. Whether an *itemslot* has an item in it is determined by the ``ajjgui.state`` SNBT.

| Item SNBT              | Default                   | Type           |
|:-----------------------|:--------------------------|:---------------|
| ``ajjgui.command``     | N/A                       | String         |
| ``ajjgui.exit``        | ``0b``                    | Byte (Boolean) |
| ``ajjgui.fixed``       | ``0b``                    | Byte (Boolean) |
| ``ajjgui.lock``        | N/A                       | String         |
| ``ajjgui.page``        | N/A                       | Byte           |
| ``ajjgui.placeholder`` | Required                  | Compound       |
| ``ajjgui.relative``    | ``0b``                    | Byte (Boolean) |
| ``ajjgui.size``        | ``99``                    | Int            |
| ``ajjgui.state``       | ``0``                     | Int            |
| ``ajjgui.widget``      | Required (``"itemslot"``) | String         |

#### Usage

```
/give @p (<default_placeholder_item_id>|<default_item_id>)[minecraft:custom_data={ajjgui:{widget:"itemslot",placeholder:<placeholder_item>,<optional_ajjgui_snbt>}}]
```

#### Examples

1. An empty *itemslot* with a default placeholder item identical to the one specified in ``ajjgui.placeholder`` and a stack size of ``99``:

    ```
    /give @p minecraft:gray_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"itemslot",placeholder:{id:"minecraft:gray_stained_glass_pane",count:1,components:{"minecraft:item_name":{text:"Empty"}}},size:99}},minecraft:item_name={text:"Empty"}]
    ```

2. An empty *itemslot* with a default placeholder item identical to the one specified in ``ajjgui.placeholder`` and a stack size of ``16``:

    ```
    /give @p minecraft:gray_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"itemslot",placeholder:{id:"minecraft:gray_stained_glass_pane",count:1,components:{"minecraft:item_name":{text:"Empty"}}},size:16}},minecraft:item_name={text:"Empty"}]
    ```

3. An empty *itemslot* with a default placeholder item different from the one specified in ``ajjgui.placeholder`` and a stack size of ``99``:

    ```
    /give @p minecraft:white_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"itemslot",placeholder:{id:"minecraft:gray_stained_glass_pane",count:1,components:{"minecraft:item_name":{text:"Empty"}}},size:99}},minecraft:item_name={text:"Default"}]
    ```

4. An *itemslot* with an item in it by default and a stack size of ``99``. The value of ``ajjgui.state`` is set to ``1`` as the slot is occupied:

    ```
    /give @p minecraft:diamond[minecraft:custom_data={ajjgui:{widget:"itemslot",placeholder:{id:"minecraft:gray_stained_glass_pane",count:1,components:{"minecraft:item_name":{text:"Empty"}}},size:99,state:1}}]
    ```

<p align = "center">
  <img src="repo/itemslot.gif" width="350">
</p>

<p align = "center">
  <i>Widget shown in Example 1.</i>
</p>

> [!IMPORTANT]
> The *itemslot* has a built-in cooldown of 0.4s.

### Scrollbutton

The *scrollbutton* is a widget that cycles one or more lists of *static* widgets (see notes) across respective GUI slot lists when clicked. This allows for additional space in the GUI. Each widget list is specified in the ``ajjgui.widgets`` SNBT. Within each widget list, widgets are added in the order they appear in. The slot list associated with each widget list is specified in the ``ajjgui.slots`` SNBT. Within each slot list, slots are added in the order they are occupied by the respective widget list. The *scrollbutton* also contains the entire functionality of the *switch*, including the ``ajjgui.items`` and ``ajjgui.state`` SNBT.

| Item SNBT           | Default                       | Type               |
|:--------------------|:------------------------------|:-------------------|
| ``ajjgui.command``  | N/A                           | String             |
| ``ajjgui.exit``     | ``0b``                        | Byte (Boolean)     |
| ``ajjgui.fixed``    | ``0b``                        | Byte (Boolean)     |
| ``ajjgui.items``    | N/A                           | Compound List      |
| ``ajjgui.lock``     | N/A                           | String             |
| ``ajjgui.page``     | N/A                           | Byte               |
| ``ajjgui.relative`` | ``0b``                        | Byte (Boolean)     |
| ``ajjgui.slots``    | Required                      | Byte List List     |
| ``ajjgui.state``    | ``0``                         | Int                |
| ``ajjgui.widget``   | Required (``"scrollbutton"``) | String             |
| ``ajjgui.widgets``  | Required                      | Compound List List |

#### Usage

```
/give @p <item_id>[minecraft:custom_data={ajjgui:{widget:"scrollbutton",widgets:[[<widget_1,1>,<widget_1,2>,…,<widget_1,L_1>],[<widget_2,1>,<widget_2,2>,…,<widget_2,L_2>],…,[<widget_N,1>,<widget_N,2>,…,<widget_N,L_N>]],slots:[[<slot_1,1>,<slot_1,2>,…,<slot_1,M_1>],[<slot_2,1>,<slot_2,2>,…,<slot_2,M_2>],…,[<slot_N,1>,<slot_N,2>,…,<slot_N,M_N>]],<optional_ajjgui_snbt>}}]
```

where L_x and M_y are the numbers of widgets and slots in each widget list and slot list respectively, and N is the number of widget list and slot list pairs.

#### Example

A *scrollbutton* cycling 6 buttons across GUI slots 11, 12, 13, and 14. Each button leads to a different page when clicked. There is a single widget list of length 6 and a single slot list of length 4:

```
/give @p minecraft:spectral_arrow[minecraft:custom_data={ajjgui:{widget:"scrollbutton",widgets:[[{id:"minecraft:paper",count:1,components:{"minecraft:custom_data":{ajjgui:{widget:"button",page:0b}},"minecraft:item_name":{text:"Select"}}},{id:"minecraft:paper",count:2,components:{"minecraft:custom_data":{ajjgui:{widget:"button",page:1b}},"minecraft:item_name":{text:"Select"}}},{id:"minecraft:paper",count:3,components:{"minecraft:custom_data":{ajjgui:{widget:"button",page:2b}},"minecraft:item_name":{text:"Select"}}},{id:"minecraft:paper",count:4,components:{"minecraft:custom_data":{ajjgui:{widget:"button",page:3b}},"minecraft:item_name":{text:"Select"}}},{id:"minecraft:paper",count:5,components:{"minecraft:custom_data":{ajjgui:{widget:"button",page:4b}},"minecraft:item_name":{text:"Select"}}},{id:"minecraft:paper",count:6,components:{"minecraft:custom_data":{ajjgui:{widget:"button",page:5b}},"minecraft:item_name":{text:"Select"}}}]],slots:[[11b,12b,13b,14b]]}},minecraft:item_name={text:"Next"}]
```

<p align = "center">
  <img src="repo/scrollbutton.gif" width="350">
</p>

<p align = "center">
  <i>Widget shown in Example.</i>
</p>

> [!IMPORTANT]
> The *scrollbutton* only supports the *placeholder*, *button*, and *itembin* widgets.

> [!TIP]
> If the ``ajjgui.widget`` SNBT of any widget is not specified, it is set to ``"placeholder"`` by default. Therefore, the entire ``ajjgui`` argument for any *placeholder* is optional. This, by extension, applies to a *placeholder* widget specified in the ``ajjgui.widgets`` SNBT of the *scrollbutton*.

## Changing GUI Pages

Each of the widgets discussed previously, excluding the *placeholder*, can be made to change the GUI page when clicked. This is done by specifying a page number in the ``ajjgui.page`` SNBT. By default, this value is the index of the shulker box in the chest previously used to compile the GUI, where a value of ``0b`` corresponds to the first page. If it is equal to the number of pages, the count resets back to the first page, and negative values may also be used to access pages from the end. The ``ajjgui.relative`` SNBT can be set to ``1b`` in order for the value of ``ajjgui.page`` to increment the page number from its current value. This, hence, assumes that the current page has an index of ``0b`` and uses this as a reference instead of the first one.

#### Examples

1. A *button* setting the GUI page to the first one:

    ```
    /give @p minecraft:arrow[minecraft:custom_data={ajjgui:{widget:"button",page:0b}},minecraft:item_name={text:"Go to First Page"}]
    ```

2. A *button* setting the GUI page to the last one:

    ```
    /give @p minecraft:arrow[minecraft:custom_data={ajjgui:{widget:"button",page:-1b}},minecraft:item_name={text:"Go to Last Page"}]
    ```

3. A *button* setting the GUI page to the next one:

    ```
    /give @p minecraft:arrow[minecraft:custom_data={ajjgui:{widget:"button",page:1b,relative:1b}},minecraft:item_name={text:"Go to Next Page"}]
    ```

4. A *button* setting the GUI page to the previous one:

    ```
    /give @p minecraft:arrow[minecraft:custom_data={ajjgui:{widget:"button",page:-1b,relative:1b}},minecraft:item_name={text:"Go to Previous Page"}]
    ```

## Fixed GUI Widgets

Each of the widgets discussed previously can be made to stay on display when the GUI page is changed. This is done by setting the ``ajjgui.fixed`` SNBT to ``1b``. If a *scrollbutton* is made fixed, its widgets also obtain this property.

#### Example

A *button* staying fixed in its slot when the GUI page is changed:

```
/give @p minecraft:arrow[minecraft:custom_data={ajjgui:{widget:"button",page:1b,relative:1b,fixed:1b}},minecraft:item_name={text:"Go to Next Page"}]
```

## Exiting GUIs

Each of the widgets discussed previously, excluding the *placeholder*, can be made to exit the GUI when clicked. This is done by setting the ``ajjgui.exit`` SNBT to ``1b``.

#### Example

A *button* exiting the GUI:

```
/give @p minecraft:barrier[minecraft:custom_data={ajjgui:{widget:"button",exit:1b}},minecraft:item_name={text:"Exit"},minecraft:rarity=common]
```

> [!IMPORTANT]
> When this functionality is used in block entity GUIs, it causes the block above to be removed.

## Running GUI Commands and Accessing Data

### Read-Only Data

| Scoreboard Score    | Description       | Type |
|:--------------------|:------------------|:-----|
| ``@s ajjgui.count`` | Widget item count | Int  |
| ``@s ajjgui.page``  | Page number       | Int  |
| ``@s ajjgui.slot``  | Widget slot       | Int  |
| ``@s ajjgui.state`` | Widget state      | Int  |

| Data Storage SNBT      | Description                           | Type          |
|:-----------------------|:--------------------------------------|:--------------|
| ``ajjgui:data in``     | *Itembin* or *itemslot* item inserted | Compound      |
| ``ajjgui:data out``    | *Itemslot* item removed               | Compound      |
| ``ajjgui:data page``   | Page                                  | Compound List |
| ``ajjgui:data widget`` | Widget                                | Compound      |

### Modifiable Data

| Scoreboard Score                                       | Description | Type |
|:-------------------------------------------------------|:------------|:-----|
| ``@e[tag=ajjgui.gui_active,limit=1] ajjgui.page``      | Page number | Int  |

| GUI Marker Entity SNBT                                 | Description        | Type                               |
|:-------------------------------------------------------|:-------------------|:-----------------------------------|
| ``@e[tag=ajjgui.gui_active,limit=1] data.custom_name`` | GUI container name | String, Compound, or Compound List |
| ``@e[tag=ajjgui.gui_active,limit=1] data.gui``         | GUI page list      | Compound List                      |

| Data Storage SNBT      | Description                          | Type                               |
|:-----------------------|:-------------------------------------|:-----------------------------------|
| ``ajjgui:data prompt`` | Actionbar prompt for all ported GUIs | String, Compound, or Compound List |

Each of the widgets discussed previously, excluding the the *placeholder*, can be made to run commands or functions when clicked. This is done by specifying a command in the ``ajjgui.command`` SNBT. This command is executed by the player interacting with the widget. Within the command's execution, it is possible to access data exported from the GUI interaction (e.g., whether a *switch* is toggled on) and use it to make decisions.

#### Examples

1. A *button* running a command referencing the player that pressed it:

    ```
    /give @p minecraft:pink_dye[minecraft:custom_data={ajjgui:{widget:"button",command:"say pressed button"}},minecraft:item_name={text:"Command Button"}]
    ```

2. A *switch* running a command based on its current state:

    ```
    /give @p minecraft:gray_dye[minecraft:custom_data={ajjgui:{widget:"switch",items:[{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"State 0"}}},{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"State 1"}}}],command:"function name:func"}},minecraft:item_name={text:"State 0"}]
    ```

    where the following commands are located in a function ``name:func`` within a map's datapack:

    ```
    execute if score @s ajjgui.state matches 0 run say set switch to State 0
    execute if score @s ajjgui.state matches 1 run say set switch to State 1
    ```

## Locked GUI Widgets

Each of the widgets discussed previously, excluding the *placeholder*, can be made to be partially unresponsive when clicked. This is done by setting the ``ajjgui.lock`` SNBT to a scoreboard tag associated with specific players. This blocks all functionality except for exiting GUIs or running GUI commands.

### Examples

1. A *button* that works only if clicked by a player with the ``name.tag`` scoreboard tag:

    ```
    /give @p minecraft:arrow[minecraft:custom_data={ajjgui:{widget:"button",page:1b,relative:1b,lock:"name.tag"}},minecraft:item_name={text:"Go to Next Page"}]
    ```

2. A *switch* that works only if clicked by a player with the ``name.tag`` scoreboard tag:

    ```
    /give @p minecraft:gray_dye[minecraft:custom_data={ajjgui:{widget:"switch",items:[{id:"minecraft:gray_dye",count:1,components:{"minecraft:item_name":{text:"State 0"}}},{id:"minecraft:lime_dye",count:1,components:{"minecraft:item_name":{text:"State 1"}}}],lock:"name.tag"}},minecraft:item_name={text:"State 0"}]
    ```

3. An *itembin* that works only if clicked by a player with the ``name.tag`` scoreboard tag:

    ```
    /give @p minecraft:bucket[minecraft:custom_data={ajjgui:{widget:"itembin",lock:"name.tag"}},minecraft:item_name={text:"Item Bin"}]
    ```

4. An *itemslot* that only works if clicked by a player with the ``name.tag`` scoreboard tag:

    ```
    /give @p minecraft:gray_stained_glass_pane[minecraft:custom_data={ajjgui:{widget:"itemslot",placeholder:{id:"minecraft:gray_stained_glass_pane",count:1,components:{"minecraft:item_name":{text:"Empty"}}},size:99,lock:"name.tag"}},minecraft:item_name={text:"Empty"}]
    ```

## Porting GUIs to Players

Compiled GUIs can create copies for specific players. ``/function ajjgui:__port {player:<player_uuid>,id:<gui_id>}`` ports the nearest GUI to a player, to be accessed using ``/function ajjgui:__open {player:<player_uuid>,id:<gui_id>}``. Both commands receive two macro arguments: a player UUID "player", as an Int array, and a GUI ID "id", different for each GUI of the same player. The UUID of the executing player is instead used with ``/function ajjgui:__portself {id:<gui_id>}`` and ``/function ajjgui:__openself {id:<gui_id>}``, which only require a GUI ID.

#### Example

1. A mapmaker compiles a GUI for a settings menu at coordinates ``10`` ``10`` ``10``. It is intended for each player in the map to have their own settings menu. The following command can be used to port instances of this GUI to all online players. A single underscore is used to hide command feedback:

    ```
    /execute positioned 10 10 10 as @a run function ajjgui:_portself {id:"settings"}
    ```

    Then, a player can open their own GUI with:

    ```
    /function ajjgui:_openself {id:"settings"}
    ```

2. By specifying a UUID, offline players can be targeted, and a player can be allowed to open someone else's GUI. For example, the UUID of the player "Ajj" is ``[I; -1547620582, -1960489320, -1638997249, 1765947055]``. The following command can be used to port the nearest GUI to Ajj. A single underscore is used to hide command feedback:

    ```
    /function ajjgui:_port {player:[I;-1547620582,-1960489320,-1638997249,1765947055],id:"settings"}
    ```

    Then, a player can open Ajj's GUI with:

    ```
    /function ajjgui:_open {player:[I;-1547620582,-1960489320,-1638997249,1765947055],id:"settings"}
    ```

> [!NOTE]
> There is a persistent actionbar prompt instructing the user to open their inventory to view the chest boat menu and dismount to cancel. This can be modified or removed using the ``ajjgui:data prompt`` SNBT.

> [!IMPORTANT]
> These chest boats have an interaction entity surrounding their hitbox and preventing other players from accessing them. If other players come close enough to bypass this, the chest boat is removed (and the user needs to reopen the GUI). These measures are essential in ensuring that GUIs can only be accessed by their specified player.

## Advanced: Modifying GUIs Post-Compilation

There is a marker entity with the scoreboard tag ``"ajjgui.gui_origin"`` for block entity GUIs, located at the container coordinates, and ``"ajjgui.gui_ported"`` for chest boat GUIs, riding the chest boat. GUIs used at a specific tick temporarily have the ``"ajjgui.gui_active"`` scoreboard tag on their marker. This marker stores the page value in its ``ajjgui.page`` score, the container name in its ``data.custom_name`` SNBT and the page list in its ``data.gui`` SNBT. Each element in this list corresponds to a page, storing widgets in the same format containers use to store items. If the available widgets do not already support a particular functionality, the page number and widget SNBT may be modified post-compilation to achieve desired results. This would, for example, be needed if one wanted to modify a GUI without prior user interaction (i.e., without triggering a widget with the ``ajjgui.page`` or ``ajjgui.command`` SNBT). If the modification command is not triggered by a player using a GUI (i.e., with the ``ajjgui.command`` SNBT), ``/function ajjgui:_reload`` must follow in the same tick for any changes to be reflected in the GUI.

#### Examples

1. A command setting the nearest block entity GUI's page to the first one:

    ```
    /scoreboard players set @n[type=minecraft:marker,tag=ajjgui.gui_origin] ajjgui.page 0
    ```

    Then, in the same tick (not required if the above command is run from a widget):

    ```
    /function ajjgui:_reload
    ```

2. A command setting the nearest block entity GUI's first page's first slot item's id to stone:

    ```
    /data modify entity @n[type=minecraft:marker,tag=ajjgui.gui_origin] data.gui[0][{Slot:0b}].id set value "minecraft:stone"
    ```

    Then, in the same tick (not required if the above command is run from a widget):

    ```
    /function ajjgui:_reload
    ```

3. A button running a command setting its GUI's first page's first slot item's id to stone:

    ```
    /give @p minecraft:pink_dye[minecraft:custom_data={ajjgui:{widget:"button",command:'data modify entity @e[type=minecraft:marker,tag=ajjgui.gui_active,limit=1] data.gui[0][{Slot:0b}].id set value "minecraft:stone"'}},minecraft:item_name={text:"Command Button"}]
    ```

    In this example, ``/function ajjgui:_reload`` is not required.

> [!IMPORTANT]
> If a GUI is not reloaded as specified above, the datapack assumes that a player is interacting indefinitely with it, causing other active GUIs to malfunction.

> [!IMPORTANT]
> The GUI compiler adds the ``ajjgui.meta`` SNBT to each widget. This must not be changed when modifying SNBT post-compilation.

> [!IMPORTANT]
> This section explains how widgets can be modified after compilation, when required SNBT have already been initialized with default values. Creating widget SNBT from scratch or changing the ``ajjgui.widget`` SNBT is therefore not recommended.

> [!IMPORTANT]
> Widgets with the ``ajjgui.fixed`` SNBT set to ``1b`` are passed on to a new page only when it is loaded by clicking on a widget that has the ``ajjgui.page`` SNBT. Changing a page post-compilation therefore does not properly handle fixed widgets.

> [!NOTE]
> Decompiling a GUI resets it to its state before compilation.

## License

Made by Ajj and published under the MIT license. Please share the repository link.
