Skip to content

Commit 07bd6eb

Browse files
Expose getters for page, line, tab, content properties on Guis
Exposes getters for existing Gui properties like page and line. Also introduces additional non-configurable properties for things like pageCount and lineCount, as well as kotlin extension functions to retrieve them as providers. Fixed page- and line count change handler not being called.
1 parent ca83b9c commit 07bd6eb

File tree

17 files changed

+594
-31
lines changed

17 files changed

+594
-31
lines changed

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ junit-bom = { module = "org.junit:junit-bom", version = "5.11.4" }
1010
junit-jupiter = { module = "org.junit.jupiter:junit-jupiter" }
1111
junit-platformLauncher = { module = "org.junit.platform:junit-platform-launcher" }
1212
kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
13+
kotlin-test-junit = { group = "org.jetbrains.kotlin", name = "kotlin-test-junit", version.ref = "kotlin" }
1314
mockbukkit = { module = "org.mockbukkit.mockbukkit:mockbukkit-v1.21", version = "4.98.0"}
1415
logback-classic = { module = "ch.qos.logback:logback-classic", version = "1.5.13" }
1516
paper-api = { module = "io.papermc.paper:paper-api", version.ref = "paper" }

invui-kotlin/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ dependencies {
1717
api(project(":invui"))
1818
api(libs.kotlin.stdlib)
1919
api(libs.commons.provider)
20+
testImplementation(libs.kotlin.test.junit)
2021
}
2122

2223
publishing {

invui-kotlin/src/main/kotlin/xyz/xenondevs/invui/PropertyAdapter.kt

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
11
package xyz.xenondevs.invui
22

3+
import xyz.xenondevs.commons.provider.DeferredValue
34
import xyz.xenondevs.commons.provider.MutableProvider
45
import xyz.xenondevs.commons.provider.Provider
6+
import xyz.xenondevs.commons.provider.UnstableProviderApi
7+
import xyz.xenondevs.commons.provider.mutableProvider
58
import xyz.xenondevs.invui.state.MutableProperty
9+
import xyz.xenondevs.invui.state.Property
610
import java.util.function.Consumer
711

8-
internal class PropertyAdapter<T>(private val provider: Provider<T>) : MutableProperty<T> {
12+
internal class PropertyAdapter<T>(
13+
val provider: Provider<T>,
14+
val mutableView: MutableProvider<T>
15+
) : MutableProperty<T> {
16+
17+
constructor(provider: MutableProvider<T>) : this(provider, provider)
18+
19+
constructor(provider: Provider<T>) : this(provider, NonMutableMutableProvider(provider))
920

1021
override fun get(): T {
1122
return provider.get()
1223
}
1324

1425
override fun set(value: T) {
15-
if (provider is MutableProvider<T>) {
16-
provider.set(value)
17-
} else {
18-
throw UnsupportedOperationException("This property is backed by a non-mutable provider and cannot be written to.")
19-
}
26+
mutableView.set(value)
2027
}
2128

2229
override fun <O : Any> observeWeak(owner: O, observer: Consumer<in O>) {
@@ -31,4 +38,38 @@ internal class PropertyAdapter<T>(private val provider: Provider<T>) : MutablePr
3138
provider.unobserveWeak(owner)
3239
}
3340

41+
}
42+
43+
@OptIn(UnstableProviderApi::class)
44+
internal class NonMutableMutableProvider<T>(override val identifier: Provider<T>) : MutableProvider<T>, Provider<T> by identifier {
45+
override fun <R> strongMap(transform: (T) -> R, untransform: (R) -> T) = throwUoe()
46+
override fun <R> map(transform: (T) -> R, untransform: (R) -> T) = throwUoe()
47+
override fun <R> mapObserved(createObservable: (T, () -> Unit) -> R) = throwUoe()
48+
override fun <R> strongMapObserved(createObservable: (T, () -> Unit) -> R) = throwUoe()
49+
override fun consume(source: Provider<T>) = throwUoe()
50+
override fun update(value: DeferredValue<T>, ignore: Set<Provider<*>>) = throwUoe()
51+
private fun throwUoe(): Nothing =
52+
throw UnsupportedOperationException("This property was changed to a non-mutable provider")
53+
54+
override fun equals(other: Any?): Boolean = other is Provider<*> && other.identifier === identifier
55+
override fun hashCode(): Int = System.identityHashCode(identifier)
56+
}
57+
58+
internal fun <T> MutableProperty<T>.toProvider(): MutableProvider<T> {
59+
if (this is PropertyAdapter<T>)
60+
return mutableView
61+
62+
val child = mutableProvider { get() }
63+
observeWeak(child) { child -> child.set(get()) }
64+
child.subscribe { if (get() != it) set(it) }
65+
return child
66+
}
67+
68+
internal fun <T> Property<T>.toProvider(): Provider<T> {
69+
if (this is PropertyAdapter<T>)
70+
return provider
71+
72+
val child = mutableProvider { get() }
73+
observeWeak(child) { child -> child.set(get()) }
74+
return child
3475
}

invui-kotlin/src/main/kotlin/xyz/xenondevs/invui/dsl/property/ProviderDslProperty.kt

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
package xyz.xenondevs.invui.dsl.property
44

5-
import xyz.xenondevs.commons.provider.DeferredValue
65
import xyz.xenondevs.commons.provider.MutableProvider
76
import xyz.xenondevs.commons.provider.Provider
87
import xyz.xenondevs.commons.provider.UnstableProviderApi
98
import xyz.xenondevs.commons.provider.immediateFlatten
109
import xyz.xenondevs.commons.provider.mutableProvider
10+
import xyz.xenondevs.invui.NonMutableMutableProvider
1111
import xyz.xenondevs.invui.dsl.ExperimentalDslApi
1212

1313
@ExperimentalDslApi
@@ -35,21 +35,6 @@ open class ProviderDslProperty<T> internal constructor(
3535

3636
}
3737

38-
@ExperimentalDslApi
39-
private class NonMutableMutableProvider<T>(override val identifier: Provider<T>) : MutableProvider<T>, Provider<T> by identifier {
40-
override fun <R> strongMap(transform: (T) -> R, untransform: (R) -> T) = throwUoe()
41-
override fun <R> map(transform: (T) -> R, untransform: (R) -> T) = throwUoe()
42-
override fun <R> mapObserved(createObservable: (T, () -> Unit) -> R) = throwUoe()
43-
override fun <R> strongMapObserved(createObservable: (T, () -> Unit) -> R) = throwUoe()
44-
override fun consume(source: Provider<T>) = throwUoe()
45-
override fun update(value: DeferredValue<T>, ignore: Set<Provider<*>>) = throwUoe()
46-
private fun throwUoe(): Nothing =
47-
throw UnsupportedOperationException("This property was changed to a non-mutable provider")
48-
49-
override fun equals(other: Any?): Boolean = other is Provider<*> && other.identifier === identifier
50-
override fun hashCode(): Int = System.identityHashCode(identifier)
51-
}
52-
5338
@ExperimentalDslApi
5439
class MutableProviderDslProperty<T> internal constructor(
5540
private val source: MutableProvider<MutableProvider<T>>,

invui-kotlin/src/main/kotlin/xyz/xenondevs/invui/gui/PagedGuiExtensions.kt

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import xyz.xenondevs.commons.provider.MutableProvider
44
import xyz.xenondevs.commons.provider.Provider
55
import xyz.xenondevs.invui.ExperimentalReactiveApi
66
import xyz.xenondevs.invui.PropertyAdapter
7+
import xyz.xenondevs.invui.toProvider
8+
import java.lang.ref.WeakReference
79

810
/**
911
* Sets the provider containing the current page for the [PagedGui] built by this builder.
@@ -28,4 +30,37 @@ fun <C : Any, T> PagedGui.Builder<C>.setContent(provider: Provider<T>, transform
2830
*/
2931
@ExperimentalReactiveApi
3032
fun <C : Any> PagedGui.Builder<C>.setContent(content: Provider<List<C>>): PagedGui.Builder<C> =
31-
setContent(PropertyAdapter(content))
33+
setContent(PropertyAdapter(content))
34+
35+
/**
36+
* A provider containing the content of this [PagedGui].
37+
*
38+
* - If the content was defined through a [MutableProvider], the same instance is returned.
39+
* - If the content was defined through a [Provider], a non-mutable [MutableProvider]
40+
* that throws on mutation attempts is returned.
41+
* - Otherwise, each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
42+
* linked to the [PagedGui.contentProperty].
43+
*/
44+
val <C : Any> PagedGui<C>.contentProvider: MutableProvider<List<C>>
45+
get() = contentProperty.toProvider()
46+
47+
/**
48+
* A provider containing currently selected page of this [PagedGui].
49+
*
50+
* - If the page was defined through a [MutableProvider], the same instance is returned.
51+
* - If the page was defined through a [Provider], a non-mutable [MutableProvider]
52+
* that throws on mutation attempts is returned.
53+
* - Otherwise, each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
54+
* linked to the [PagedGui.pageProperty].
55+
*/
56+
val PagedGui<*>.pageProvider: MutableProvider<Int>
57+
get() = pageProperty.toProvider()
58+
59+
/**
60+
* A provider containing the page count of this [PagedGui].
61+
*
62+
* Each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
63+
* linked to the [PagedGui.getPageCountProperty].
64+
*/
65+
val PagedGui<*>.pageCountProvider: Provider<Int>
66+
get() = pageCountProperty.toProvider()

invui-kotlin/src/main/kotlin/xyz/xenondevs/invui/gui/ScrollGuiExtensions.kt

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import xyz.xenondevs.commons.provider.MutableProvider
44
import xyz.xenondevs.commons.provider.Provider
55
import xyz.xenondevs.invui.ExperimentalReactiveApi
66
import xyz.xenondevs.invui.PropertyAdapter
7+
import xyz.xenondevs.invui.toProvider
8+
import java.lang.ref.WeakReference
79

810
/**
911
* Sets the provider containing the current line for the [ScrollGui] built by this builder.
@@ -28,4 +30,46 @@ fun <C : Any, T> ScrollGui.Builder<C>.setContent(provider: Provider<T>, transfor
2830
*/
2931
@ExperimentalReactiveApi
3032
fun <C : Any> ScrollGui.Builder<C>.setContent(content: Provider<List<C>>): ScrollGui.Builder<C> =
31-
setContent(PropertyAdapter(content))
33+
setContent(PropertyAdapter(content))
34+
35+
/**
36+
* A provider containing the content of this [ScrollGui].
37+
*
38+
* - If the content was defined through a [MutableProvider], the same instance is returned.
39+
* - If the content was defined through a [Provider], a non-mutable [MutableProvider]
40+
* that throws on mutation attempts is returned.
41+
* - Otherwise, each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
42+
* linked to the [ScrollGui.contentProperty].
43+
*/
44+
val <C : Any> ScrollGui<C>.contentProvider: MutableProvider<List<C>>
45+
get() = contentProperty.toProvider()
46+
47+
/**
48+
* A provider containing currently selected line of this [ScrollGui].
49+
*
50+
* - If the line was defined through a [MutableProvider], the same instance is returned.
51+
* - If the line was defined through a [Provider], a non-mutable [MutableProvider]
52+
* that throws on mutation attempts is returned.
53+
* - Otherwise, each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
54+
* linked to the [ScrollGui.lineProperty].
55+
*/
56+
val ScrollGui<*>.lineProvider: MutableProvider<Int>
57+
get() = lineProperty.toProvider()
58+
59+
/**
60+
* A provider containing the line count of this [ScrollGui].
61+
*
62+
* Each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
63+
* linked to the [ScrollGui.getLineCountProperty].
64+
*/
65+
val ScrollGui<*>.lineCountProvider: Provider<Int>
66+
get() = lineCountProperty.toProvider()
67+
68+
/**
69+
* A provider containing the max line index of this [ScrollGui].
70+
*
71+
* Each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
72+
* linked to the [ScrollGui.getMaxLineProperty].
73+
*/
74+
val ScrollGui<*>.maxLineProvider: Provider<Int>
75+
get() = maxLineProperty.toProvider()

invui-kotlin/src/main/kotlin/xyz/xenondevs/invui/gui/TabGuiExtensions.kt

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@ package xyz.xenondevs.invui.gui
22

33
import xyz.xenondevs.commons.provider.MutableProvider
44
import xyz.xenondevs.commons.provider.Provider
5+
import xyz.xenondevs.commons.provider.getOrNull
56
import xyz.xenondevs.invui.ExperimentalReactiveApi
67
import xyz.xenondevs.invui.PropertyAdapter
8+
import xyz.xenondevs.invui.toProvider
9+
import java.lang.ref.WeakReference
710

811
/**
912
* Sets the provider containing the current tab for the [TabGui] built by this builder.
@@ -28,4 +31,37 @@ fun <T> TabGui.Builder.setTabs(provider: Provider<T>, transform: (T) -> List<Gui
2831
*/
2932
@ExperimentalReactiveApi
3033
fun TabGui.Builder.setTabs(tabs: Provider<List<Gui?>>): TabGui.Builder =
31-
setTabs(PropertyAdapter(tabs))
34+
setTabs(PropertyAdapter(tabs))
35+
36+
/**
37+
* A provider containing the tabs of this [TabGui].
38+
*
39+
* - If the tabs were defined through a [MutableProvider], the same instance is returned.
40+
* - If the tabs were defined through a [Provider], a non-mutable [MutableProvider]
41+
* that throws on mutation attempts is returned.
42+
* - Otherwise, each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
43+
* linked to the [TabGui.tabsProperty].
44+
*/
45+
val TabGui.tabsProvider: MutableProvider<List<Gui?>>
46+
get() = tabsProperty.toProvider()
47+
48+
/**
49+
* A provider containing the currently selected tab index of this [TabGui].
50+
*
51+
* - If the tab was defined through a [MutableProvider], the same instance is returned.
52+
* - If the tab was defined through a [Provider], a non-mutable [MutableProvider]
53+
* that throws on mutation attempts is returned.
54+
* - Otherwise, each invocation returns a new instance of a [MutableProvider] that is [weakly][WeakReference]
55+
* linked to the [TabGui.tabProperty].
56+
*/
57+
val TabGui.tabProvider: MutableProvider<Int>
58+
get() = tabProperty.toProvider()
59+
60+
/**
61+
* A provider containing the currently active tab [Gui] of this [TabGui],
62+
* or null if the current tab index is invalid.
63+
*
64+
* Equivalent to `tabsProvider.getOrNull(tabProvider)`.
65+
*/
66+
val TabGui.activeTabProvider: Provider<Gui?>
67+
get() = tabsProvider.getOrNull(tabProvider)

invui-kotlin/src/test/kotlin/PropertyAccessorsPresenceTest.kt renamed to invui-kotlin/src/test/kotlin/xyz/xenondevs/invui/PropertyAccessorsPresenceTest.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
package xyz.xenondevs.invui
2+
13
import org.junit.jupiter.api.Test
24
import xyz.xenondevs.invui.gui.PagedGui
35
import xyz.xenondevs.invui.gui.ScrollGui
@@ -62,6 +64,9 @@ class PropertyAccessorsPresenceTest {
6264
scrollGui.scrollHandlers
6365
scrollGui.scrollHandlers = emptyList()
6466

67+
scrollGui.lineCountChangeHandlers
68+
scrollGui.lineCountChangeHandlers = emptyList()
69+
6570
// -- TabGui --
6671
tabGui.contentListSlots
6772
tabGui.contentListSlots = emptyList()
@@ -121,6 +126,4 @@ class PropertyAccessorsPresenceTest {
121126
}
122127
}
123128

124-
private fun <T> emptySequencedSet() = LinkedHashSet<T>()
125-
126129
}

0 commit comments

Comments
 (0)