-
Notifications
You must be signed in to change notification settings - Fork 20
Menu DSL Pagination
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.
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.
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}")
}
}
}
}
}
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)
}
}
}
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
}
}