From 4c30c4e73d63e792e8b0b0821b9cc350ee809d4e Mon Sep 17 00:00:00 2001 From: Luca Raddatz Date: Thu, 27 Feb 2025 17:11:20 +0100 Subject: [PATCH] Fixed multiple bug found during testing --- .../uicomponents/UIComponentExample.kt | 3 +-- bgw-gui/src/commonMain/kotlin/Json.kt | 18 +++++++++------- .../jsMain/kotlin/tools/aqua/bgw/Animator.kt | 10 +++++++++ .../elements/uicomponents/PasswordField.kt | 5 ++++- .../bgw/elements/uicomponents/TextArea.kt | 5 ++++- .../bgw/elements/uicomponents/TextField.kt | 5 ++++- .../aqua/bgw/animation/SequentialAnimation.kt | 2 +- .../aqua/bgw/application/JCEFApplication.kt | 4 ++-- .../tools/aqua/bgw/binding/KtorApplication.kt | 5 +++++ .../kotlin/tools/aqua/bgw/core/Frontend.kt | 5 ++++- .../kotlin/tools/aqua/bgw/core/Scene.kt | 21 +++++++++++++++++-- .../tools/aqua/bgw/mapper/AnimationMapper.kt | 3 ++- .../tools/aqua/bgw/mapper/ComponentMapper.kt | 2 +- 13 files changed, 68 insertions(+), 20 deletions(-) diff --git a/bgw-examples/bgw-docs-examples/src/main/kotlin/examples/components/uicomponents/UIComponentExample.kt b/bgw-examples/bgw-docs-examples/src/main/kotlin/examples/components/uicomponents/UIComponentExample.kt index 29e1588b2..cbc1a0ca9 100644 --- a/bgw-examples/bgw-docs-examples/src/main/kotlin/examples/components/uicomponents/UIComponentExample.kt +++ b/bgw-examples/bgw-docs-examples/src/main/kotlin/examples/components/uicomponents/UIComponentExample.kt @@ -22,7 +22,6 @@ import tools.aqua.bgw.core.Alignment import tools.aqua.bgw.core.BoardGameApplication import tools.aqua.bgw.core.Color import tools.aqua.bgw.core.MenuScene -import tools.aqua.bgw.style.BorderRadius import tools.aqua.bgw.util.Font import tools.aqua.bgw.visual.ColorVisual @@ -41,7 +40,7 @@ class UIComponentExample : BoardGameApplication("UIComponent Example", width = 8 text = "I am a Label.", alignment = Alignment.CENTER, isWrapText = true, - visual = ColorVisual.GREEN.apply { style.borderRadius = BorderRadius.XXL }) + visual = ColorVisual.GREEN) init { menuScene.addComponents(outputLabel) diff --git a/bgw-gui/src/commonMain/kotlin/Json.kt b/bgw-gui/src/commonMain/kotlin/Json.kt index f59f25352..2fe004d73 100644 --- a/bgw-gui/src/commonMain/kotlin/Json.kt +++ b/bgw-gui/src/commonMain/kotlin/Json.kt @@ -69,16 +69,16 @@ private val module = SerializersModule { subclass(DialogData::class) subclass(FileDialogData::class) // ANIMATIONS + subclass(DelayAnimationData::class) + subclass(DiceAnimationData::class) subclass(FadeAnimationData::class) + subclass(FlipAnimationData::class) subclass(MovementAnimationData::class) + subclass(ParallelAnimationData::class) + subclass(RandomizeAnimationData::class) subclass(RotationAnimationData::class) subclass(ScaleAnimationData::class) - subclass(FlipAnimationData::class) subclass(SequentialAnimationData::class) - subclass(ParallelAnimationData::class) - subclass(RandomizeAnimationData::class) - subclass(DiceAnimationData::class) - subclass(DelayAnimationData::class) } polymorphic(LayoutViewData::class) { subclass(PaneData::class) @@ -180,12 +180,16 @@ private val module = SerializersModule { } // ANIMATIONS polymorphic(AnimationData::class) { + subclass(DelayAnimationData::class) + subclass(DiceAnimationData::class) subclass(FadeAnimationData::class) + subclass(FlipAnimationData::class) subclass(MovementAnimationData::class) + subclass(ParallelAnimationData::class) + subclass(RandomizeAnimationData::class) subclass(RotationAnimationData::class) subclass(ScaleAnimationData::class) - subclass(FlipAnimationData::class) - subclass(DelayAnimationData::class) + subclass(SequentialAnimationData::class) } } diff --git a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/Animator.kt b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/Animator.kt index 468f6263d..8b4be2adc 100644 --- a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/Animator.kt +++ b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/Animator.kt @@ -111,6 +111,11 @@ internal class Animator { val animations = animation.animations animations.forEach { + if (it is DelayAnimationData || + it is SequentialAnimationData || + it is ParallelAnimationData || + it is SteppedComponentAnimationData) + return@forEach val component = it as? ComponentAnimationData ?: return val componentId = component.componentView?.id.toString() @@ -132,6 +137,11 @@ internal class Animator { val animations = animation.animations animations.forEach { + if (it is DelayAnimationData || + it is SequentialAnimationData || + it is ParallelAnimationData || + it is SteppedComponentAnimationData) + return@forEach val component = it as? ComponentAnimationData ?: return val componentId = component.componentView?.id.toString() diff --git a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/PasswordField.kt b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/PasswordField.kt index 2d95fbd46..3e5b23713 100644 --- a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/PasswordField.kt +++ b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/PasswordField.kt @@ -53,6 +53,8 @@ internal val PasswordField = val elementRef = useRef(null) + val (inputValue, setInputValue) = useState(props.data.text) + bgwPasswordField { id = props.data.id className = ClassName("passwordField") @@ -71,7 +73,7 @@ internal val PasswordField = placeholder = props.data.prompt type = InputType.password defaultValue = props.data.text - value = props.data.text + value = inputValue spellCheck = false css { fontBuilder(props.data) @@ -84,6 +86,7 @@ internal val PasswordField = } onChange = { val value = it.target.value + setInputValue(value) JCEFEventDispatcher.dispatchEvent( TextInputChangedEventData(value).apply { id = props.data.id }) } diff --git a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextArea.kt b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextArea.kt index a304253b8..f84cc8ecc 100644 --- a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextArea.kt +++ b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextArea.kt @@ -52,6 +52,8 @@ internal val TextArea = val elementRef = useRef(null) + val (inputValue, setInputValue) = useState(props.data.text) + bgwTextArea { id = props.data.id className = ClassName("textArea") @@ -68,7 +70,7 @@ internal val TextArea = textarea { placeholder = props.data.prompt defaultValue = props.data.text - value = props.data.text + value = inputValue spellCheck = false css { fontBuilder(props.data) @@ -82,6 +84,7 @@ internal val TextArea = } onChange = { val value = it.target.value + setInputValue(value) JCEFEventDispatcher.dispatchEvent( TextInputChangedEventData(value).apply { id = props.data.id }) } diff --git a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextField.kt b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextField.kt index 24dae0ec9..c78898c19 100644 --- a/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextField.kt +++ b/bgw-gui/src/jsMain/kotlin/tools/aqua/bgw/elements/uicomponents/TextField.kt @@ -52,6 +52,8 @@ internal val TextField = val elementRef = useRef(null) + val (inputValue, setInputValue) = useState(props.data.text) + bgwTextField { id = props.data.id className = ClassName("textField") @@ -68,7 +70,7 @@ internal val TextField = input { placeholder = props.data.prompt defaultValue = props.data.text - value = props.data.text + value = inputValue spellCheck = false css { inputBuilder(props.data) @@ -81,6 +83,7 @@ internal val TextField = } onChange = { val value = it.target.value + setInputValue(value) JCEFEventDispatcher.dispatchEvent( TextInputChangedEventData(value).apply { id = props.data.id }) } diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/animation/SequentialAnimation.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/animation/SequentialAnimation.kt index f17a6d1e1..779b43d2c 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/animation/SequentialAnimation.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/animation/SequentialAnimation.kt @@ -45,7 +45,7 @@ data class SequentialAnimation( * Creates a new [SequentialAnimation] Additional constructor that enables the use of varargs for * the animations. * - * @param animation The [Animation]s that this [ParallelAnimation] should contain. + * @param animation The [Animation]s that this [SequentialAnimation] should contain. * * @see Animation * @see ParallelAnimation diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt index c5987dd77..10fca8a80 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/application/JCEFApplication.kt @@ -68,7 +68,7 @@ import tools.aqua.bgw.util.Coordinate internal object Constants { val PORT = ServerSocket(0).use { it.localPort } - // val PORT = 5173 + val DEBUG = false } internal class JCEFApplication : Application { @@ -79,7 +79,7 @@ internal class JCEFApplication : Application { override fun start(onClose: () -> Unit, callback: (Any) -> Unit) { println("[BGW] Starting BGW Runtime (${Constants.PORT})") EventQueue.invokeLater { - frame = MainFrame(loadCallback = callback) + frame = MainFrame(loadCallback = callback, debugLogging = Constants.DEBUG) JCEFApplication::class .java .getResource("/icon.png") diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/binding/KtorApplication.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/binding/KtorApplication.kt index c1e7c56c0..2ed90b3cf 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/binding/KtorApplication.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/binding/KtorApplication.kt @@ -143,6 +143,11 @@ internal fun enqueueUpdate(data: AppData) { } } +internal fun forceUpdate() { + debounceJob?.cancel() + CoroutineScope(Dispatchers.IO).launch { debounceMutex.withLock { processLastUpdate() } } +} + private suspend fun processLastUpdate() { val lastUpdate = updateStack.lastOrNull() updateStack.clear() diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Frontend.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Frontend.kt index 6b2ad5d44..dab3e3926 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Frontend.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Frontend.kt @@ -23,15 +23,17 @@ import ActionProp import PropData import io.ktor.server.engine.* import io.ktor.server.netty.* +import java.lang.Runnable import java.util.* import jsonMapper -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.* import kotlinx.serialization.encodeToString import tools.aqua.bgw.animation.Animation import tools.aqua.bgw.application.Application import tools.aqua.bgw.application.Constants import tools.aqua.bgw.application.JCEFApplication import tools.aqua.bgw.binding.componentChannel +import tools.aqua.bgw.binding.forceUpdate import tools.aqua.bgw.binding.markDirty import tools.aqua.bgw.binding.module import tools.aqua.bgw.builder.SceneBuilder @@ -153,6 +155,7 @@ internal class Frontend { } internal fun sendAnimation(animation: Animation) { + forceUpdate() val animationData = AnimationMapper.map(animation) val json = jsonMapper.encodeToString(PropData(animationData)) runBlocking { componentChannel.sendToAllClients(json) } diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Scene.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Scene.kt index 7a05c33bc..2655901bf 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Scene.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/core/Scene.kt @@ -20,6 +20,8 @@ package tools.aqua.bgw.core import tools.aqua.bgw.animation.Animation +import tools.aqua.bgw.animation.ParallelAnimation +import tools.aqua.bgw.animation.SequentialAnimation import tools.aqua.bgw.components.ComponentView import tools.aqua.bgw.components.RootComponent import tools.aqua.bgw.event.KeyEvent @@ -169,15 +171,30 @@ sealed class Scene(width: Number, height: Number, background: } /** - * Plays given [Animation]. + * Plays given [Animation] non-blocking. This means that any subsequent updates may be executed + * before the [Animation] actually starts playing. * * @param animation [Animation] to play. */ fun playAnimation(animation: Animation) { - animations.add(animation) + addAnimationRecursively(animation) Frontend.sendAnimation(animation) } + private fun addAnimationRecursively(animation: Animation) { + when (animation) { + is SequentialAnimation -> { + animation.animations.forEach { addAnimationRecursively(it) } + animations.add(animation) + } + is ParallelAnimation -> { + animation.animations.forEach { addAnimationRecursively(it) } + animations.add(animation) + } + else -> animations.add(animation) + } + } + /** * Searches [node] recursively through the visual tree and logs path where the [node] appears as * first component and the [rootNode] as last. diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/AnimationMapper.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/AnimationMapper.kt index 014745494..6370389be 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/AnimationMapper.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/AnimationMapper.kt @@ -18,6 +18,7 @@ package tools.aqua.bgw.mapper import AnimationData +import RecursiveMapper import data.animation.* import tools.aqua.bgw.animation.* @@ -34,7 +35,7 @@ internal object AnimationMapper { ): ComponentAnimationData { return this.apply { id = componentAnimation.id - componentView = ComponentMapper.map(componentAnimation.componentView) + componentView = RecursiveMapper.map(componentAnimation.componentView) duration = componentAnimation.duration } } diff --git a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/ComponentMapper.kt b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/ComponentMapper.kt index 25de7bbb5..01e7bab55 100644 --- a/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/ComponentMapper.kt +++ b/bgw-gui/src/jvmMain/kotlin/tools/aqua/bgw/mapper/ComponentMapper.kt @@ -199,7 +199,7 @@ internal object ComponentMapper { } is ProgressBar -> (mapSpecific(componentView) as ProgressBarData).apply { - progress = componentView.progress + progress = componentView.progress.coerceIn(0.0, 1.0) barColor = "rgba(${componentView.barColor.red}, ${componentView.barColor.green}, ${componentView.barColor.blue}, ${componentView.barColor.alpha})" }