From ac911d5846c8c812fd3c7292a76d09c434e97864 Mon Sep 17 00:00:00 2001 From: MattMX Date: Thu, 4 Jul 2024 23:36:39 +0100 Subject: [PATCH] Experimental implementation for #31 Introduced a higher level wrapper for multi page screens with cramming options --- .../pagination/GuiCramMultiPageScreen.kt | 75 +++++++++++++++++++ .../screen/pagination/GuiMultiPageScreen.kt | 68 +++++++++++++++++ .../mattmx/ktgui/components/signal/Signal.kt | 6 +- .../main/kotlin/com/mattmx/ktgui/KotlinGui.kt | 2 +- .../ktgui/examples/SignalScoreboardExample.kt | 14 +++- 5 files changed, 160 insertions(+), 5 deletions(-) create mode 100644 api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiCramMultiPageScreen.kt create mode 100644 api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiMultiPageScreen.kt diff --git a/api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiCramMultiPageScreen.kt b/api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiCramMultiPageScreen.kt new file mode 100644 index 0000000..d21840f --- /dev/null +++ b/api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiCramMultiPageScreen.kt @@ -0,0 +1,75 @@ +package com.mattmx.ktgui.components.screen.pagination + +import com.mattmx.ktgui.components.button.GuiButton +import com.mattmx.ktgui.components.screen.GuiScreen +import com.mattmx.ktgui.dsl.button +import com.mattmx.ktgui.utils.not +import net.kyori.adventure.text.Component +import org.bukkit.Material + +class GuiCramMultiPageScreen( + title: Component, + rows: Int = 6 +) : GuiMultiPageScreen(title, rows) { + val extraReservedSlots = arrayListOf() + + infix fun reserve(slots: IntRange) = extraReservedSlots.addAll(slots) + infix fun reserve(slots: List) = extraReservedSlots.addAll(slots) + + operator fun GuiButton<*>.unaryPlus() = cramAdd(this) + operator fun Collection>.unaryPlus() = forEach { cramAdd(it) } + open fun cramAdd(child: GuiButton<*>) { + var lastPage = pages.lastOrNull() + + // If it's full then make a new one + if (lastPage == null || isFull(lastPage)) { + lastPage = GuiScreen(Component.empty(), rows).apply { pages.add(this) } + } + + val nextSlot = nextSlotToFill(lastPage) + if (nextSlot == null) { + GuiScreen(Component.empty(), rows).apply { pages.add(this) } + return cramAdd(child) + } + + child childOf lastPage slot nextSlot + } + + fun nextSlotToFill(sub: GuiScreen): Int? { + var nextSlot = sub.slotsUsed().max() + 1 + + while (nextSlot in reservedSlots() && nextSlot <= sub.totalSlots()) { + nextSlot++ + } + + if (nextSlot >= sub.totalSlots()) return null + + return nextSlot + } + + fun isFull(sub: GuiScreen) = sub.slotsUsed().size >= totalSlots() - reservedSlots().size + + fun reservedSlots() = this.slotsUsed() + extraReservedSlots + +} + +fun cramMultiPageScreen(title: Component, rows: Int = 6, block: GuiCramMultiPageScreen.() -> Unit) = + GuiCramMultiPageScreen(title, rows).apply(block) + +fun main() { + val gui = cramMultiPageScreen(!"Materials") { + reserve(last() - 8..last()) + + button(Material.SPECTRAL_ARROW) { + named(!"&aNext") + click.left { navigateNextPage() } + } slot last() + + button(Material.SPECTRAL_ARROW) { + named(!"&cLast") + click.left { navigatePreviousPage() } + } slot last() - 8 + + +Material.values().map { button(it) {} } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiMultiPageScreen.kt b/api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiMultiPageScreen.kt new file mode 100644 index 0000000..cf940d5 --- /dev/null +++ b/api/src/main/kotlin/com/mattmx/ktgui/components/screen/pagination/GuiMultiPageScreen.kt @@ -0,0 +1,68 @@ +package com.mattmx.ktgui.components.screen.pagination + +import com.mattmx.ktgui.components.screen.GuiScreen +import com.mattmx.ktgui.dsl.button +import com.mattmx.ktgui.event.EventCallback +import com.mattmx.ktgui.utils.not +import net.kyori.adventure.text.Component +import org.bukkit.Material +import java.util.* +import kotlin.math.max +import kotlin.math.min + +open class GuiMultiPageScreen( + title: Component, + rows: Int = 6 +) : GuiScreen(title, rows) { + var currentPage = 0 + set(value) { + field = value + refresh() + pageChange.invoke(value) + } + val pageChange = EventCallback() + val pages = Collections.synchronizedList(arrayListOf()) + + infix fun page(block: GuiScreen.() -> Unit) = page(null, block) + open fun page(index: Int? = null, block: GuiScreen.() -> Unit) = apply { + val sub = GuiScreen(Component.empty(), rows).apply(block) + if (index == null) { + pages.add(sub) + } else { + pages[index] = sub + } + } + + open fun navigatePreviousPage() { + currentPage = max(0, currentPage - 1) + } + + open fun navigateNextPage() { + currentPage = min(pages.size, currentPage + 1) + } +} + +fun multiPageGui(title: Component, rows: Int = 6, block: GuiMultiPageScreen.() -> Unit) = + GuiMultiPageScreen(title, rows).apply(block) + +fun main() { + val gui = multiPageGui(!"Test") { + button(Material.SPECTRAL_ARROW) { + named(!"&aLast") + click.left { navigatePreviousPage() } + } slot last() + + button(Material.SPECTRAL_ARROW) { + named(!"&aNext") + click.left { navigateNextPage() } + } slot last() + + page { + + } + + page { + + } + } +} \ No newline at end of file diff --git a/api/src/main/kotlin/com/mattmx/ktgui/components/signal/Signal.kt b/api/src/main/kotlin/com/mattmx/ktgui/components/signal/Signal.kt index dd5bbae..0226b2e 100644 --- a/api/src/main/kotlin/com/mattmx/ktgui/components/signal/Signal.kt +++ b/api/src/main/kotlin/com/mattmx/ktgui/components/signal/Signal.kt @@ -44,11 +44,11 @@ class Signal(initial: V, private val owner: SignalOwner) : ReadWriteProperty< * * @return [value] */ - operator fun invoke() = value.apply { - owner.addDependency(this@Signal) + operator fun SignalOwner.invoke() = value.apply { + addDependency(this@Signal) } - fun get() = invoke() + fun SignalOwner.get() = invoke() /** * Set the value of [value]. diff --git a/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt b/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt index 8be3e2d..ae059b7 100644 --- a/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt +++ b/plugin/src/main/kotlin/com/mattmx/ktgui/KotlinGui.kt @@ -45,7 +45,7 @@ class KotlinGui : JavaPlugin() { val animatedScoreboard = AnimatedScoreboardExample() val scoreboardExample = ScoreboardExample() - val signalScoreboardExample = SignalScoreboardExample() + val signalScoreboardExample = SignalScoreboardExample(this) val examples = hashMapOf( "animated-scoreboard" to { animatedScoreboard }, "scoreboard" to { scoreboardExample }, diff --git a/plugin/src/main/kotlin/com/mattmx/ktgui/examples/SignalScoreboardExample.kt b/plugin/src/main/kotlin/com/mattmx/ktgui/examples/SignalScoreboardExample.kt index b6095fb..907701a 100644 --- a/plugin/src/main/kotlin/com/mattmx/ktgui/examples/SignalScoreboardExample.kt +++ b/plugin/src/main/kotlin/com/mattmx/ktgui/examples/SignalScoreboardExample.kt @@ -1,5 +1,7 @@ package com.mattmx.ktgui.examples +import com.mattmx.ktgui.KotlinGui +import com.mattmx.ktgui.dsl.event import com.mattmx.ktgui.scoreboards.dynamicScoreboard import com.mattmx.ktgui.utils.not import com.mattmx.ktgui.utils.placeholders @@ -7,12 +9,22 @@ import com.mattmx.ktgui.utils.pretty import com.mattmx.ktgui.utils.seconds import net.kyori.adventure.text.Component import org.bukkit.entity.Player +import org.bukkit.event.player.PlayerInteractEvent import java.text.DateFormat import java.util.* import kotlin.math.max import kotlin.math.min -class SignalScoreboardExample : Example { +class SignalScoreboardExample( + plugin: KotlinGui +) : Example { +// var timesDisplayed by signal(0) + + init { + event(plugin) { +// timesDisplayed++ + } + } val board = dynamicScoreboard(!"D7068&lYour Server") {