From 99ffe082584770ce09368ec14b60bb257b3364f1 Mon Sep 17 00:00:00 2001 From: bluefireoly Date: Thu, 22 Oct 2020 19:22:39 +0200 Subject: [PATCH] Space compound update --- .../kspigot/inventory/InventoryGUIBuilder.kt | 68 +++++-- .../AbstractInventoryGUISpaceCompound.kt | 166 ++++++++++++++++++ .../elements/InventoryGUISpaceCompound.kt | 131 -------------- .../InventoryGUISpaceCompoundScrollButton.kt | 26 ++- 4 files changed, 235 insertions(+), 156 deletions(-) create mode 100644 src/main/kotlin/net/axay/kspigot/inventory/elements/AbstractInventoryGUISpaceCompound.kt delete mode 100644 src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompound.kt diff --git a/src/main/kotlin/net/axay/kspigot/inventory/InventoryGUIBuilder.kt b/src/main/kotlin/net/axay/kspigot/inventory/InventoryGUIBuilder.kt index d084e948..a49b7a13 100644 --- a/src/main/kotlin/net/axay/kspigot/inventory/InventoryGUIBuilder.kt +++ b/src/main/kotlin/net/axay/kspigot/inventory/InventoryGUIBuilder.kt @@ -166,13 +166,40 @@ class InventoryGUIPageBuilder( onClick: (clickEvent: InventoryGUIClickEvent, 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 createCompound( + fromSlot: SingleInventorySlot, + toSlot: SingleInventorySlot, + iconGenerator: (E) -> ItemStack, + onClick: (clickEvent: InventoryGUIClickEvent, element: E) -> Unit + ): InventoryGUIRectSpaceCompound { + 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 compoundSpace( slots: InventorySlotCompound, - compound: InventoryGUISpaceCompound + compound: AbstractInventoryGUISpaceCompound ) { compound.addSlots(slots) defineSlots( @@ -183,24 +210,45 @@ class InventoryGUIPageBuilder( /** * 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, icon: ItemStack, compound: InventoryGUISpaceCompound, - 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, icon: ItemStack, - compound: InventoryGUISpaceCompound, - scrollDistance: Int = compound.invType.dimensions.height - ) = defineSlots(slots, InventoryGUISpaceCompoundScrollButton(icon, compound, -scrollDistance.absoluteValue)) + compound: InventoryGUIRectSpaceCompound, + scrollTimes: Int = 1, + reverse: Boolean = false + ) = defineSlots( + slots, + InventoryGUISpaceCompoundScrollButton( + icon, + compound, + compound.compoundWidth, + scrollTimes, + reverse + ) + ) } \ No newline at end of file diff --git a/src/main/kotlin/net/axay/kspigot/inventory/elements/AbstractInventoryGUISpaceCompound.kt b/src/main/kotlin/net/axay/kspigot/inventory/elements/AbstractInventoryGUISpaceCompound.kt new file mode 100644 index 00000000..60bb01a9 --- /dev/null +++ b/src/main/kotlin/net/axay/kspigot/inventory/elements/AbstractInventoryGUISpaceCompound.kt @@ -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 internal constructor( + private val compound: AbstractInventoryGUISpaceCompound +) : InventoryGUIElement() { + + override fun getItemStack(slot: Int) = compound.getItemStack(slot) + + override fun onClickElement(clickEvent: InventoryGUIClickEvent) { + compound.onClickElement(clickEvent) + } + + override fun startUsing(gui: InventoryGUI<*>) = compound.registerGUI(gui) + + override fun stopUsing(gui: InventoryGUI<*>) = compound.unregisterGUI(gui) + +} + +class InventoryGUIRectSpaceCompound( + invType: InventoryType, + iconGenerator: (E) -> ItemStack, + onClick: (InventoryGUIClickEvent, E) -> Unit, + internal val compoundWidth: Int +) : AbstractInventoryGUISpaceCompound(invType, iconGenerator, onClick) { + + override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) = + (internalSlotsSize + newProgress <= contentSize + (compoundWidth - (contentSize % compoundWidth))) + +} + +class InventoryGUISpaceCompound( + invType: InventoryType, + iconGenerator: (E) -> ItemStack, + onClick: (InventoryGUIClickEvent, E) -> Unit +) : AbstractInventoryGUISpaceCompound(invType, iconGenerator, onClick) { + + override fun handleScrollEndReached(newProgress: Int, internalSlotsSize: Int, contentSize: Int) = false + +} + +abstract class AbstractInventoryGUISpaceCompound internal constructor( + val invType: InventoryType, + private val iconGenerator: (E) -> ItemStack, + private val onClick: (InventoryGUIClickEvent, E) -> Unit +) { + + private val content = ArrayList() + private var currentContent: List = emptyList() + + private val internalSlots: MutableList = ArrayList() + + private var scrollProgress: Int = 0 + + private var contentSort: () -> Unit = { } + + private val registeredGUIs = HashSet>() + + 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) { + val element = contentAtSlot(clickEvent.bukkitEvent.slot) ?: kotlin.run { + clickEvent.bukkitEvent.isCancelled = true + return + } + onClick.invoke(clickEvent, element) + } + + internal fun addSlots(slots: InventorySlotCompound) { + 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 > 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) { + + content += elements + contentSort.invoke() + + recalculateCurrentContent() + + updateOpenGUIs() + + } + +} \ No newline at end of file diff --git a/src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompound.kt b/src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompound.kt deleted file mode 100644 index 3a95296a..00000000 --- a/src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompound.kt +++ /dev/null @@ -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 internal constructor( - private val compound: InventoryGUISpaceCompound -) : InventoryGUIElement() { - - override fun getItemStack(slot: Int) = compound.getItemStack(slot) - - override fun onClickElement(clickEvent: InventoryGUIClickEvent) { - compound.onClickElement(clickEvent) - } - - override fun startUsing(gui: InventoryGUI<*>) = compound.registerGUI(gui) - - override fun stopUsing(gui: InventoryGUI<*>) = compound.unregisterGUI(gui) - -} - -class InventoryGUISpaceCompound( - val invType: InventoryType, - private val iconGenerator: (E) -> ItemStack, - private val onClick: (InventoryGUIClickEvent, E) -> Unit -) { - - private val content = ArrayList() - - private val realInternalSlots = ArrayList() - - private val currentInternalSlots: List - 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>() - - 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) { - val element = contentAtSlot(clickEvent.bukkitEvent.slot) ?: return - onClick.invoke(clickEvent, element) - } - - internal fun addSlots(slots: InventorySlotCompound) { - 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 > 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) { - content += elements - contentSort.invoke() - onChange() - } - -} \ No newline at end of file diff --git a/src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompoundScrollButton.kt b/src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompoundScrollButton.kt index 1ffa5848..94fe71dd 100644 --- a/src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompoundScrollButton.kt +++ b/src/main/kotlin/net/axay/kspigot/inventory/elements/InventoryGUISpaceCompoundScrollButton.kt @@ -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( - private val icon: ItemStack, - private val compound: InventoryGUISpaceCompound, - private val scrollDistance: Int = compound.invType.dimensions.height, + icon: ItemStack, + private val compound: AbstractInventoryGUISpaceCompound, + private val scrollDistance: Int, + private val scrollTimes: Int, private val reverse: Boolean = false -) : InventoryGUIElement() { - - override fun getItemStack(slot: Int) = icon - - override fun onClickElement(clickEvent: InventoryGUIClickEvent) { +) : InventoryGUIButton(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() } - } - -} \ No newline at end of file + } else if (scrollTimes == 1) + if (reverse) compound.scroll(-scrollDistance) else compound.scroll(scrollDistance) +}) \ No newline at end of file