Skip to content

Menu DSL Pagination

Gabriel Souza edited this page Aug 12, 2020 · 5 revisions

KotlinBukkitAPI provides an extension to Menu DSL to work with menus that needs Pagination.

This page will cover how to use the Pagination system to it's full potential.

Basic

For starters, we will cover the basics used of the Pagination extension.

typealias ItemsAdapter<T> = MenuPlayer.(List<T>) -> List<T>

inline fun <T> MenuDSL.pagination(
        itemsProvider: ObservableCollection<T>, // a collection with items content
        nextPageSlot: SlotDSL, // a Slot that will be used to move to the next page
        previousPageSlot: SlotDSL, // a Slot that will be used to move to the previous page
        autoUpdateSwitchPageSlot: Boolean = true, // call `update` on nextPageSlot and previousPageSlot when move to other page.
        startLine: Int = 1, // the start line in the menu that will put the items
        endLine: Int = lines-1, // the end line for the pagination
        startSlot: Int = 1, // the start slot that will put the items
        endSlot: Int = 9, // the end slot for the pagination
        orientation: Orientation = Orientation.HORIZONTAL, // the items insersion orientation
        noinline itemsAdapterOnOpen: ItemsAdapter<T>? = null, // a apdater used when the pagination menu is open
        noinline itemsAdapterOnUpdate: ItemsAdapter<T>? = null, // a adapter used when the menu updates
        builder: MenuPaginationImpl<T>.() -> Unit // builder lambda
): MenuPaginationImpl<T>

We have a bunch of configuration available, you don't need to know all of it, we will cover here.

For demonstration, we will create a Menu that shows ALL Materials.

val myMenu = menu("Materials menu", 6, true) {

   val nextPage = slot(6, 9, item(Material.ARROW).displayName("Next page"))
   val previousPage = slot(6, 1, item(Material.ARROW).displayName("Previous page"))

   val pagination = pagination(
        itemsProvider = Material.values().toMutableList().asObservable(),
        nextPageSlot = nextPage,
        previousPageSlot = previousPage,
        startLine = 1,
        endLine = 5
   ) {
     slot {
        onRender { material -> // a nullable item from your collection
           // null if case that this slot is not being used
           // this provided you with the power of add a custom item for let your menu pretty
           showingItem = item(material ?: Material.STAINED_GLASS_PANE)
        }
        onClick { material -> // a nullable item from your collection
           if(material != null) {
               player.msg("You click at the material: ${material.name}")
           }
        }
     }
   }
}

The events are a little different here because they receive the element of the collection that is using at that stage.

inline fun <T> MenuPagination<T>.slot(
        builder: PaginationSlotDSL<T>.() -> Unit
)

interface PaginationSlotDSL<T> {
   fun onPageChange(pageChange: MenuPlayerSlotPageChangeEvent<T>)
   fun onClick(click: MenuPlayerPageSlotInteractEvent<T>)
   fun onRender(render: MenuPlayerPageSlotRenderEvent<T>)
   fun onUpdate(update: MenuPlayerPageSlotUpdateEvent<T>)
}

Here is the result Note: some Materials will get bug because there is not texture in the game.

RESULT

Adapters

With itemsAdapterOnOpen & itemsAdapterOnUpdate is possible to adapt the collection when a Player open the Menu or when the Pagination or Menu get updated, for everyone or for the player.

With the power of the Player Data that we already know how to use from the Menu DSL Advanced, we can put information in the menu and use it in the Adapter.

We will use Player Data to insert FILTERS into the Menu to be used in the pagination.

typealias ItemsAdapter<T> = MenuPlayer.(List<T>) -> List<T>

// before open the menu to player
const val KEY_MATERIAL_NAME_FILTER = "material-name"

myMenu.putPlayerData(player, KEY_MATERIAL_NAME_FILTER, playerCommandInputMaterial)

val myMenu = menu("Materials menu", 6, true) {

   val nextPage = slot(6, 9, item(Material.ARROW).displayName("Next page"))
   val previousPage = slot(6, 1, item(Material.ARROW).displayName("Previous page"))

   val pagination = pagination(
        itemsProvider = Material.values().toMutableList().asObservable(),
        nextPageSlot = nextPage,
        previousPageSlot = previousPage,
        startLine = 1,
        endLine = 5,
        itemsAdapterOnOpen = { items ->
            val materialName = getPlayerData(KEY_MATERIAL_NAME_FILTER) as? String?
            if(materialName != null) {
                items.filter { it.name.contains(materialName, ignoreCase = true) }
            } else {
                items
            }
        }
   ) {
     slot {
        onRender { material ->
           showingItem = item(material ?: Material.STAINED_GLASS_PANE)
        }
        onClick { material ->
           if(material != null) {
               player.msg("You click at the material: ${material.name}")
           }
        }
     }
   }
}

RESULT

itemsAdapterOnUpdate can easily be use as well by providing the update at a Button in the Menu.

val myMenu = menu("Materials menu", 6, true) {

   val nextPage = slot(6, 9, item(Material.ARROW).displayName("Next page"))
   val previousPage = slot(6, 1, item(Material.ARROW).displayName("Previous page"))

   val pagination = pagination(
        itemsProvider = Material.values().toMutableList().asObservable(),
        nextPageSlot = nextPage,
        previousPageSlot = previousPage,
        startLine = 1,
        endLine = 5,
        itemsAdapterOnOpen = { items ->
            val materialName = getPlayerData(KEY_MATERIAL_NAME_FILTER) as? String?
            if(materialName != null) {
                items.filter { it.name.contains(materialName, ignoreCase = true) }
            } else {
                items
            }
        },
        itemsAdapterOnUpdate = { items ->
            // here we do not have much to do because of the limitation of scope
            // from this example, but if is a shop, you can reload all Player specific selling items
            // and when some one buy it, when reload, it is removed.
            items
        }
   ) {
     slot {
        onRender { material ->
           showingItem = item(material ?: Material.STAINED_GLASS_PANE)
        }
        onClick { material ->
           if(material != null) {
               player.msg("You click at the material: ${material.name}")
           }
        }
     }
   }

   slot(6, 5, item(Material.PAPER).displayName("reload the list")) {
      onClick {
         pagination.updateItemsToPlayer(this)
      }
   }
}

autoUpdateSwitchPageSlot

It's used to auto call update in the nextPageSlot and previousPageSlot when a page is changed. This can help you with auto change the item when get in the last page or when is in the first one.

// before open the menu to player
const val KEY_MATERIAL_NAME_FILTER = "material-name"

myMenu.putPlayerData(player, KEY_MATERIAL_NAME_FILTER, playerCommandInputMaterial)

val myMenu = menu("Materials menu", 6, true) {

   val nextPage = slot(6, 9, item(Material.ARROW).displayName("Next page"))
   val previousPage = slot(6, 1, item(Material.ARROW).displayName("Previous page"))

   val pagination = pagination(
        itemsProvider = Material.values().toMutableList().asObservable(),
        nextPageSlot = nextPage,
        previousPageSlot = previousPage,
        autoUpdateSwitchPageSlot = true,
        startLine = 1,
        endLine = 5,
        itemsAdapterOnOpen = { items ->
            val materialName = getPlayerData(KEY_MATERIAL_NAME_FILTER) as? String?
            if(materialName != null) {
                items.filter { it.name.contains(materialName, ignoreCase = true) }
            } else {
                items
            }
        }
   ) {
     slot {
        onRender { material ->
           showingItem = item(material ?: Material.STAINED_GLASS_PANE)
        }
        onClick { material ->
           if(material != null) {
               player.msg("You click at the material: ${material.name}")
           }
        }
     }
   }

   nextPage.onUpdate {
      showingItem = if(pagination.hasNextPage()) item else null
   }
   previousPage.onUpdate {
     showingItem = if(pagination.hasPreviousPage()) item else null
   }
}

RESULT

Clone this wiki locally