commit 381b346c7a0a3d7b560e9bfa8452d5e0779258a7
parent 283f0266d9f4588d5242c719172e21cbef6eede8
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Sun, 4 Feb 2024 16:01:30 +0100
feat(scripting): text input component
- add visibility attribute
Diffstat:
5 files changed, 155 insertions(+), 73 deletions(-)
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/InterfaceManager.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/InterfaceManager.kt
@@ -12,6 +12,7 @@ import me.rhunk.snapenhance.common.scripting.ui.components.NodeType
import me.rhunk.snapenhance.common.scripting.ui.components.impl.ActionNode
import me.rhunk.snapenhance.common.scripting.ui.components.impl.ActionType
import me.rhunk.snapenhance.common.scripting.ui.components.impl.RowColumnNode
+import me.rhunk.snapenhance.common.scripting.ui.components.impl.TextInputNode
import me.rhunk.snapenhance.common.ui.createComposeAlertDialog
import org.mozilla.javascript.Function
import org.mozilla.javascript.annotations.JSFunction
@@ -74,6 +75,12 @@ class InterfaceBuilder {
attributes["items"] = items
attributes["callback"] = callback
}
+
+ fun textInput(placeholder: String, value: String, callback: (String) -> Unit) = TextInputNode().apply {
+ placeholder(placeholder)
+ value(value)
+ callback(callback)
+ }.also { nodes.add(it) }
}
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/ScriptInterface.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/ScriptInterface.kt
@@ -1,10 +1,12 @@
package me.rhunk.snapenhance.common.scripting.ui
+import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.*
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Slider
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -68,92 +70,117 @@ private fun DrawNode(node: Node) {
)
}
- when (node.type) {
- NodeType.ACTION -> {
- when ((node as ActionNode).actionType) {
- ActionType.LAUNCHED -> {
- LaunchedEffect(node.key) {
- runCallbackSafe {
- node.callback()
+ if (cachedAttributes["visibility"] != "gone") {
+ AnimatedVisibility(
+ visible = cachedAttributes["visibility"] != "invisible",
+ ) {
+ when (node.type) {
+ NodeType.ACTION -> {
+ when ((node as ActionNode).actionType) {
+ ActionType.LAUNCHED -> {
+ LaunchedEffect(node.key) {
+ runCallbackSafe {
+ node.callback()
+ }
+ }
+ }
+ ActionType.DISPOSE -> {
+ DisposableEffect(Unit) {
+ onDispose {
+ runCallbackSafe {
+ node.callback()
+ }
+ }
+ }
}
}
}
- ActionType.DISPOSE -> {
- DisposableEffect(Unit) {
- onDispose {
- runCallbackSafe {
- node.callback()
- }
+ NodeType.COLUMN -> {
+ Column(
+ verticalArrangement = arrangement as? Arrangement.Vertical ?: spacing?.let { Arrangement.spacedBy(it.dp) } ?: Arrangement.Top,
+ horizontalAlignment = alignment as? Alignment.Horizontal ?: Alignment.Start,
+ modifier = rowColumnModifier
+ ) {
+ node.children.forEach { child ->
+ DrawNode(child)
}
}
}
- }
- }
- NodeType.COLUMN -> {
- Column(
- verticalArrangement = arrangement as? Arrangement.Vertical ?: spacing?.let { Arrangement.spacedBy(it.dp) } ?: Arrangement.Top,
- horizontalAlignment = alignment as? Alignment.Horizontal ?: Alignment.Start,
- modifier = rowColumnModifier
- ) {
- node.children.forEach { child ->
- DrawNode(child)
+ NodeType.ROW -> {
+ Row(
+ horizontalArrangement = arrangement as? Arrangement.Horizontal ?: spacing?.let { Arrangement.spacedBy(it.dp) } ?: Arrangement.SpaceBetween,
+ verticalAlignment = alignment as? Alignment.Vertical ?: Alignment.CenterVertically,
+ modifier = rowColumnModifier
+ ) {
+ node.children.forEach { child ->
+ DrawNode(child)
+ }
+ }
}
- }
- }
- NodeType.ROW -> {
- Row(
- horizontalArrangement = arrangement as? Arrangement.Horizontal ?: spacing?.let { Arrangement.spacedBy(it.dp) } ?: Arrangement.SpaceBetween,
- verticalAlignment = alignment as? Alignment.Vertical ?: Alignment.CenterVertically,
- modifier = rowColumnModifier
- ) {
- node.children.forEach { child ->
- DrawNode(child)
+ NodeType.TEXT -> NodeLabel()
+ NodeType.SWITCH -> {
+ var switchState by remember {
+ mutableStateOf(cachedAttributes["state"] as Boolean)
+ }
+ Switch(
+ checked = switchState,
+ onCheckedChange = { state ->
+ runCallbackSafe {
+ switchState = state
+ node.setAttribute("state", state)
+ (cachedAttributes["callback"] as? (Boolean) -> Unit)?.let { it(state) }
+ }
+ }
+ )
}
- }
- }
- NodeType.TEXT -> NodeLabel()
- NodeType.SWITCH -> {
- var switchState by remember {
- mutableStateOf(cachedAttributes["state"] as Boolean)
- }
- Switch(
- checked = switchState,
- onCheckedChange = { state ->
- runCallbackSafe {
- switchState = state
- node.setAttribute("state", state)
- (cachedAttributes["callback"] as? (Boolean) -> Unit)?.let { it(state) }
+ NodeType.SLIDER -> {
+ var sliderValue by remember {
+ mutableFloatStateOf((cachedAttributes["value"] as Int).toFloat())
}
+ Slider(
+ value = sliderValue,
+ onValueChange = { value ->
+ runCallbackSafe {
+ sliderValue = value
+ node.setAttribute("value", value.toInt())
+ (cachedAttributes["callback"] as? (Int) -> Unit)?.let { it(value.toInt()) }
+ }
+ },
+ valueRange = (cachedAttributes["min"] as Int).toFloat()..(cachedAttributes["max"] as Int).toFloat(),
+ steps = cachedAttributes["step"] as Int,
+ )
}
- )
- }
- NodeType.SLIDER -> {
- var sliderValue by remember {
- mutableFloatStateOf((cachedAttributes["value"] as Int).toFloat())
- }
- Slider(
- value = sliderValue,
- onValueChange = { value ->
- runCallbackSafe {
- sliderValue = value
- node.setAttribute("value", value.toInt())
- (cachedAttributes["callback"] as? (Int) -> Unit)?.let { it(value.toInt()) }
+ NodeType.BUTTON -> {
+ OutlinedButton(onClick = {
+ runCallbackSafe {
+ (cachedAttributes["callback"] as? () -> Unit)?.let { it() }
+ }
+ }) {
+ NodeLabel()
}
- },
- valueRange = (cachedAttributes["min"] as Int).toFloat()..(cachedAttributes["max"] as Int).toFloat(),
- steps = cachedAttributes["step"] as Int,
- )
- }
- NodeType.BUTTON -> {
- OutlinedButton(onClick = {
- runCallbackSafe {
- (cachedAttributes["callback"] as? () -> Unit)?.let { it() }
}
- }) {
- NodeLabel()
+ NodeType.TEXT_INPUT -> {
+ var textInputValue by remember {
+ mutableStateOf(cachedAttributes["value"].toString())
+ }
+ TextField(
+ value = textInputValue,
+ readOnly = cachedAttributes["readonly"] as? Boolean ?: false,
+ singleLine = cachedAttributes["singleLine"] as? Boolean ?: true,
+ maxLines = cachedAttributes["maxLines"] as? Int ?: 1,
+ onValueChange = { value ->
+ runCallbackSafe {
+ textInputValue = value
+ node.setAttribute("value", value)
+ (cachedAttributes["callback"] as? (String) -> Unit)?.let { it(value) }
+ }
+ },
+ placeholder = { Text(cachedAttributes["placeholder"].toString()) }
+ )
+ }
+ else -> {}
}
}
- else -> {}
}
}
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/components/Node.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/components/Node.kt
@@ -1,5 +1,6 @@
package me.rhunk.snapenhance.common.scripting.ui.components
+@Suppress("MemberVisibilityCanBePrivate")
open class Node(
val type: NodeType,
) {
@@ -16,6 +17,10 @@ open class Node(
}
}
+ init {
+ visibility("visible")
+ }
+
fun setAttribute(key: String, value: Any?) {
attributes[key] = value
}
@@ -49,4 +54,9 @@ open class Node(
attributes["color"] = color
return this
}
+
+ fun visibility(state: String) {
+ assert(state == "visible" || state == "invisible" || state == "gone") { "Invalid visibility state. Must be one of: visible, invisible, gone" }
+ attributes["visibility"] = state
+ }
}
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/components/NodeType.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/components/NodeType.kt
@@ -8,5 +8,6 @@ enum class NodeType {
BUTTON,
SLIDER,
LIST,
- ACTION
+ ACTION,
+ TEXT_INPUT,
}
\ No newline at end of file
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/components/impl/TextInputNode.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/ui/components/impl/TextInputNode.kt
@@ -0,0 +1,36 @@
+package me.rhunk.snapenhance.common.scripting.ui.components.impl
+
+import me.rhunk.snapenhance.common.scripting.ui.components.Node
+import me.rhunk.snapenhance.common.scripting.ui.components.NodeType
+
+class TextInputNode : Node(NodeType.TEXT_INPUT) {
+ fun placeholder(text: String): TextInputNode {
+ attributes["placeholder"] = text
+ return this
+ }
+
+ fun value(text: String): TextInputNode {
+ attributes["value"] = text
+ return this
+ }
+
+ fun callback(callback: (String) -> Unit): TextInputNode {
+ attributes["callback"] = callback
+ return this
+ }
+
+ fun readonly(state: Boolean): TextInputNode {
+ attributes["readonly"] = state
+ return this
+ }
+
+ fun singleLine(state: Boolean): TextInputNode {
+ attributes["singleLine"] = state
+ return this
+ }
+
+ fun maxLines(maxLines: Int): TextInputNode {
+ attributes["maxLines"] = maxLines
+ return this
+ }
+}+
\ No newline at end of file