Space compound update

This commit is contained in:
bluefireoly
2020-10-22 19:22:39 +02:00
parent 5cb7eb1636
commit 99ffe08258
4 changed files with 235 additions and 156 deletions

View File

@@ -166,13 +166,40 @@ class InventoryGUIPageBuilder<T : ForInventory>(
onClick: (clickEvent: InventoryGUIClickEvent<T>, element: E) -> Unit
) = InventoryGUISpaceCompound(type, iconGenerator, onClick)
/**
* Creates a new compound, holding data which can be displayed
* in any compound space.
* This compound is strictly a rectangle.
* The space is automatically defined.
*/
fun <E> createCompound(
fromSlot: SingleInventorySlot<out T>,
toSlot: SingleInventorySlot<out T>,
iconGenerator: (E) -> ItemStack,
onClick: (clickEvent: InventoryGUIClickEvent<T>, element: E) -> Unit
): InventoryGUIRectSpaceCompound<T, E> {
val rectSlotCompound = fromSlot rectTo toSlot
return InventoryGUIRectSpaceCompound(
type,
iconGenerator,
onClick,
(rectSlotCompound.endInclusive.slotInRow - rectSlotCompound.start.slotInRow) + 1
).apply {
addSlots(rectSlotCompound)
defineSlots(
rectSlotCompound,
InventoryGUISpaceCompoundElement(this)
)
}
}
/**
* Defines an area where the content of the given compound
* is displayed.
*/
fun <E> compoundSpace(
slots: InventorySlotCompound<T>,
compound: InventoryGUISpaceCompound<T, E>
compound: AbstractInventoryGUISpaceCompound<T, E>
) {
compound.addSlots(slots)
defineSlots(
@@ -183,24 +210,45 @@ class InventoryGUIPageBuilder<T : ForInventory>(
/**
* By pressing this button,
* the user scrolls forward in the compound.
* the user scrolls forwards or backwards in the compound.
*/
fun compoundScrollForwards(
fun compoundScroll(
slots: InventorySlotCompound<T>,
icon: ItemStack,
compound: InventoryGUISpaceCompound<T, *>,
scrollDistance: Int = compound.invType.dimensions.height
) = defineSlots(slots, InventoryGUISpaceCompoundScrollButton(icon, compound, scrollDistance.absoluteValue))
scrollDistance: Int = 1,
scrollTimes: Int = 1,
reverse: Boolean = false
) = defineSlots(
slots,
InventoryGUISpaceCompoundScrollButton(
icon,
compound,
scrollDistance.absoluteValue,
scrollTimes,
reverse
)
)
/**
* By pressing this button,
* the user scrolls backwards in the compound.
* the user scrolls forwards or backwards in the compound.
*/
fun compoundScrollBackwards(
fun compoundScroll(
slots: InventorySlotCompound<T>,
icon: ItemStack,
compound: InventoryGUISpaceCompound<T, *>,
scrollDistance: Int = compound.invType.dimensions.height
) = defineSlots(slots, InventoryGUISpaceCompoundScrollButton(icon, compound, -scrollDistance.absoluteValue))
compound: InventoryGUIRectSpaceCompound<T, *>,
scrollTimes: Int = 1,
reverse: Boolean = false
) = defineSlots(
slots,
InventoryGUISpaceCompoundScrollButton(
icon,
compound,
compound.compoundWidth,
scrollTimes,
reverse
)
)
}

View File

@@ -0,0 +1,166 @@
@file:Suppress("MemberVisibilityCanBePrivate")
package net.axay.kspigot.inventory.elements
import net.axay.kspigot.inventory.*
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
class InventoryGUISpaceCompoundElement<T : ForInventory, E> internal constructor(
private val compound: AbstractInventoryGUISpaceCompound<T, E>
) : InventoryGUIElement<T>() {
override fun getItemStack(slot: Int) = compound.getItemStack(slot)
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
compound.onClickElement(clickEvent)
}
override fun startUsing(gui: InventoryGUI<*>) = compound.registerGUI(gui)
override fun stopUsing(gui: InventoryGUI<*>) = compound.unregisterGUI(gui)
}
class InventoryGUIRectSpaceCompound<T : ForInventory, E>(
invType: InventoryType<T>,
iconGenerator: (E) -> ItemStack,
onClick: (InventoryGUIClickEvent<T>, E) -> Unit,
internal val compoundWidth: Int
) : AbstractInventoryGUISpaceCompound<T, E>(invType, iconGenerator, onClick) {
override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) =
(internalSlotsSize + newProgress <= contentSize + (compoundWidth - (contentSize % compoundWidth)))
}
class InventoryGUISpaceCompound<T : ForInventory, E>(
invType: InventoryType<T>,
iconGenerator: (E) -> ItemStack,
onClick: (InventoryGUIClickEvent<T>, E) -> Unit
) : AbstractInventoryGUISpaceCompound<T, E>(invType, iconGenerator, onClick) {
override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) = false
}
abstract class AbstractInventoryGUISpaceCompound<T : ForInventory, E> internal constructor(
val invType: InventoryType<T>,
private val iconGenerator: (E) -> ItemStack,
private val onClick: (InventoryGUIClickEvent<T>, E) -> Unit
) {
private val content = ArrayList<E>()
private var currentContent: List<E> = emptyList()
private val internalSlots: MutableList<Int> = ArrayList()
private var scrollProgress: Int = 0
private var contentSort: () -> Unit = { }
private val registeredGUIs = HashSet<InventoryGUI<*>>()
private fun contentAtSlot(slot: Int) = currentContent.getOrNull(internalSlots.indexOf(slot))
private fun recalculateCurrentContent() {
if (scrollProgress >= content.size)
throw IllegalStateException("The scrollProgress is greater than the content size.")
// avoid IndexOutOfBoundsException
var sliceUntil = internalSlots.size + scrollProgress
if (sliceUntil > content.lastIndex)
sliceUntil = content.size
currentContent = content.slice(scrollProgress until sliceUntil)
}
private fun updateOpenGUIs() {
registeredGUIs.forEach { it.reloadCurrentPage() }
}
internal fun scroll(distance: Int): Boolean {
val value = scrollProgress + distance
return if (value >= 0) {
// always scroll if the end of the content is not reached
val ifScroll = if (internalSlots.size + value <= content.size) true
// scroll further if the width of the compound is defined and the last line can be filled up
else handleScrollEndReached(value, internalSlots.size, content.size)
if (ifScroll) {
scrollProgress = value
recalculateCurrentContent()
updateOpenGUIs()
true
} else false
} else false
}
internal abstract fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int): Boolean
internal fun getItemStack(slot: Int): ItemStack {
return contentAtSlot(slot)?.let { return@let iconGenerator.invoke(it) }
?: ItemStack(Material.AIR)
}
internal fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
val element = contentAtSlot(clickEvent.bukkitEvent.slot) ?: kotlin.run {
clickEvent.bukkitEvent.isCancelled = true
return
}
onClick.invoke(clickEvent, element)
}
internal fun addSlots(slots: InventorySlotCompound<T>) {
slots.realSlotsWithInvType(invType).forEach {
if (!internalSlots.contains(it))
internalSlots.add(it)
}
internalSlots.sort()
}
internal fun registerGUI(gui: InventoryGUI<*>) {
registeredGUIs += gui
}
internal fun unregisterGUI(gui: InventoryGUI<*>) {
registeredGUIs -= gui
}
/**
* Defines the sort behaviour which gets applied to the content
* automatically.
*/
fun <R : Comparable<R>> sortContentBy(reverse: Boolean = false, selector: (E) -> R?) {
contentSort = {
if (!reverse) content.sortBy(selector) else content.sortByDescending(selector)
}
contentSort.invoke()
}
/**
* Adds a new element to the compound.
*/
fun addContent(element: E) {
addContent(listOf(element))
}
/**
* Adds new elements to the compound.
*/
fun addContent(elements: Collection<E>) {
content += elements
contentSort.invoke()
recalculateCurrentContent()
updateOpenGUIs()
}
}

View File

@@ -1,131 +0,0 @@
@file:Suppress("MemberVisibilityCanBePrivate", "unused")
package net.axay.kspigot.inventory.elements
import net.axay.kspigot.inventory.*
import org.bukkit.Material
import org.bukkit.inventory.ItemStack
class InventoryGUISpaceCompoundElement<T : ForInventory, E> internal constructor(
private val compound: InventoryGUISpaceCompound<T, E>
) : InventoryGUIElement<T>() {
override fun getItemStack(slot: Int) = compound.getItemStack(slot)
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
compound.onClickElement(clickEvent)
}
override fun startUsing(gui: InventoryGUI<*>) = compound.registerGUI(gui)
override fun stopUsing(gui: InventoryGUI<*>) = compound.unregisterGUI(gui)
}
class InventoryGUISpaceCompound<T : ForInventory, E>(
val invType: InventoryType<T>,
private val iconGenerator: (E) -> ItemStack,
private val onClick: (InventoryGUIClickEvent<T>, E) -> Unit
) {
private val content = ArrayList<E>()
private val realInternalSlots = ArrayList<Int>()
private val currentInternalSlots: List<Int>
get() {
val result = ArrayList(realInternalSlots)
var more = 1
while (content.size > result.size) {
result += realInternalSlots.mapTo(ArrayList()) { it + (more * invType.dimensions.slotAmount) }
more++
}
return result
}
internal var scrolledLines: Int = 0; private set
fun scroll(distance: Int): Boolean {
val value = scrolledLines + distance
return if (
value >= 0 &&
((value - 1) * invType.dimensions.width) < content.size
) {
scrolledLines = value
onChange()
true
} else false
}
private var contentSort: () -> Unit = { }
private val registeredGUIs = HashSet<InventoryGUI<*>>()
private fun translateSlot(slot: Int) = (scrolledLines * invType.dimensions.width) + slot
private fun contentAtSlot(slot: Int) = content.getOrNull(
realInternalSlots.indexOf(translateSlot(slot))
)
internal fun getItemStack(slot: Int): ItemStack {
return contentAtSlot(slot)?.let { return@let iconGenerator.invoke(it) }
?: ItemStack(Material.AIR)
}
internal fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
val element = contentAtSlot(clickEvent.bukkitEvent.slot) ?: return
onClick.invoke(clickEvent, element)
}
internal fun addSlots(slots: InventorySlotCompound<T>) {
slots.realSlotsWithInvType(invType).forEach {
if (!realInternalSlots.contains(it))
realInternalSlots.add(it)
}
realInternalSlots.sort()
}
internal fun onChange() {
registeredGUIs.forEach { it.reloadCurrentPage() }
}
internal fun registerGUI(gui: InventoryGUI<*>) {
registeredGUIs += gui
}
internal fun unregisterGUI(gui: InventoryGUI<*>) {
registeredGUIs -= gui
}
/**
* Defines the sort behaviour which gets applied to the content
* automatically.
*/
fun <R : Comparable<R>> sortContentBy(reverse: Boolean = false, selector: (E) -> R?) {
contentSort = {
if (!reverse) content.sortBy(selector) else content.sortByDescending(selector)
}
contentSort.invoke()
}
/**
* Adds a new element to the compound.
*/
fun addContent(element: E) {
addContent(listOf(element))
}
/**
* Adds new elements to the compound.
*/
fun addContent(elements: Collection<E>) {
content += elements
contentSort.invoke()
onChange()
}
}

View File

@@ -1,28 +1,24 @@
package net.axay.kspigot.inventory.elements
import net.axay.kspigot.inventory.ForInventory
import net.axay.kspigot.inventory.InventoryGUIClickEvent
import net.axay.kspigot.inventory.InventoryGUIElement
import net.axay.kspigot.runnables.task
import org.bukkit.inventory.ItemStack
class InventoryGUISpaceCompoundScrollButton<T : ForInventory>(
private val icon: ItemStack,
private val compound: InventoryGUISpaceCompound<T, *>,
private val scrollDistance: Int = compound.invType.dimensions.height,
icon: ItemStack,
private val compound: AbstractInventoryGUISpaceCompound<T, *>,
private val scrollDistance: Int,
private val scrollTimes: Int,
private val reverse: Boolean = false
) : InventoryGUIElement<T>() {
override fun getItemStack(slot: Int) = icon
override fun onClickElement(clickEvent: InventoryGUIClickEvent<T>) {
) : InventoryGUIButton<T>(icon, {
if (scrollTimes > 1) {
task(
period = 1,
howOften = scrollDistance.toLong()
howOften = scrollTimes.toLong()
) {
val ifScrolled = if (reverse) compound.scroll(-1) else compound.scroll(1)
val ifScrolled = if (reverse) compound.scroll(-scrollDistance) else compound.scroll(scrollDistance)
if (!ifScrolled) it.cancel()
}
}
}
} else if (scrollTimes == 1)
if (reverse) compound.scroll(-scrollDistance) else compound.scroll(scrollDistance)
})