Skip to content

Related: APIๆฆ‚่งˆ.md ยท ๆ•ฐๆฎๅญ˜ๅ‚จไธŽๅ ไฝ็ฌฆ.md ยท ../02-ๆœไธปๆŒ‡ๅ—/่‡ชๅฎšไน‰GUI.md ยท ../05-ๅ‚่€ƒ/ๆœฏ่ฏญ่กจ.md

๐Ÿ–ผ๏ธ GUI Programming API (CustomGuiManager) โ€‹

This page covers programmatic GUI operations โ€” opening / rendering / feeding data using Kotlin code. For the GUI YAML fields (layout, icons, click actions, etc.), see the server-owner doc ../02-ๆœไธปๆŒ‡ๅ—/่‡ชๅฎšไน‰GUI.md; this page only covers the code side.

The core entry point is CustomGuiManager.


1. Initialization and config loading โ€‹

kotlin
CustomGuiManager.init(plugin)        // Usually called once in onEnable
CustomGuiManager.loadAllGuis()       // Load all GUI configs
CustomGuiManager.reloadAllGuis()     // Reload
CustomGuiManager.loadedGuiCount()    // Number already loaded
val config = CustomGuiManager.getGuiConfig("shop")   // Get a specific GUI config

2. Opening a GUI โ€‹

2.1 openGui โ€” open an already-loaded config GUI โ€‹

kotlin
CustomGuiManager.openGui(
    player,
    "shop",                           // GUI id
    placeholderProvider = null,       // Optional: placeholder provider for this open
    actionHandler = null              // Optional: action handler for this open
)

Both placeholderProvider and actionHandler are optional: pass them to use a dedicated one for this open, injecting different data / behavior into the same GUI across different scenarios.

2.2 openGuiFromConfig โ€” open from a given config section, with session data โ€‹

kotlin
CustomGuiManager.openGuiFromConfig(
    player,
    section,                          // Config section
    guiId = "shop",
    sessionData = mapOf("page" to 1), // Session data, readable during render
    placeholderProvider = null,
    actionHandler = null
)

2.3 openDynamic โ€” pure-code dynamic GUI โ€‹

Reads no config, drawn entirely by code:

kotlin
CustomGuiManager.openDynamic(
    player,
    rows = 3,                         // Number of rows
    title = "My Backpack",
    openSound = null,                 // Optional open sound
    closeSound = null,                // Optional close sound
    render = { gui, inventory ->
        // Fill items into gui / inventory here (see section 5, DynamicGui)
        gui.setItem(13, ItemStack(Material.DIAMOND)) { clickType ->
            player.sendMessage("Clicked the diamond: $clickType")
            false   // Return whether to refresh the interface
        }
    }
)

2.4 openDynamicFromConfig โ€‹

openDynamicFromConfig(...) โ€” takes the basic info (title / rows / sounds, etc.) from config, then draws content with code via render. It is a middle ground between the two above.


3. Registering custom placeholders โ€‹

3.1 CustomGuiManager.registerPlaceholder โ€‹

The simplest one: register a key โ†’ player-related string placeholder.

kotlin
CustomGuiManager.registerPlaceholder("coins") { player ->
    getCoins(player).toString()
}
// Write this key in the GUI text; it gets replaced at render time

3.2 PlaceholderManager โ€” layered placeholders โ€‹

PlaceholderManager provides registration at three granularities:

MethodScope
registerPlaceholder(...)Global placeholder
registerGuiPlaceholder(...)Placeholder within a specific GUI
registerItemPlaceholder(...)Placeholder for a specific item / slot
kotlin
PlaceholderManager.registerPlaceholder("server_name") { "Qinhuai Server" }
PlaceholderManager.registerGuiPlaceholder(/* guiId, key, resolver */)
PlaceholderManager.registerItemPlaceholder(/* ... */)

4. Wiring up a paginated data source (GuiDataProvider) โ€‹

When a GUI needs to display a long, page-turnable list of content (online players, products, leaderboards, etc.), implement GuiDataProvider. It has two sub-interfaces:

4.1 GuiPaginationListProvider โ€” paginated list โ€‹

kotlin
interface GuiPaginationListProvider : GuiDataProvider {
    fun loadEntries(player: Player, gui: /*Gui*/, sourceValue: String): List<GuiPaginationEntry>
}

loadEntries returns a list of GuiPaginationEntry; the framework handles paginated display.

GuiPaginationEntry fields:

FieldDescription
placeholders: MapThis entry's placeholders (filled into the icon's name / lore)
displayItem (nullable)Directly specify the display item
leftAction (nullable)Left-click action
rightAction (nullable)Right-click action
action (nullable)General action
kotlin
class FriendListProvider : GuiPaginationListProvider {
    override fun loadEntries(player: Player, gui: Gui, sourceValue: String): List<GuiPaginationEntry> {
        return getFriends(player).map { friend ->
            GuiPaginationEntry(
                placeholders = mapOf(
                    "name" to friend.name,
                    "status" to friend.statusText()
                ),
                leftAction  = "tp ${friend.name}",
                rightAction = "msg ${friend.name}"
            )
        }
    }
}

๐Ÿ”Œ Built-in data source online_players: QCL ships with an online-players data source. Just reference online_players directly in the GUI config to paginate the online players โ€” no need to implement it yourself.

4.2 GuiDynamicSlotProvider โ€” dynamic single slot โ€‹

kotlin
interface GuiDynamicSlotProvider : GuiDataProvider {
    fun loadItem(/* player, gui, ... */): ItemStack
}

Use this when a slot's item needs to change per player / real-time state (e.g. "current party icon").


5. DynamicGui โ€” drawing with code โ€‹

The gui given to you by openDynamic / openDynamicFromConfig is a DynamicGui. Use setItem to place an item into a slot and attach a click callback:

kotlin
gui.setItem(slot, itemStack) { clickType ->
    // Handle the click
    when (clickType) {
        ClickType.LEFT  -> doBuy(player)
        ClickType.RIGHT -> doInfo(player)
        else            -> {}
    }
    true   // Return Boolean: true = refresh the interface after the click, false = don't refresh
}
  • The third parameter is the click callback (clickType) -> Boolean.
  • The return value indicates whether to refresh: true redraws the interface (good for switching pages, changing state), false keeps it unchanged.

Full dynamic GUI example:

kotlin
CustomGuiManager.openDynamic(
    player,
    rows = 6,
    title = "Shop",
    render = { gui, inventory ->
        shopItems.forEachIndexed { i, item ->
            gui.setItem(i, item.icon) { click ->
                if (click == ClickType.LEFT) {
                    buy(player, item)
                    true     // Refresh after buying (update stock / balance display)
                } else false
            }
        }
    }
)

6. Summary of placeholderProvider / actionHandler injection โ€‹

When opening a GUI you can inject two kinds of callbacks, letting the same GUI config adapt to different scenarios:

  • placeholderProvider โ€” the placeholder source dedicated to this open (overrides / supplements the global placeholders).
  • actionHandler โ€” the action handler dedicated to this open (clicks go through it).
kotlin
CustomGuiManager.openGui(player, "profile",
    placeholderProvider = { key -> profileData[key] },
    actionHandler = myActionHandler
)

๐Ÿ“– Continue reading โ€‹