Skip to content

Commit feda0cc

Browse files
committed
feat: custom form builder & better plus & support latest allay-api
1 parent cdcd803 commit feda0cc

File tree

6 files changed

+144
-32
lines changed

6 files changed

+144
-32
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package vip.cdms.allaymc.kotlinx.form
2+
3+
import org.allaymc.api.form.Forms
4+
import org.allaymc.api.form.element.CustomFormElement
5+
import org.allaymc.api.form.element.Dropdown
6+
import org.allaymc.api.form.element.Input
7+
import org.allaymc.api.form.element.Label
8+
import org.allaymc.api.form.element.Slider
9+
import org.allaymc.api.form.element.StepSlider
10+
import org.allaymc.api.form.element.Toggle
11+
import org.allaymc.api.form.type.CustomForm
12+
import vip.cdms.allaymc.kotlinx.Player
13+
import kotlin.reflect.KProperty
14+
15+
open class CustomFormBuilder : FormBuilder<CustomForm, CustomFormBuilder.Response>(), FormImager {
16+
open var icon: FormImage? = null
17+
18+
class Element<T>(
19+
val converter: (String) -> T,
20+
val builder: () -> CustomFormElement,
21+
) {
22+
lateinit var response: String
23+
val value get() = converter(response)
24+
operator fun getValue(thisRef: Any?, property: KProperty<*>) = value
25+
}
26+
27+
open val elements = mutableListOf<Element<*>>()
28+
29+
fun label(text: String) =
30+
Element({ null }) { Label(text) }
31+
.also { elements += it }
32+
fun input(text: String, placeholder: String = "", default: String = "") =
33+
Element({ it }) { Input(text, placeholder, default) }
34+
.also { elements += it }
35+
fun toggle(text: String, default: Boolean = false) =
36+
Element({ it.toBoolean() }) { Toggle(text, default) }
37+
.also { elements += it }
38+
fun dropdown(text: String, options: List<String>, default: Int = 0) =
39+
Element({ it.toInt() }) { Dropdown(text, options, default) }
40+
.also { elements += it }
41+
fun slider(text: String, min: Float, max: Float, step: Int = 1, default: Float = min) =
42+
Element({ it.toFloat() }) { Slider(text, min, max, step, default) }
43+
.also { elements += it }
44+
fun stepSlider(text: String, steps: List<String>, default: Int = 0) =
45+
Element({ it.toInt() }) { StepSlider(text, steps, default) }
46+
.also { elements += it }
47+
48+
data class Response(val values: List<String?>) : FormBuilder.Response
49+
50+
override fun build(player: Player): CustomForm = Forms.custom()
51+
.title(title)
52+
.icon(icon?.convert())
53+
.apply {
54+
elements.forEach { element(it.builder()) }
55+
}
56+
.onResponse {
57+
elements.forEachIndexed { index, element ->
58+
element.response = it[index] ?: /*label only*/""
59+
}
60+
response(player, Response(it))
61+
}
62+
.onClose(Runnable { response(player, null) })
63+
}

core/src/main/kotlin/vip/cdms/allaymc/kotlinx/form/FormBuilder.kt

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@ import vip.cdms.allaymc.kotlinx.Player
66
abstract class FormBuilder<T : Form, R : FormBuilder.Response> {
77
interface Response
88

9-
var title: String = ""
10-
var echo: Player.(R?) -> Unit = {}
11-
var submit: Player.(R) -> Unit = {}
12-
var close: Player.() -> Unit = {}
9+
open var title: String = ""
10+
open var echo: Player.(R?) -> Unit = {}
11+
open var submit: Player.(R) -> Unit = {}
12+
open var close: Player.() -> Unit = {}
1313

14-
protected open fun Player.onResponse(response: R?) {}
15-
protected fun response(player: Player, response: R?) = with(player) {
14+
internal open fun response(player: Player, response: R?) = with(player) {
1615
if (response != null) submit(response) else close()
1716
echo(response)
18-
onResponse(response)
1917
}
2018

2119
abstract fun build(player: Player): T
@@ -26,11 +24,36 @@ infix fun Player.send(builder: FormBuilder<*, *>) = builder sendTo this
2624

2725
fun ModalFormBuilder(block: ModalFormBuilder.() -> Unit) = ModalFormBuilder().apply(block)
2826
fun SimpleFormBuilder(block: SimpleFormBuilder.() -> Unit) = SimpleFormBuilder().apply(block)
27+
fun CustomFormBuilder(block: CustomFormBuilder.() -> Unit) = CustomFormBuilder().apply(block)
28+
29+
operator fun SimpleFormBuilder.plus(other: SimpleFormBuilder.() -> Unit) = plus(SimpleFormBuilder(other))
30+
operator fun SimpleFormBuilder.plus(other: SimpleFormBuilder) = object : SimpleFormBuilder() {
31+
val origin = this@SimpleFormBuilder
32+
val originSize = origin.buttons.size
33+
override var title = other.title.ifBlank { origin.title }
34+
override var content = other.content.ifBlank { origin.content }
35+
override val buttons = (origin.buttons + other.buttons).toMutableList()
36+
override val callbacks = (origin.callbacks + other.callbacks).toMutableMap()
37+
override fun response(player: Player, response: Response?) {
38+
super.response(player, response)
39+
if (response == null || response.index < originSize)
40+
origin.response(player, response)
41+
if (response == null || response.index >= originSize)
42+
origin.response(player, response?.let { Response(it.index - originSize) })
43+
}
44+
}
2945

30-
operator fun SimpleFormBuilder.plus(block: SimpleFormBuilder.() -> Unit) = plus(SimpleFormBuilder(block))
31-
operator fun SimpleFormBuilder.plus(builder: SimpleFormBuilder) = SimpleFormBuilder outputBuilder@{
32-
this@outputBuilder.title = builder.title.ifBlank { this@SimpleFormBuilder.title }
33-
this@outputBuilder.content = builder.content.ifBlank { this@SimpleFormBuilder.content }
34-
this@outputBuilder.buttons += this@SimpleFormBuilder.buttons + builder.buttons
35-
this@outputBuilder.callbacks += this@SimpleFormBuilder.callbacks + builder.callbacks
46+
operator fun CustomFormBuilder.plus(other: CustomFormBuilder.() -> Unit) = plus(CustomFormBuilder(other))
47+
operator fun CustomFormBuilder.plus(other: CustomFormBuilder) = object : CustomFormBuilder() {
48+
val origin = this@CustomFormBuilder
49+
val originSize = origin.elements.size
50+
val otherSize = other.elements.size
51+
override var title = other.title.ifBlank { origin.title }
52+
override var icon = other.icon ?: origin.icon
53+
override val elements = (origin.elements + other.elements).toMutableList()
54+
override fun response(player: Player, response: Response?) {
55+
super.response(player, response)
56+
origin.response(player, response?.let { it.copy(values = it.values.dropLast(otherSize)) })
57+
other.response(player, response?.let { it.copy(values = it.values.drop(originSize)) })
58+
}
3659
}

core/src/main/kotlin/vip/cdms/allaymc/kotlinx/form/FormHolder.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,18 @@ infix fun Player.send(holder: FormHolder<*>) = holder sendTo this
88

99
fun ModalForm(block: ModalFormBuilder.(Player) -> Unit) = FormHolder { ModalFormBuilder { block(it) } }
1010
fun SimpleForm(block: SimpleFormBuilder.(Player) -> Unit) = FormHolder { SimpleFormBuilder { block(it) } }
11+
fun CustomForm(block: CustomFormBuilder.(Player) -> Unit) = FormHolder { CustomFormBuilder { block(it) } }
1112

13+
@JvmName("plusSimpleFormHolder")
1214
operator fun FormHolder<SimpleFormBuilder>.plus(other: FormHolder<SimpleFormBuilder>) =
1315
FormHolder { this.evaluate(it) + other.evaluate(it) }
16+
@JvmName("plusSimpleFormHolderBlock")
1417
operator fun FormHolder<SimpleFormBuilder>.plus(other: SimpleFormBuilder.(Player) -> Unit) =
1518
plus(SimpleForm(other))
19+
20+
@JvmName("plusCustomFormBuilder")
21+
operator fun FormHolder<CustomFormBuilder>.plus(other: FormHolder<CustomFormBuilder>) =
22+
FormHolder { this.evaluate(it) + other.evaluate(it) }
23+
@JvmName("plusCustomFormHolderBlock")
24+
operator fun FormHolder<CustomFormBuilder>.plus(other: CustomFormBuilder.(Player) -> Unit) =
25+
plus(CustomForm(other))
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package vip.cdms.allaymc.kotlinx.form
2+
3+
import org.allaymc.api.form.element.ImageData
4+
5+
// for better DSL :)
6+
interface FormImager {
7+
sealed interface Image {
8+
val data: String
9+
data class Path(override val data: String) : Image
10+
data class Url(override val data: String) : Image
11+
}
12+
13+
fun imagePathOf(path: String) = Image.Path(path)
14+
fun imageUrlOf(url: String) = Image.Url(url)
15+
16+
fun Image.convert() = ImageData(
17+
if (this is Image.Path)
18+
ImageData.ImageType.PATH
19+
else
20+
ImageData.ImageType.URL,
21+
data
22+
)
23+
}
24+
25+
typealias FormImage = FormImager.Image

core/src/main/kotlin/vip/cdms/allaymc/kotlinx/form/ModalFormBuilder.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import org.allaymc.api.form.Forms
44
import org.allaymc.api.form.type.ModalForm
55
import vip.cdms.allaymc.kotlinx.Player
66

7-
class ModalFormBuilder : FormBuilder<ModalForm, ModalFormBuilder.Response>(){
7+
open class ModalFormBuilder : FormBuilder<ModalForm, ModalFormBuilder.Response>(){
88
var content = ""
99

1010
data class Action(val text: String, val callbacks: MutableList<(Player.() -> Unit)?>)
@@ -38,5 +38,5 @@ class ModalFormBuilder : FormBuilder<ModalForm, ModalFormBuilder.Response>(){
3838
val response = Response(false)
3939
response(player, response)
4040
}
41-
.onClose { response(player, null) }
41+
.onClose(Runnable { response(player, null) })
4242
}

core/src/main/kotlin/vip/cdms/allaymc/kotlinx/form/SimpleFormBuilder.kt

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,21 @@ package vip.cdms.allaymc.kotlinx.form
22

33
import org.allaymc.api.form.Forms
44
import org.allaymc.api.form.element.Button
5-
import org.allaymc.api.form.element.ImageData
65
import org.allaymc.api.form.type.SimpleForm
76
import vip.cdms.allaymc.kotlinx.Player
87

9-
class SimpleFormBuilder : FormBuilder<SimpleForm, SimpleFormBuilder.Response>() {
10-
var content = ""
8+
open class SimpleFormBuilder : FormBuilder<SimpleForm, SimpleFormBuilder.Response>(), FormImager {
9+
open var content = ""
1110

12-
sealed interface Image {
13-
val data: String
14-
data class Path(override val data: String) : Image
15-
data class Url(override val data: String) : Image
16-
}
17-
data class Button(val text: String, val image: Image? = null)
18-
fun imagePathOf(path: String) = Image.Path(path)
19-
fun imageUrlOf(url: String) = Image.Url(url)
20-
fun Image.convert() = ImageData(if (this is Image.Path) ImageData.ImageType.PATH else ImageData.ImageType.URL, data)
11+
data class Button(val text: String, val image: FormImage? = null)
2112
fun Button.convert() = Button(text, image?.convert())
2213

23-
val buttons = mutableListOf<Button>()
24-
val callbacks = linkedMapOf<Button, MutableList<(Player.() -> Unit)?>>()
14+
open val buttons = mutableListOf<Button>()
15+
open val callbacks = mutableMapOf<Button, MutableList<(Player.() -> Unit)?>>()
2516

26-
fun buttonOf(text: String, image: Image? = null, callback: (Player.() -> Unit)? = null) =
17+
fun buttonOf(text: String, image: FormImage? = null, callback: (Player.() -> Unit)? = null) =
2718
Button(text, image).apply { callbacks.getOrPut(this) { mutableListOf() } += callback }
28-
fun button(text: String, image: Image? = null, callback: (Player.() -> Unit)? = null) =
19+
fun button(text: String, image: FormImage? = null, callback: (Player.() -> Unit)? = null) =
2920
buttonOf(text, image, callback).also { buttons += it }
3021

3122
@JvmInline
@@ -45,5 +36,5 @@ class SimpleFormBuilder : FormBuilder<SimpleForm, SimpleFormBuilder.Response>()
4536
button(button)
4637
}
4738
}
48-
.onClose { response(player, null) }
39+
.onClose(Runnable { response(player, null) })
4940
}

0 commit comments

Comments
 (0)