From 8cb49bf55d789a7b372bf7a494f90360ec060f55 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 30 Mar 2026 00:19:04 +1000 Subject: [PATCH 01/43] ElementaVersion: v12 --- .../gg/essential/elementa/ElementaVersion.kt | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt index 7ddf7f1c..127b712c 100644 --- a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt +++ b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt @@ -7,6 +7,7 @@ import gg.essential.elementa.components.Window import gg.essential.elementa.constraints.SuperConstraint import gg.essential.elementa.constraints.animation.AnimationComponent import gg.essential.elementa.effects.Effect +import gg.essential.universal.UScreen import gg.essential.universal.render.URenderPipeline import gg.essential.universal.shader.BlendState @@ -204,8 +205,30 @@ enum class ElementaVersion { * * See [UniversalCraft#128](https://github.com/SparkUniverse/UniversalCraft/pull/128) for the underlying changes. */ + @Deprecated(DEPRECATION_MESSAGE) V11, + /** + * Improvements for key press inputs. + * + * WindowScreen now implements [UScreen.InputHandler] which provides new functions for key and mouse inputs that + * follow more modern Minecraft input method conventions, most notably allowing us to mark an input as handled to + * prevent followup execution in Minecraft itself. + * + * As of V12 the key & character input events have been split up and moved to these new methods. + * All occurrences of `keyType(typedChar: Char, keyCode: Int)` in [UIComponent]s have been split into: + * - keyType(keyEvent: UIKeyEvent) + * - charType(charEvent: UICharEvent) + * As well as all related setup functions. E.G. `onKeyType { }` + * For now any key listeners setup via the old methods will still be called. + * + * Also see [gg.essential.elementa.events.UIKeyEvent] & [gg.essential.elementa.events.UICharEvent], note the + * propagation stopping feature. + * + * See [UniversalCraft#125](https://github.com/SparkUniverse/UniversalCraft/pull/125) for the underlying changes. + */ + V12, + ; /** @@ -258,7 +281,9 @@ Be sure to read through all the changes between your current version and your ne internal val v9 = V9 @Suppress("DEPRECATION") internal val v10 = V10 + @Suppress("DEPRECATION") internal val v11 = V11 + internal val v12 = V12 internal val atLeastV9Active: Boolean get() = active >= v9 @@ -266,6 +291,8 @@ Be sure to read through all the changes between your current version and your ne get() = active >= v10 internal val atLeastV11Active: Boolean get() = active >= v11 + internal val atLeastV12Active: Boolean + get() = active >= v12 @PublishedApi internal var active: ElementaVersion = v0 From 7e60e5acfe1501d831bb2e4f5764cdf708cabac5 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 30 Mar 2026 00:19:16 +1000 Subject: [PATCH 02/43] Dependencies: Bump UC --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d900edda..b5e2ef15 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.9.25" kotlinx-coroutines = "1.5.2" jetbrains-annotations = "23.0.0" -universalcraft = "466" +universalcraft = "474+feature-em-1645-closing-uscreen-via-esc-on-119-puts-you-in-the-menu-rather" commonmark = "0.17.1" dom4j = "2.1.1" From ac698e9f079436de8bb0b9a2be2d82b88ef58a52 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 30 Mar 2026 00:19:55 +1000 Subject: [PATCH 03/43] UIKeyboardEvents: Create new key events --- .../gg/essential/elementa/events/UIEvent.kt | 2 ++ .../elementa/events/UIKeyboardEvents.kt | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt diff --git a/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt b/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt index 4bc942d5..1e5b1770 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt @@ -4,6 +4,8 @@ abstract class UIEvent { var propagationStopped = false var propagationStoppedImmediately = false + fun isStopped() = propagationStopped || propagationStoppedImmediately + /** * Stops this event from continuing to bubble to parent components. If there are * multiple listeners on the current component, those "sibling" listeners will continue to fire. diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt new file mode 100644 index 00000000..807f1ff9 --- /dev/null +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -0,0 +1,22 @@ +package gg.essential.elementa.events + +import gg.essential.universal.UKeyboard + +/** + * Represents a key typing event whose propagation can be stopped from continuing on to Minecraft + */ +class UIKeyEvent( + val keyCode: Int, + val scanCode: Int, + val modifiers: UKeyboard.Modifiers, +) : UIEvent() + + +/** + * Represents a character typing event whose propagation can be stopped from continuing on to Minecraft + */ +class UICharEvent( + val codepoint: Int, +) : UIEvent() { + val char = codepoint.toChar() +} \ No newline at end of file From def76ba977c4259239c4322dd72e65c28f9bc7f7 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 30 Mar 2026 00:20:40 +1000 Subject: [PATCH 04/43] WindowScreen & UIComponent: implement `UScreen.InputHandler` & split off keyType functions into key and character event functions --- .../gg/essential/elementa/UIComponent.kt | 45 +++++++++++ .../gg/essential/elementa/WindowScreen.kt | 80 ++++++++++++++++++- .../essential/elementa/components/Window.kt | 23 ++++++ 3 files changed, 145 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index 9f869630..3de324ae 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -13,7 +13,9 @@ import gg.essential.elementa.dsl.animate import gg.essential.elementa.dsl.toConstraint import gg.essential.elementa.effects.Effect import gg.essential.elementa.effects.ScissorEffect +import gg.essential.elementa.events.UICharEvent import gg.essential.elementa.events.UIClickEvent +import gg.essential.elementa.events.UIKeyEvent import gg.essential.elementa.events.UIScrollEvent import gg.essential.elementa.font.FontProvider import gg.essential.elementa.state.v2.ReferenceHolder @@ -119,7 +121,11 @@ abstract class UIComponent : Observable(), ReferenceHolder { get() = field.also { ownFlags += Flags.RequiresMouseMove } val mouseDragListeners = mutableListOf Unit>() get() = field.also { ownFlags += Flags.RequiresMouseDrag } + @Deprecated("[See ElementaVersion.V12]. These listeners will still function for the time being.", + replaceWith = ReplaceWith("keyTypedEventListeners; /*or*/ charTypedEventListeners")) val keyTypedListeners = mutableListOf Unit>() + val keyTypedEventListeners = mutableListOf Unit>() + val charTypedEventListeners = mutableListOf Unit>() private var currentlyHovered = false private val beforeHideAnimations = mutableListOf Unit>() @@ -836,11 +842,36 @@ abstract class UIComponent : Observable(), ReferenceHolder { this.forEachChild { it.superCall() } } + @Deprecated("[See ElementaVersion.V12]. This will still be called for backwards compatibility in the mean time.", + replaceWith = ReplaceWith("keyType(keyEvent) /*and*/ chartype(charEvent)")) open fun keyType(typedChar: Char, keyCode: Int) { + @Suppress("DEPRECATION") for (listener in keyTypedListeners) this.listener(typedChar, keyCode) } + // Called only when ElementaVersion >= V12 + open fun keyType(keyEvent: UIKeyEvent) { + for (listener in keyTypedEventListeners){ + this.listener(keyEvent) + if (keyEvent.propagationStoppedImmediately) return + } + + @Suppress("DEPRECATION") // Retaining support for the old listeners for now as it is trivial to do so + keyType(0.toChar(), keyEvent.keyCode) + } + + // Called only when ElementaVersion >= V12 + open fun charType(charEvent: UICharEvent) { + for (listener in charTypedEventListeners){ + this.listener(charEvent) + if (charEvent.propagationStoppedImmediately) return + } + + @Suppress("DEPRECATION") // Retaining support for the old listeners for now as it is trivial to do so + keyType(charEvent.char, 0) + } + @Deprecated("See [ElementaVersion.V8].") open fun animationFrame() { if (versionOrV0 >= ElementaVersion.v8) { @@ -1021,11 +1052,25 @@ abstract class UIComponent : Observable(), ReferenceHolder { mouseScrollListeners.add { method.accept(it) } } + @Deprecated("[See ElementaVersion.V12]. These listeners will still function for the time being.", + replaceWith = ReplaceWith("onKeyType(method) /*or*/ onCharType(method)")) fun onKeyType(method: UIComponent.(typedChar: Char, keyCode: Int) -> Unit) = apply { + @Suppress("DEPRECATION") keyTypedListeners.add(method) } + fun onKeyType(method: UIComponent.(keyEvent: UIKeyEvent) -> Unit) = apply { + keyTypedEventListeners.add(method) + } + + fun onCharType(method: UIComponent.(keyEvent: UICharEvent) -> Unit) = apply { + charTypedEventListeners.add(method) + } + + @Deprecated("[See ElementaVersion.V12]. These consumers will still function for the time being.", + replaceWith = ReplaceWith("onKeyType(method) /*or*/ onCharType(method)")) fun onKeyTypeConsumer(method: BiConsumer) { + @Suppress("DEPRECATION") keyTypedListeners.add { t: Char, u: Int -> method.accept(t, u) } } diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index a50e9cbe..44bec3f3 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -2,6 +2,8 @@ package gg.essential.elementa import gg.essential.elementa.components.Window import gg.essential.elementa.constraints.animation.* +import gg.essential.elementa.events.UICharEvent +import gg.essential.elementa.events.UIKeyEvent import gg.essential.universal.UKeyboard import gg.essential.universal.UMatrixStack import gg.essential.universal.UMouse @@ -21,7 +23,7 @@ abstract class WindowScreen @JvmOverloads constructor( private val drawDefaultBackground: Boolean = true, restoreCurrentGuiOnClose: Boolean = false, newGuiScale: Int = -1 -) : UScreen(restoreCurrentGuiOnClose, newGuiScale) { +) : UScreen(restoreCurrentGuiOnClose, newGuiScale), UScreen.InputHandler { val window = Window(version) private var isInitialized = false @@ -35,8 +37,22 @@ abstract class WindowScreen @JvmOverloads constructor( ) : this(ElementaVersion.v0, enableRepeatKeys, drawDefaultBackground, restoreCurrentGuiOnClose, newGuiScale) init { - window.onKeyType { typedChar, keyCode -> - defaultKeyBehavior(typedChar, keyCode) + if (version >= ElementaVersion.v12) { + window.onKeyType { keyEvent -> + defaultKeyBehavior(keyEvent) + } + window.onCharType { charEvent -> + defaultCharBehavior(charEvent) + } + } else { + @Suppress("DEPRECATION") + window.onKeyType { typedChar, keyCode -> + defaultKeyBehavior(typedChar, keyCode) + } + + // This will ensure that inputs go to the old `on*(): Unit` input functions + // rather than the V12+ `u*(): Boolean` functions + inputHandler = null } } @@ -60,6 +76,7 @@ abstract class WindowScreen @JvmOverloads constructor( window.draw(matrixStack) } + @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") override fun onMouseClicked(mouseX: Double, mouseY: Double, mouseButton: Int) { super.onMouseClicked(mouseX, mouseY, mouseButton) @@ -79,6 +96,14 @@ abstract class WindowScreen @JvmOverloads constructor( window.mouseClick(adjustedMouseX, adjustedMouseY, mouseButton) } + // Called only when ElementaVersion >= V12 + override fun uMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { + // TODO returning implementation, see uKeyPressed() + onMouseClicked(x, y, button) + return false + } + + @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") override fun onMouseReleased(mouseX: Double, mouseY: Double, state: Int) { super.onMouseReleased(mouseX, mouseY, state) @@ -86,6 +111,13 @@ abstract class WindowScreen @JvmOverloads constructor( window.mouseRelease() } + // Called only when ElementaVersion >= V12 + override fun uMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { + // TODO returning implementation, see uKeyPressed() + onMouseReleased(x, y, button) + return false + } + @Suppress("DEPRECATION") @Deprecated( "Provided `delta` values have different units depending on Minecraft versions. See ElementaVersion.V11 for details.", @@ -100,6 +132,7 @@ abstract class WindowScreen @JvmOverloads constructor( } } + @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") override fun onMouseScrolled(mouseX: Double, mouseY: Double, deltaHorizontal: Double, deltaVertical: Double) { super.onMouseScrolled(mouseX, mouseY, deltaHorizontal, deltaVertical) @@ -108,11 +141,36 @@ abstract class WindowScreen @JvmOverloads constructor( } } + // Called only when ElementaVersion >= V12 + override fun uMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean { + // TODO returning implementation, see uKeyPressed() + onMouseScrolled(x, y, scrollX, scrollY) + return false + } + + @Suppress("DEPRECATION") + @Deprecated("Method is not called when ElementaVersion >= V12. See ElementaVersion.V12 for details.", + replaceWith = ReplaceWith("uKeyPressed(key, scanCode, modifiers)") + ) override fun onKeyPressed(keyCode: Int, typedChar: Char, modifiers: UKeyboard.Modifiers?) { // We also need to pass along typed keys window.keyType(typedChar, keyCode) } + // Called only when ElementaVersion >= V12 + override fun uKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean { + val event = UIKeyEvent(key, scanCode, modifiers) + window.keyType(event) + return event.isStopped() + } + + // Called only when ElementaVersion >= V12 + override fun uCharTyped(codepoint: Int): Boolean { + val event = UICharEvent(codepoint) + window.charType(event) + return event.isStopped() + } + override fun initScreen(width: Int, height: Int) { window.onWindowResize() @@ -133,10 +191,26 @@ abstract class WindowScreen @JvmOverloads constructor( UKeyboard.allowRepeatEvents(false) } + @Deprecated("[See ElementaVersion.V12] This method is not called when ElementaVersion >= V12.") fun defaultKeyBehavior(typedChar: Char, keyCode: Int) { + @Suppress("DEPRECATION") super.onKeyPressed(keyCode, typedChar, UKeyboard.getModifiers()) } + // Called only when ElementaVersion >= V12 + fun defaultKeyBehavior(keyEvent: UIKeyEvent) { + if (keyEvent.keyCode != 0 && super.uKeyPressed(keyEvent.keyCode, keyEvent.scanCode, keyEvent.modifiers)) { + keyEvent.stopPropagation() + } + } + + // Called only when ElementaVersion >= V12 + fun defaultCharBehavior(charEvent: UICharEvent) { + if (!charEvent.char.isISOControl() && super.uCharTyped(charEvent.codepoint)) { + charEvent.stopPropagation() + } + } + /** * Field animation API */ diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 0f3a0c99..30bb1201 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -7,6 +7,8 @@ import gg.essential.elementa.constraints.resolution.ConstraintResolutionGui import gg.essential.elementa.constraints.resolution.ConstraintResolver import gg.essential.elementa.constraints.resolution.ConstraintResolverV2 import gg.essential.elementa.effects.ScissorEffect +import gg.essential.elementa.events.UICharEvent +import gg.essential.elementa.events.UIKeyEvent import gg.essential.elementa.font.FontRenderer import gg.essential.elementa.utils.elementaDev import gg.essential.elementa.utils.requireMainThread @@ -329,6 +331,9 @@ class Window @JvmOverloads constructor( currentMouseButton = -1 } + @Suppress("DEPRECATION") + @Deprecated("[See ElementaVersion.V12].", + replaceWith = ReplaceWith("keyType(keyEvent) or chartype(charEvent)")) override fun keyType(typedChar: Char, keyCode: Int) { if (hasErrored && version >= ElementaVersion.v7) { return @@ -347,6 +352,24 @@ class Window @JvmOverloads constructor( } } + // Called only when ElementaVersion >= V12 + override fun keyType(keyEvent: UIKeyEvent) { + if (hasErrored) return + + requireMainThread() + + focusedComponent?.keyType(keyEvent) ?: super.keyType(keyEvent) + } + + // Called only when ElementaVersion >= V12 + override fun charType(charEvent: UICharEvent) { + if (hasErrored) return + + requireMainThread() + + focusedComponent?.charType(charEvent) ?: super.charType(charEvent) + } + internal var prevDraggedMouseX: Float? = null internal var prevDraggedMouseY: Float? = null From 0d413f739cb06286e360e6ae9ef72d55eb458731 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 30 Mar 2026 00:21:11 +1000 Subject: [PATCH 05/43] Misc: usage suppressions --- .../essential/elementa/components/input/AbstractTextInput.kt | 1 + .../kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt | 1 + .../kotlin/gg/essential/elementa/unstable/util/focusable.kt | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 9f85f7ee..7ee45b4a 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -81,6 +81,7 @@ abstract class AbstractTextInput( setHeight(lineHeight.pixels()) + @Suppress("DEPRECATION") onKeyType { typedChar, keyCode -> if (!active) return@onKeyType diff --git a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt index 4193a417..33a33ce4 100644 --- a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt @@ -110,6 +110,7 @@ class MarkdownComponent( grabWindowFocus() } + @Suppress("DEPRECATION") onKeyType { _, keyCode -> if (selection != null && keyCode == UKeyboard.KEY_C && UKeyboard.isCtrlKeyDown()) { UDesktop.setClipboardString(drawables.selectedText(UKeyboard.isShiftKeyDown())) diff --git a/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt b/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt index 2b619bbc..a1bc4fbc 100644 --- a/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt +++ b/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt @@ -19,7 +19,7 @@ fun Modifier.focusable(disabled: State = stateOf(false)): Modifier { return tag(Focusable(disabled)) .then { val keyListener = setupKeyboardNavigation() - return@then { keyTypedListeners.remove(keyListener) } + return@then { @Suppress("DEPRECATION") keyTypedListeners.remove(keyListener) } } .then { makeFocusOrHoverScope(); { throw NotImplementedError() } } } @@ -61,6 +61,7 @@ private fun UIComponent.setupKeyboardNavigation(): UIComponent.(Char, Int) -> Un } } + @Suppress("DEPRECATION") onKeyType(keyListener) return keyListener From 8b8f630a21980fbe4ba53fc68ed0d9bf084a8a05 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 30 Mar 2026 00:21:25 +1000 Subject: [PATCH 06/43] API: update --- api/Elementa.api | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/api/Elementa.api b/api/Elementa.api index be070249..f4188dae 100644 --- a/api/Elementa.api +++ b/api/Elementa.api @@ -4,6 +4,7 @@ public final class gg/essential/elementa/ElementaVersion : java/lang/Enum { public static final field V1 Lgg/essential/elementa/ElementaVersion; public static final field V10 Lgg/essential/elementa/ElementaVersion; public static final field V11 Lgg/essential/elementa/ElementaVersion; + public static final field V12 Lgg/essential/elementa/ElementaVersion; public static final field V2 Lgg/essential/elementa/ElementaVersion; public static final field V3 Lgg/essential/elementa/ElementaVersion; public static final field V4 Lgg/essential/elementa/ElementaVersion; @@ -55,6 +56,7 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun beforeDraw ()V public fun beforeDraw (Lgg/essential/universal/UMatrixStack;)V public final fun beforeDrawCompat (Lgg/essential/universal/UMatrixStack;)V + public fun charType (Lgg/essential/elementa/events/UICharEvent;)V public fun childrenOfType (Ljava/lang/Class;)Ljava/util/List; public fun clearChildren ()Lgg/essential/elementa/UIComponent; public final fun delay (JLkotlin/jvm/functions/Function0;)Lkotlin/jvm/functions/Function0; @@ -70,6 +72,7 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, protected final fun fireScrollEvent (Lgg/essential/elementa/events/UIScrollEvent;)V public final fun focus ()V public fun getBottom ()F + public final fun getCharTypedEventListeners ()Ljava/util/List; public fun getChildren ()Lgg/essential/elementa/utils/ObservableList; public fun getColor ()Ljava/awt/Color; public final fun getComponentName ()Ljava/lang/String; @@ -78,6 +81,7 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun getFontProvider ()Lgg/essential/elementa/font/FontProvider; public fun getHasParent ()Z public fun getHeight ()F + public final fun getKeyTypedEventListeners ()Ljava/util/List; public final fun getKeyTypedListeners ()Ljava/util/List; public final fun getLastDraggedMouseX ()Ljava/lang/Double; public final fun getLastDraggedMouseY ()Ljava/lang/Double; @@ -112,6 +116,7 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun isPointInside (FF)Z public fun isPositionCenter ()Z public fun keyType (CI)V + public fun keyType (Lgg/essential/elementa/events/UIKeyEvent;)V public final fun loseFocus ()V public final fun makeAnimation ()Lgg/essential/elementa/constraints/animation/AnimatingConstraints; public fun mouseClick (DDI)V @@ -120,8 +125,10 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun mouseRelease ()V public fun mouseScroll (D)V public fun mouseScroll (DD)V + public final fun onCharType (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; public final fun onFocus (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/UIComponent; public final fun onFocusLost (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/UIComponent; + public final fun onKeyType (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; public final fun onKeyType (Lkotlin/jvm/functions/Function3;)Lgg/essential/elementa/UIComponent; public final fun onKeyTypeConsumer (Ljava/util/function/BiConsumer;)V public final fun onMouseClick (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; @@ -243,7 +250,7 @@ public final class gg/essential/elementa/VanillaFontRenderer : gg/essential/elem public final class gg/essential/elementa/VanillaFontRenderer$Companion { } -public abstract class gg/essential/elementa/WindowScreen : gg/essential/universal/UScreen { +public abstract class gg/essential/elementa/WindowScreen : gg/essential/universal/UScreen, gg/essential/universal/UScreen$InputHandler { public fun ()V public fun (Lgg/essential/elementa/ElementaVersion;)V public fun (Lgg/essential/elementa/ElementaVersion;Z)V @@ -267,7 +274,9 @@ public abstract class gg/essential/elementa/WindowScreen : gg/essential/universa public static synthetic fun animate$default (Lgg/essential/elementa/WindowScreen;Lkotlin/reflect/KMutableProperty0;Lgg/essential/elementa/constraints/animation/AnimationStrategy;FIFILjava/lang/Object;)V public static synthetic fun animate$default (Lgg/essential/elementa/WindowScreen;Lkotlin/reflect/KMutableProperty0;Lgg/essential/elementa/constraints/animation/AnimationStrategy;FJFILjava/lang/Object;)V public static synthetic fun animate$default (Lgg/essential/elementa/WindowScreen;Lkotlin/reflect/KMutableProperty0;Lgg/essential/elementa/constraints/animation/AnimationStrategy;FLjava/awt/Color;FILjava/lang/Object;)V + public final fun defaultCharBehavior (Lgg/essential/elementa/events/UICharEvent;)V public final fun defaultKeyBehavior (CI)V + public final fun defaultKeyBehavior (Lgg/essential/elementa/events/UIKeyEvent;)V public final fun getWindow ()Lgg/essential/elementa/components/Window; public fun initScreen (II)V public fun onDrawScreen (Lgg/essential/universal/UMatrixStack;IIF)V @@ -278,6 +287,11 @@ public abstract class gg/essential/elementa/WindowScreen : gg/essential/universa public fun onMouseScrolled (DDDD)V public fun onScreenClose ()V public final fun stopAnimating (Lkotlin/reflect/KMutableProperty0;)V + public fun uCharTyped (I)Z + public fun uKeyPressed (IILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uMouseClicked (DDILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uMouseReleased (DDILgg/essential/universal/UKeyboard$Modifiers;)Z + public fun uMouseScrolled (DDDD)Z } public class gg/essential/elementa/components/GradientComponent : gg/essential/elementa/components/UIBlock { @@ -756,6 +770,7 @@ public final class gg/essential/elementa/components/Window : gg/essential/elemen public final fun addFloatingComponent (Lgg/essential/elementa/UIComponent;)V public fun afterInitialization ()V public fun animationFrame ()V + public fun charType (Lgg/essential/elementa/events/UICharEvent;)V public fun draw (Lgg/essential/universal/UMatrixStack;)V public final fun drawFloatingComponents ()V public final fun drawFloatingComponents (Lgg/essential/universal/UMatrixStack;)V @@ -776,6 +791,7 @@ public final class gg/essential/elementa/components/Window : gg/essential/elemen public final fun invalidateCachedConstraints ()V public final fun isAreaVisible (DDDD)Z public fun keyType (CI)V + public fun keyType (Lgg/essential/elementa/events/UIKeyEvent;)V public fun mouseClick (DDI)V public fun mouseRelease ()V public fun mouseScroll (D)V @@ -2717,6 +2733,12 @@ public final class gg/essential/elementa/effects/StencilEffect$Companion { public final fun enableStencil ()V } +public final class gg/essential/elementa/events/UICharEvent : gg/essential/elementa/events/UIEvent { + public fun (I)V + public final fun getChar ()C + public final fun getCodepoint ()I +} + public final class gg/essential/elementa/events/UIClickEvent : gg/essential/elementa/events/UIEvent { public fun (FFILgg/essential/elementa/UIComponent;Lgg/essential/elementa/UIComponent;I)V public final fun component1 ()F @@ -2744,12 +2766,20 @@ public abstract class gg/essential/elementa/events/UIEvent { public fun ()V public final fun getPropagationStopped ()Z public final fun getPropagationStoppedImmediately ()Z + public final fun isStopped ()Z public final fun setPropagationStopped (Z)V public final fun setPropagationStoppedImmediately (Z)V public final fun stopImmediatePropagation ()V public final fun stopPropagation ()V } +public final class gg/essential/elementa/events/UIKeyEvent : gg/essential/elementa/events/UIEvent { + public fun (IILgg/essential/universal/UKeyboard$Modifiers;)V + public final fun getKeyCode ()I + public final fun getModifiers ()Lgg/essential/universal/UKeyboard$Modifiers; + public final fun getScanCode ()I +} + public final class gg/essential/elementa/events/UIScrollEvent : gg/essential/elementa/events/UIEvent { public fun (DDLgg/essential/elementa/UIComponent;Lgg/essential/elementa/UIComponent;)V public fun (DLgg/essential/elementa/UIComponent;Lgg/essential/elementa/UIComponent;)V From 2060917e91a7402e23e161ba29b1caaa8853b579 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 10:02:54 +1000 Subject: [PATCH 07/43] Review: fix deprecation text --- src/main/kotlin/gg/essential/elementa/UIComponent.kt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index 3de324ae..3df708b3 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -121,8 +121,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { get() = field.also { ownFlags += Flags.RequiresMouseMove } val mouseDragListeners = mutableListOf Unit>() get() = field.also { ownFlags += Flags.RequiresMouseDrag } - @Deprecated("[See ElementaVersion.V12]. These listeners will still function for the time being.", - replaceWith = ReplaceWith("keyTypedEventListeners; /*or*/ charTypedEventListeners")) + @Deprecated("See [ElementaVersion.V12]. These listeners will still function for the time being.") val keyTypedListeners = mutableListOf Unit>() val keyTypedEventListeners = mutableListOf Unit>() val charTypedEventListeners = mutableListOf Unit>() @@ -842,8 +841,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { this.forEachChild { it.superCall() } } - @Deprecated("[See ElementaVersion.V12]. This will still be called for backwards compatibility in the mean time.", - replaceWith = ReplaceWith("keyType(keyEvent) /*and*/ chartype(charEvent)")) + @Deprecated("See {ElementaVersion.V12]. This will still be called for backwards compatibility in the mean time.") open fun keyType(typedChar: Char, keyCode: Int) { @Suppress("DEPRECATION") for (listener in keyTypedListeners) @@ -1052,8 +1050,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { mouseScrollListeners.add { method.accept(it) } } - @Deprecated("[See ElementaVersion.V12]. These listeners will still function for the time being.", - replaceWith = ReplaceWith("onKeyType(method) /*or*/ onCharType(method)")) + @Deprecated("See [ElementaVersion.V12]. These listeners will still function for the time being.") fun onKeyType(method: UIComponent.(typedChar: Char, keyCode: Int) -> Unit) = apply { @Suppress("DEPRECATION") keyTypedListeners.add(method) @@ -1067,8 +1064,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { charTypedEventListeners.add(method) } - @Deprecated("[See ElementaVersion.V12]. These consumers will still function for the time being.", - replaceWith = ReplaceWith("onKeyType(method) /*or*/ onCharType(method)")) + @Deprecated("See [ElementaVersion.V12]. These consumers will still function for the time being.") fun onKeyTypeConsumer(method: BiConsumer) { @Suppress("DEPRECATION") keyTypedListeners.add { t: Char, u: Int -> method.accept(t, u) } From 92f656f0b090bbe8170e7c7fbde385b1408c114f Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 10:28:42 +1000 Subject: [PATCH 08/43] Review: convert to using `-> Boolean` functions --- .../gg/essential/elementa/UIComponent.kt | 22 +++++++++------- .../gg/essential/elementa/WindowScreen.kt | 26 +++---------------- .../essential/elementa/components/Window.kt | 12 ++++----- .../gg/essential/elementa/events/UIEvent.kt | 2 -- .../elementa/events/UIKeyboardEvents.kt | 4 +-- 5 files changed, 24 insertions(+), 42 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index 3df708b3..271d4446 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -123,8 +123,8 @@ abstract class UIComponent : Observable(), ReferenceHolder { get() = field.also { ownFlags += Flags.RequiresMouseDrag } @Deprecated("See [ElementaVersion.V12]. These listeners will still function for the time being.") val keyTypedListeners = mutableListOf Unit>() - val keyTypedEventListeners = mutableListOf Unit>() - val charTypedEventListeners = mutableListOf Unit>() + val keyTypedEventListeners = mutableListOf Boolean>() + val charTypedEventListeners = mutableListOf Boolean>() private var currentlyHovered = false private val beforeHideAnimations = mutableListOf Unit>() @@ -849,25 +849,27 @@ abstract class UIComponent : Observable(), ReferenceHolder { } // Called only when ElementaVersion >= V12 - open fun keyType(keyEvent: UIKeyEvent) { + open fun keyType(keyEvent: UIKeyEvent): Boolean { for (listener in keyTypedEventListeners){ - this.listener(keyEvent) - if (keyEvent.propagationStoppedImmediately) return + if (this.listener(keyEvent)) return true } @Suppress("DEPRECATION") // Retaining support for the old listeners for now as it is trivial to do so keyType(0.toChar(), keyEvent.keyCode) + + return false } // Called only when ElementaVersion >= V12 - open fun charType(charEvent: UICharEvent) { + open fun charType(charEvent: UICharEvent): Boolean { for (listener in charTypedEventListeners){ - this.listener(charEvent) - if (charEvent.propagationStoppedImmediately) return + if (this.listener(charEvent)) return true } @Suppress("DEPRECATION") // Retaining support for the old listeners for now as it is trivial to do so keyType(charEvent.char, 0) + + return false } @Deprecated("See [ElementaVersion.V8].") @@ -1056,11 +1058,11 @@ abstract class UIComponent : Observable(), ReferenceHolder { keyTypedListeners.add(method) } - fun onKeyType(method: UIComponent.(keyEvent: UIKeyEvent) -> Unit) = apply { + fun onKeyType(method: UIComponent.(keyEvent: UIKeyEvent) -> Boolean) = apply { keyTypedEventListeners.add(method) } - fun onCharType(method: UIComponent.(keyEvent: UICharEvent) -> Unit) = apply { + fun onCharType(method: UIComponent.(keyEvent: UICharEvent) -> Boolean) = apply { charTypedEventListeners.add(method) } diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index 44bec3f3..d8283680 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -39,10 +39,10 @@ abstract class WindowScreen @JvmOverloads constructor( init { if (version >= ElementaVersion.v12) { window.onKeyType { keyEvent -> - defaultKeyBehavior(keyEvent) + super.uKeyPressed(keyEvent.keyCode, keyEvent.scanCode, keyEvent.modifiers) } window.onCharType { charEvent -> - defaultCharBehavior(charEvent) + super.uCharTyped(charEvent.codepoint) } } else { @Suppress("DEPRECATION") @@ -159,16 +159,12 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean { - val event = UIKeyEvent(key, scanCode, modifiers) - window.keyType(event) - return event.isStopped() + return window.keyType(UIKeyEvent(key, scanCode, modifiers)) } // Called only when ElementaVersion >= V12 override fun uCharTyped(codepoint: Int): Boolean { - val event = UICharEvent(codepoint) - window.charType(event) - return event.isStopped() + return window.charType(UICharEvent(codepoint)) } override fun initScreen(width: Int, height: Int) { @@ -197,20 +193,6 @@ abstract class WindowScreen @JvmOverloads constructor( super.onKeyPressed(keyCode, typedChar, UKeyboard.getModifiers()) } - // Called only when ElementaVersion >= V12 - fun defaultKeyBehavior(keyEvent: UIKeyEvent) { - if (keyEvent.keyCode != 0 && super.uKeyPressed(keyEvent.keyCode, keyEvent.scanCode, keyEvent.modifiers)) { - keyEvent.stopPropagation() - } - } - - // Called only when ElementaVersion >= V12 - fun defaultCharBehavior(charEvent: UICharEvent) { - if (!charEvent.char.isISOControl() && super.uCharTyped(charEvent.codepoint)) { - charEvent.stopPropagation() - } - } - /** * Field animation API */ diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 30bb1201..3a36d4f6 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -353,21 +353,21 @@ class Window @JvmOverloads constructor( } // Called only when ElementaVersion >= V12 - override fun keyType(keyEvent: UIKeyEvent) { - if (hasErrored) return + override fun keyType(keyEvent: UIKeyEvent): Boolean { + if (hasErrored) return false requireMainThread() - focusedComponent?.keyType(keyEvent) ?: super.keyType(keyEvent) + return (focusedComponent?.keyType(keyEvent) ?: false) || super.keyType(keyEvent) } // Called only when ElementaVersion >= V12 - override fun charType(charEvent: UICharEvent) { - if (hasErrored) return + override fun charType(charEvent: UICharEvent): Boolean { + if (hasErrored) return false requireMainThread() - focusedComponent?.charType(charEvent) ?: super.charType(charEvent) + return (focusedComponent?.charType(charEvent) ?: false) || super.charType(charEvent) } internal var prevDraggedMouseX: Float? = null diff --git a/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt b/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt index 1e5b1770..4bc942d5 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIEvent.kt @@ -4,8 +4,6 @@ abstract class UIEvent { var propagationStopped = false var propagationStoppedImmediately = false - fun isStopped() = propagationStopped || propagationStoppedImmediately - /** * Stops this event from continuing to bubble to parent components. If there are * multiple listeners on the current component, those "sibling" listeners will continue to fire. diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt index 807f1ff9..fdc5ab90 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -9,7 +9,7 @@ class UIKeyEvent( val keyCode: Int, val scanCode: Int, val modifiers: UKeyboard.Modifiers, -) : UIEvent() +) /** @@ -17,6 +17,6 @@ class UIKeyEvent( */ class UICharEvent( val codepoint: Int, -) : UIEvent() { +) { val char = codepoint.toChar() } \ No newline at end of file From e94c8dac2fdc67cbd6ab8dd442608876ef785a8c Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 10:32:14 +1000 Subject: [PATCH 09/43] Review: comments --- src/main/kotlin/gg/essential/elementa/UIComponent.kt | 2 ++ .../kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index 271d4446..9c8059a2 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -123,7 +123,9 @@ abstract class UIComponent : Observable(), ReferenceHolder { get() = field.also { ownFlags += Flags.RequiresMouseDrag } @Deprecated("See [ElementaVersion.V12]. These listeners will still function for the time being.") val keyTypedListeners = mutableListOf Unit>() + // Requires ElementaVersion.V12 val keyTypedEventListeners = mutableListOf Boolean>() + // Requires ElementaVersion.V12 val charTypedEventListeners = mutableListOf Boolean>() private var currentlyHovered = false diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt index fdc5ab90..dc8aa42e 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -4,6 +4,8 @@ import gg.essential.universal.UKeyboard /** * Represents a key typing event whose propagation can be stopped from continuing on to Minecraft + * + * Requires ElementaVersion.V12 */ class UIKeyEvent( val keyCode: Int, @@ -14,6 +16,8 @@ class UIKeyEvent( /** * Represents a character typing event whose propagation can be stopped from continuing on to Minecraft + * + * Requires ElementaVersion.V12 */ class UICharEvent( val codepoint: Int, From 7e90891902e3d787d32a28bceb2d96fb64067118 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 10:33:12 +1000 Subject: [PATCH 10/43] Review: no longer use old listeners --- .../kotlin/gg/essential/elementa/UIComponent.kt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index 9c8059a2..a59ee05c 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -121,7 +121,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { get() = field.also { ownFlags += Flags.RequiresMouseMove } val mouseDragListeners = mutableListOf Unit>() get() = field.also { ownFlags += Flags.RequiresMouseDrag } - @Deprecated("See [ElementaVersion.V12]. These listeners will still function for the time being.") + @Deprecated("See [ElementaVersion.V12].") val keyTypedListeners = mutableListOf Unit>() // Requires ElementaVersion.V12 val keyTypedEventListeners = mutableListOf Boolean>() @@ -855,10 +855,6 @@ abstract class UIComponent : Observable(), ReferenceHolder { for (listener in keyTypedEventListeners){ if (this.listener(keyEvent)) return true } - - @Suppress("DEPRECATION") // Retaining support for the old listeners for now as it is trivial to do so - keyType(0.toChar(), keyEvent.keyCode) - return false } @@ -867,10 +863,6 @@ abstract class UIComponent : Observable(), ReferenceHolder { for (listener in charTypedEventListeners){ if (this.listener(charEvent)) return true } - - @Suppress("DEPRECATION") // Retaining support for the old listeners for now as it is trivial to do so - keyType(charEvent.char, 0) - return false } @@ -1054,7 +1046,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { mouseScrollListeners.add { method.accept(it) } } - @Deprecated("See [ElementaVersion.V12]. These listeners will still function for the time being.") + @Deprecated("See [ElementaVersion.V12].") fun onKeyType(method: UIComponent.(typedChar: Char, keyCode: Int) -> Unit) = apply { @Suppress("DEPRECATION") keyTypedListeners.add(method) @@ -1068,7 +1060,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { charTypedEventListeners.add(method) } - @Deprecated("See [ElementaVersion.V12]. These consumers will still function for the time being.") + @Deprecated("See [ElementaVersion.V12].") fun onKeyTypeConsumer(method: BiConsumer) { @Suppress("DEPRECATION") keyTypedListeners.add { t: Char, u: Int -> method.accept(t, u) } From 67abf9f6574bd8ca207b3654a4451ec8d6f6e900 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 10:42:50 +1000 Subject: [PATCH 11/43] Review: comment --- src/main/kotlin/gg/essential/elementa/UIComponent.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index a59ee05c..80d1ccda 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -843,7 +843,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { this.forEachChild { it.superCall() } } - @Deprecated("See {ElementaVersion.V12]. This will still be called for backwards compatibility in the mean time.") + @Deprecated("See {ElementaVersion.V12].") open fun keyType(typedChar: Char, keyCode: Int) { @Suppress("DEPRECATION") for (listener in keyTypedListeners) From 5314522930c18209035dc55f1fa422ecd92523e4 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 10:45:26 +1000 Subject: [PATCH 12/43] Review: formatting --- src/main/kotlin/gg/essential/elementa/UIComponent.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index 80d1ccda..f9af410e 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -852,7 +852,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { // Called only when ElementaVersion >= V12 open fun keyType(keyEvent: UIKeyEvent): Boolean { - for (listener in keyTypedEventListeners){ + for (listener in keyTypedEventListeners) { if (this.listener(keyEvent)) return true } return false @@ -860,7 +860,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { // Called only when ElementaVersion >= V12 open fun charType(charEvent: UICharEvent): Boolean { - for (listener in charTypedEventListeners){ + for (listener in charTypedEventListeners) { if (this.listener(charEvent)) return true } return false From 33f2fddaff32c38adeac546fe86985f16535c8d8 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 10:59:46 +1000 Subject: [PATCH 13/43] Review: inline new functions into Window --- .../gg/essential/elementa/UIComponent.kt | 16 ----------- .../essential/elementa/components/Window.kt | 28 ++++++++++++++++--- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index f9af410e..fe629e24 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -850,22 +850,6 @@ abstract class UIComponent : Observable(), ReferenceHolder { this.listener(typedChar, keyCode) } - // Called only when ElementaVersion >= V12 - open fun keyType(keyEvent: UIKeyEvent): Boolean { - for (listener in keyTypedEventListeners) { - if (this.listener(keyEvent)) return true - } - return false - } - - // Called only when ElementaVersion >= V12 - open fun charType(charEvent: UICharEvent): Boolean { - for (listener in charTypedEventListeners) { - if (this.listener(charEvent)) return true - } - return false - } - @Deprecated("See [ElementaVersion.V8].") open fun animationFrame() { if (versionOrV0 >= ElementaVersion.v8) { diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 3a36d4f6..65ff21d8 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -353,21 +353,41 @@ class Window @JvmOverloads constructor( } // Called only when ElementaVersion >= V12 - override fun keyType(keyEvent: UIKeyEvent): Boolean { + fun keyType(keyEvent: UIKeyEvent): Boolean { if (hasErrored) return false requireMainThread() - return (focusedComponent?.keyType(keyEvent) ?: false) || super.keyType(keyEvent) + focusedComponent?.run { + for (listener in keyTypedEventListeners) { + if (listener(keyEvent)) return true + } + } + + for (listener in keyTypedEventListeners) { + if (listener(keyEvent)) return true + } + + return false } // Called only when ElementaVersion >= V12 - override fun charType(charEvent: UICharEvent): Boolean { + fun charType(charEvent: UICharEvent): Boolean { if (hasErrored) return false requireMainThread() - return (focusedComponent?.charType(charEvent) ?: false) || super.charType(charEvent) + focusedComponent?.run { + for (listener in charTypedEventListeners) { + if (listener(charEvent)) return true + } + } + + for (listener in charTypedEventListeners) { + if (listener(charEvent)) return true + } + + return false } internal var prevDraggedMouseX: Float? = null From fb410e012903d032ef1ee8bd502e3f89a830a7b8 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 11:00:39 +1000 Subject: [PATCH 14/43] Review: comment --- src/main/kotlin/gg/essential/elementa/components/Window.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 65ff21d8..419c6ba0 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -332,8 +332,7 @@ class Window @JvmOverloads constructor( } @Suppress("DEPRECATION") - @Deprecated("[See ElementaVersion.V12].", - replaceWith = ReplaceWith("keyType(keyEvent) or chartype(charEvent)")) + @Deprecated("See [ElementaVersion.V12].") override fun keyType(typedChar: Char, keyCode: Int) { if (hasErrored && version >= ElementaVersion.v7) { return From 9df4981b8a6f32f62260d37910deecae64f2ad93 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 11:04:22 +1000 Subject: [PATCH 15/43] Review: rename functions --- src/main/kotlin/gg/essential/elementa/WindowScreen.kt | 8 +++----- .../kotlin/gg/essential/elementa/components/Window.kt | 4 ++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index d8283680..c3828dbc 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -149,9 +149,7 @@ abstract class WindowScreen @JvmOverloads constructor( } @Suppress("DEPRECATION") - @Deprecated("Method is not called when ElementaVersion >= V12. See ElementaVersion.V12 for details.", - replaceWith = ReplaceWith("uKeyPressed(key, scanCode, modifiers)") - ) + @Deprecated("Method is not called when ElementaVersion >= V12. See ElementaVersion.V12 for details.") override fun onKeyPressed(keyCode: Int, typedChar: Char, modifiers: UKeyboard.Modifiers?) { // We also need to pass along typed keys window.keyType(typedChar, keyCode) @@ -159,12 +157,12 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uKeyPressed(key: Int, scanCode: Int, modifiers: UKeyboard.Modifiers): Boolean { - return window.keyType(UIKeyEvent(key, scanCode, modifiers)) + return window.keyPressed(UIKeyEvent(key, scanCode, modifiers)) } // Called only when ElementaVersion >= V12 override fun uCharTyped(codepoint: Int): Boolean { - return window.charType(UICharEvent(codepoint)) + return window.charTyped(UICharEvent(codepoint)) } override fun initScreen(width: Int, height: Int) { diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 419c6ba0..5d98e5ba 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -352,7 +352,7 @@ class Window @JvmOverloads constructor( } // Called only when ElementaVersion >= V12 - fun keyType(keyEvent: UIKeyEvent): Boolean { + fun keyPressed(keyEvent: UIKeyEvent): Boolean { if (hasErrored) return false requireMainThread() @@ -371,7 +371,7 @@ class Window @JvmOverloads constructor( } // Called only when ElementaVersion >= V12 - fun charType(charEvent: UICharEvent): Boolean { + fun charTyped(charEvent: UICharEvent): Boolean { if (hasErrored) return false requireMainThread() From 53c76dfa06c4147e3ed03fcb09e56b335c2d03f7 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 11:11:49 +1000 Subject: [PATCH 16/43] Review: OVERRIDE_DEPRECATION -> Deprecation --- src/main/kotlin/gg/essential/elementa/WindowScreen.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index c3828dbc..ca33c2d7 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -76,7 +76,8 @@ abstract class WindowScreen @JvmOverloads constructor( window.draw(matrixStack) } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") + @Deprecated("See [ElementaVersion.V12]") + @Suppress("DEPRECATION") override fun onMouseClicked(mouseX: Double, mouseY: Double, mouseButton: Int) { super.onMouseClicked(mouseX, mouseY, mouseButton) From 78cdc6274e81544ca73ac8034a1457a92f8bafbe Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 14:16:36 +1000 Subject: [PATCH 17/43] Review: replace incorrect toChar() usage with a properly constructed string --- .../kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt index dc8aa42e..f23c4e80 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -22,5 +22,5 @@ class UIKeyEvent( class UICharEvent( val codepoint: Int, ) { - val char = codepoint.toChar() + val string = String(Character.toChars(codepoint)) } \ No newline at end of file From cb5185985f1158786fa46aa62682de668aad0103 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 14:33:27 +1000 Subject: [PATCH 18/43] Review: call uSuperInputHandler() super methods --- .../gg/essential/elementa/WindowScreen.kt | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index ca33c2d7..221f8b61 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -99,9 +99,12 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { - // TODO returning implementation, see uKeyPressed() - onMouseClicked(x, y, button) - return false + uSuperInputHandler().uMouseClicked(x, y, button, modifiers) + + // We also need to pass along clicks + window.mouseClick(x, y, button) + + return false // TODO returning implementation, see uKeyPressed() } @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") @@ -114,9 +117,12 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { - // TODO returning implementation, see uKeyPressed() - onMouseReleased(x, y, button) - return false + uSuperInputHandler().uMouseReleased(x, y, button, modifiers) + + // We also need to pass along mouse releases + window.mouseRelease() + + return false // TODO returning implementation, see uKeyPressed() } @Suppress("DEPRECATION") @@ -144,9 +150,11 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean { - // TODO returning implementation, see uKeyPressed() - onMouseScrolled(x, y, scrollX, scrollY) - return false + uSuperInputHandler().uMouseScrolled(x, y, scrollX, scrollY) + + window.mouseScroll(scrollX, scrollY) + + return false // TODO returning implementation, see uKeyPressed() } @Suppress("DEPRECATION") From 5dbf1adaff6f73857ad41c683466459c113b6fb8 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 14:43:05 +1000 Subject: [PATCH 19/43] Review: fixup comments --- src/main/kotlin/gg/essential/elementa/ElementaVersion.kt | 9 ++++----- src/main/kotlin/gg/essential/elementa/UIComponent.kt | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt index 127b712c..886ec1ef 100644 --- a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt +++ b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt @@ -216,14 +216,13 @@ enum class ElementaVersion { * prevent followup execution in Minecraft itself. * * As of V12 the key & character input events have been split up and moved to these new methods. - * All occurrences of `keyType(typedChar: Char, keyCode: Int)` in [UIComponent]s have been split into: - * - keyType(keyEvent: UIKeyEvent) - * - charType(charEvent: UICharEvent) + * `keyType(typedChar: Char, keyCode: Int)` in [Window] has been split into: + * - keyPressed(keyEvent: UIKeyEvent) + * - charTyped(charEvent: UICharEvent) * As well as all related setup functions. E.G. `onKeyType { }` * For now any key listeners setup via the old methods will still be called. * - * Also see [gg.essential.elementa.events.UIKeyEvent] & [gg.essential.elementa.events.UICharEvent], note the - * propagation stopping feature. + * Also see [gg.essential.elementa.events.UIKeyEvent] & [gg.essential.elementa.events.UICharEvent]. * * See [UniversalCraft#125](https://github.com/SparkUniverse/UniversalCraft/pull/125) for the underlying changes. */ diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index fe629e24..242fc5d8 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -843,7 +843,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { this.forEachChild { it.superCall() } } - @Deprecated("See {ElementaVersion.V12].") + @Deprecated("See [ElementaVersion.V12]. Listeners are now directly called from [Window].") open fun keyType(typedChar: Char, keyCode: Int) { @Suppress("DEPRECATION") for (listener in keyTypedListeners) From daf24cbfbf9e9b7df32e0641682034f1651bba8c Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 14:51:10 +1000 Subject: [PATCH 20/43] Review: fixup function names --- src/main/kotlin/gg/essential/elementa/UIComponent.kt | 8 ++++---- src/main/kotlin/gg/essential/elementa/WindowScreen.kt | 4 ++-- .../kotlin/gg/essential/elementa/components/Window.kt | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index 242fc5d8..d3d4a64e 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -124,7 +124,7 @@ abstract class UIComponent : Observable(), ReferenceHolder { @Deprecated("See [ElementaVersion.V12].") val keyTypedListeners = mutableListOf Unit>() // Requires ElementaVersion.V12 - val keyTypedEventListeners = mutableListOf Boolean>() + val keyPressedEventListeners = mutableListOf Boolean>() // Requires ElementaVersion.V12 val charTypedEventListeners = mutableListOf Boolean>() @@ -1036,11 +1036,11 @@ abstract class UIComponent : Observable(), ReferenceHolder { keyTypedListeners.add(method) } - fun onKeyType(method: UIComponent.(keyEvent: UIKeyEvent) -> Boolean) = apply { - keyTypedEventListeners.add(method) + fun onKeyPressed(method: UIComponent.(keyEvent: UIKeyEvent) -> Boolean) = apply { + keyPressedEventListeners.add(method) } - fun onCharType(method: UIComponent.(keyEvent: UICharEvent) -> Boolean) = apply { + fun onCharTyped(method: UIComponent.(keyEvent: UICharEvent) -> Boolean) = apply { charTypedEventListeners.add(method) } diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index 221f8b61..68367a64 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -38,10 +38,10 @@ abstract class WindowScreen @JvmOverloads constructor( init { if (version >= ElementaVersion.v12) { - window.onKeyType { keyEvent -> + window.onKeyPressed { keyEvent -> super.uKeyPressed(keyEvent.keyCode, keyEvent.scanCode, keyEvent.modifiers) } - window.onCharType { charEvent -> + window.onCharTyped { charEvent -> super.uCharTyped(charEvent.codepoint) } } else { diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 5d98e5ba..539ba378 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -358,12 +358,12 @@ class Window @JvmOverloads constructor( requireMainThread() focusedComponent?.run { - for (listener in keyTypedEventListeners) { + for (listener in keyPressedEventListeners) { if (listener(keyEvent)) return true } } - for (listener in keyTypedEventListeners) { + for (listener in keyPressedEventListeners) { if (listener(keyEvent)) return true } From c5d856ea9a72e33279df94c9ecfe1cf4a4450ab5 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 15:18:18 +1000 Subject: [PATCH 21/43] Review: keyCode -> key --- src/main/kotlin/gg/essential/elementa/WindowScreen.kt | 2 +- .../kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index 68367a64..e8de23d3 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -39,7 +39,7 @@ abstract class WindowScreen @JvmOverloads constructor( init { if (version >= ElementaVersion.v12) { window.onKeyPressed { keyEvent -> - super.uKeyPressed(keyEvent.keyCode, keyEvent.scanCode, keyEvent.modifiers) + super.uKeyPressed(keyEvent.key, keyEvent.scanCode, keyEvent.modifiers) } window.onCharTyped { charEvent -> super.uCharTyped(charEvent.codepoint) diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt index f23c4e80..0f8d65ca 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -8,7 +8,7 @@ import gg.essential.universal.UKeyboard * Requires ElementaVersion.V12 */ class UIKeyEvent( - val keyCode: Int, + val key: Int, val scanCode: Int, val modifiers: UKeyboard.Modifiers, ) From 6371b34310fabe3fc19126e09ef8be38c7821911 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 15:19:57 +1000 Subject: [PATCH 22/43] Review: revert unstable diff --- .../kotlin/gg/essential/elementa/unstable/util/focusable.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt b/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt index a1bc4fbc..30b8d0c3 100644 --- a/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt +++ b/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt @@ -19,7 +19,7 @@ fun Modifier.focusable(disabled: State = stateOf(false)): Modifier { return tag(Focusable(disabled)) .then { val keyListener = setupKeyboardNavigation() - return@then { @Suppress("DEPRECATION") keyTypedListeners.remove(keyListener) } + return@then { keyTypedListeners.remove(keyListener) } } .then { makeFocusOrHoverScope(); { throw NotImplementedError() } } } From 3e478f7ce782bf35e13ff9c4118162b1e4d542b4 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 15:25:30 +1000 Subject: [PATCH 23/43] API --- api/Elementa.api | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/api/Elementa.api b/api/Elementa.api index f4188dae..6ef01a5c 100644 --- a/api/Elementa.api +++ b/api/Elementa.api @@ -56,7 +56,6 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun beforeDraw ()V public fun beforeDraw (Lgg/essential/universal/UMatrixStack;)V public final fun beforeDrawCompat (Lgg/essential/universal/UMatrixStack;)V - public fun charType (Lgg/essential/elementa/events/UICharEvent;)V public fun childrenOfType (Ljava/lang/Class;)Ljava/util/List; public fun clearChildren ()Lgg/essential/elementa/UIComponent; public final fun delay (JLkotlin/jvm/functions/Function0;)Lkotlin/jvm/functions/Function0; @@ -81,7 +80,7 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun getFontProvider ()Lgg/essential/elementa/font/FontProvider; public fun getHasParent ()Z public fun getHeight ()F - public final fun getKeyTypedEventListeners ()Ljava/util/List; + public final fun getKeyPressedEventListeners ()Ljava/util/List; public final fun getKeyTypedListeners ()Ljava/util/List; public final fun getLastDraggedMouseX ()Ljava/lang/Double; public final fun getLastDraggedMouseY ()Ljava/lang/Double; @@ -116,7 +115,6 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun isPointInside (FF)Z public fun isPositionCenter ()Z public fun keyType (CI)V - public fun keyType (Lgg/essential/elementa/events/UIKeyEvent;)V public final fun loseFocus ()V public final fun makeAnimation ()Lgg/essential/elementa/constraints/animation/AnimatingConstraints; public fun mouseClick (DDI)V @@ -125,10 +123,10 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun mouseRelease ()V public fun mouseScroll (D)V public fun mouseScroll (DD)V - public final fun onCharType (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; + public final fun onCharTyped (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; public final fun onFocus (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/UIComponent; public final fun onFocusLost (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/UIComponent; - public final fun onKeyType (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; + public final fun onKeyPressed (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; public final fun onKeyType (Lkotlin/jvm/functions/Function3;)Lgg/essential/elementa/UIComponent; public final fun onKeyTypeConsumer (Ljava/util/function/BiConsumer;)V public final fun onMouseClick (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; @@ -274,9 +272,7 @@ public abstract class gg/essential/elementa/WindowScreen : gg/essential/universa public static synthetic fun animate$default (Lgg/essential/elementa/WindowScreen;Lkotlin/reflect/KMutableProperty0;Lgg/essential/elementa/constraints/animation/AnimationStrategy;FIFILjava/lang/Object;)V public static synthetic fun animate$default (Lgg/essential/elementa/WindowScreen;Lkotlin/reflect/KMutableProperty0;Lgg/essential/elementa/constraints/animation/AnimationStrategy;FJFILjava/lang/Object;)V public static synthetic fun animate$default (Lgg/essential/elementa/WindowScreen;Lkotlin/reflect/KMutableProperty0;Lgg/essential/elementa/constraints/animation/AnimationStrategy;FLjava/awt/Color;FILjava/lang/Object;)V - public final fun defaultCharBehavior (Lgg/essential/elementa/events/UICharEvent;)V public final fun defaultKeyBehavior (CI)V - public final fun defaultKeyBehavior (Lgg/essential/elementa/events/UIKeyEvent;)V public final fun getWindow ()Lgg/essential/elementa/components/Window; public fun initScreen (II)V public fun onDrawScreen (Lgg/essential/universal/UMatrixStack;IIF)V @@ -770,7 +766,7 @@ public final class gg/essential/elementa/components/Window : gg/essential/elemen public final fun addFloatingComponent (Lgg/essential/elementa/UIComponent;)V public fun afterInitialization ()V public fun animationFrame ()V - public fun charType (Lgg/essential/elementa/events/UICharEvent;)V + public final fun charTyped (Lgg/essential/elementa/events/UICharEvent;)Z public fun draw (Lgg/essential/universal/UMatrixStack;)V public final fun drawFloatingComponents ()V public final fun drawFloatingComponents (Lgg/essential/universal/UMatrixStack;)V @@ -790,8 +786,8 @@ public final class gg/essential/elementa/components/Window : gg/essential/elemen public fun hitTest (FF)Lgg/essential/elementa/UIComponent; public final fun invalidateCachedConstraints ()V public final fun isAreaVisible (DDDD)Z + public final fun keyPressed (Lgg/essential/elementa/events/UIKeyEvent;)Z public fun keyType (CI)V - public fun keyType (Lgg/essential/elementa/events/UIKeyEvent;)V public fun mouseClick (DDI)V public fun mouseRelease ()V public fun mouseScroll (D)V @@ -2733,10 +2729,10 @@ public final class gg/essential/elementa/effects/StencilEffect$Companion { public final fun enableStencil ()V } -public final class gg/essential/elementa/events/UICharEvent : gg/essential/elementa/events/UIEvent { +public final class gg/essential/elementa/events/UICharEvent { public fun (I)V - public final fun getChar ()C public final fun getCodepoint ()I + public final fun getString ()Ljava/lang/String; } public final class gg/essential/elementa/events/UIClickEvent : gg/essential/elementa/events/UIEvent { @@ -2766,16 +2762,15 @@ public abstract class gg/essential/elementa/events/UIEvent { public fun ()V public final fun getPropagationStopped ()Z public final fun getPropagationStoppedImmediately ()Z - public final fun isStopped ()Z public final fun setPropagationStopped (Z)V public final fun setPropagationStoppedImmediately (Z)V public final fun stopImmediatePropagation ()V public final fun stopPropagation ()V } -public final class gg/essential/elementa/events/UIKeyEvent : gg/essential/elementa/events/UIEvent { +public final class gg/essential/elementa/events/UIKeyEvent { public fun (IILgg/essential/universal/UKeyboard$Modifiers;)V - public final fun getKeyCode ()I + public final fun getKey ()I public final fun getModifiers ()Lgg/essential/universal/UKeyboard$Modifiers; public final fun getScanCode ()I } From 299cb80e381ccc4b832e7739cadc587398753429 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 15:29:32 +1000 Subject: [PATCH 24/43] fix diff --- .../main/kotlin/gg/essential/elementa/unstable/util/focusable.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt b/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt index 30b8d0c3..2b619bbc 100644 --- a/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt +++ b/unstable/layoutdsl/src/main/kotlin/gg/essential/elementa/unstable/util/focusable.kt @@ -61,7 +61,6 @@ private fun UIComponent.setupKeyboardNavigation(): UIComponent.(Char, Int) -> Un } } - @Suppress("DEPRECATION") onKeyType(keyListener) return keyListener From df772bcd079453efc9f8320ff2b48424e20099a6 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 16:01:36 +1000 Subject: [PATCH 25/43] implement new listeners in AbstractTextInput --- .../components/input/AbstractTextInput.kt | 66 ++++++++++++++++--- 1 file changed, 56 insertions(+), 10 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 7ee45b4a..3791316d 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -81,36 +81,60 @@ abstract class AbstractTextInput( setHeight(lineHeight.pixels()) - @Suppress("DEPRECATION") - onKeyType { typedChar, keyCode -> - if (!active) return@onKeyType + @Deprecated("See [ElementaVersion.V12]") + fun handleChar(typedChar: Char): Boolean { + if (isAllowedCharacter(typedChar)) { // Most of the ASCII characters + commitTextAddition(typedChar.toString()) + return true + } + return false + } + + // Only used when ElementaVersion >= V12 + fun handleCodePoint(codepoint: Int): Boolean { + // Replicated from old input check + if (codepoint != 167 && codepoint >= ' '.code && codepoint != 127) { + commitTextAddition(String(Character.toChars(codepoint))) + return true + } + return false + } + + // Return value only used when ElementaVersion >= V12 + fun handleKeyCode(keyCode: Int): Boolean { + if (!active) return false if (keyCode == UKeyboard.KEY_ESCAPE) { releaseWindowFocus() + return true } else if (UKeyboard.isKeyComboCtrlA(keyCode)) { selectAll() + return true } else if (UKeyboard.isKeyComboCtrlC(keyCode) && hasSelection()) { copySelection() + return true } else if (UKeyboard.isKeyComboCtrlX(keyCode) && hasSelection()) { copySelection() deleteSelection() + return true } else if (UKeyboard.isKeyComboCtrlV(keyCode)) { commitTextAddition(UDesktop.getClipboardString()) + return true } else if (UKeyboard.isKeyComboCtrlZ(keyCode)) { if (undoStack.isEmpty()) - return@onKeyType + return false val operationToUndo = undoStack.pop() operationToUndo.undo() redoStack.push(operationToUndo) + return true } else if (UKeyboard.isKeyComboCtrlShiftZ(keyCode) || UKeyboard.isKeyComboCtrlY(keyCode)) { if (redoStack.isEmpty()) - return@onKeyType + return false val operationToRedo = redoStack.pop() operationToRedo.redo() undoStack.push(operationToRedo) - } else if (isAllowedCharacter(typedChar)) { // Most of the ASCII characters - commitTextAddition(typedChar.toString()) - } else if (keyCode == UKeyboard.KEY_LEFT) { + return true + }else if (keyCode == UKeyboard.KEY_LEFT) { val holdingShift = UKeyboard.isShiftKeyDown() val holdingCtrl = UKeyboard.isCtrlKeyDown() @@ -122,11 +146,12 @@ abstract class AbstractTextInput( if (!holdingShift) { setCursorPosition(newCursorPosition) - return@onKeyType + return false } cursor = newCursorPosition cursorNeedsRefocus = true + return true } else if (keyCode == UKeyboard.KEY_RIGHT) { val holdingShift = UKeyboard.isShiftKeyDown() val holdingCtrl = UKeyboard.isCtrlKeyDown() @@ -139,11 +164,12 @@ abstract class AbstractTextInput( if (!holdingShift) { setCursorPosition(newCursorPosition) - return@onKeyType + return false } cursor = newCursorPosition cursorNeedsRefocus = true + return true } else if (keyCode == UKeyboard.KEY_UP) { val newVisualPos = if (cursor.line == 0) { LinePosition(0, 0, isVisual = true) @@ -158,6 +184,7 @@ abstract class AbstractTextInput( } else { setCursorPosition(newVisualPos) } + return true } else if (keyCode == UKeyboard.KEY_DOWN) { val newVisualPos = if (cursor.line == visualLines.lastIndex) { LinePosition(visualLines.lastIndex, visualLines.last().length, isVisual = true) @@ -172,6 +199,7 @@ abstract class AbstractTextInput( } else { setCursorPosition(newVisualPos) } + return true } else if (keyCode == UKeyboard.KEY_BACKSPACE) { if (hasSelection()) { deleteSelection() @@ -183,6 +211,7 @@ abstract class AbstractTextInput( commitTextRemoval(startPos, endPos, selectAfterUndo = false) } + return true } else if (keyCode == UKeyboard.KEY_DELETE) { if (hasSelection()) { deleteSelection() @@ -194,6 +223,7 @@ abstract class AbstractTextInput( commitTextRemoval(startPos, endPos, selectAfterUndo = false) } + return true } else if (keyCode == UKeyboard.KEY_HOME) { if (UKeyboard.isShiftKeyDown()) { cursor = cursor.withColumn(0) @@ -201,6 +231,7 @@ abstract class AbstractTextInput( } else { setCursorPosition(cursor.withColumn(0)) } + return true } else if (keyCode == UKeyboard.KEY_END) { cursor.withColumn(visualLines[cursor.line].length).also { if (UKeyboard.isShiftKeyDown()) { @@ -210,11 +241,26 @@ abstract class AbstractTextInput( setCursorPosition(it) } } + return true } else if (keyCode == UKeyboard.KEY_ENTER) { // Enter onEnterPressed() + return true } + + return false } + // Pre ElementaVersion.V12 function + @Suppress("DEPRECATION") + onKeyType { char, key -> + handleKeyCode(key) + handleChar(char) + } + + // ElementaVersion.V12+ functions + onCharTyped { charEvent -> handleCodePoint(charEvent.codepoint) } + onKeyPressed { keyEvent -> handleKeyCode(keyEvent.key) } + onMouseScroll { val heightDifference = getHeight() - visualLines.size * lineHeight if (heightDifference > 0) From c9e2864c5d195b047f6772fe11124bee7cf8d7e8 Mon Sep 17 00:00:00 2001 From: Traben Date: Mon, 6 Apr 2026 16:06:16 +1000 Subject: [PATCH 26/43] implement new listeners in MarkDownComponent --- .../essential/elementa/markdown/MarkdownComponent.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt index 33a33ce4..28966475 100644 --- a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt @@ -110,12 +110,22 @@ class MarkdownComponent( grabWindowFocus() } + // Pre ElementaVersion.V12 function @Suppress("DEPRECATION") onKeyType { _, keyCode -> if (selection != null && keyCode == UKeyboard.KEY_C && UKeyboard.isCtrlKeyDown()) { UDesktop.setClipboardString(drawables.selectedText(UKeyboard.isShiftKeyDown())) } } + + // ElementaVersion.V12+ function + onKeyPressed { keyEvent -> + if (selection != null && keyEvent.key == UKeyboard.KEY_C && keyEvent.modifiers.isCtrl) { + UDesktop.setClipboardString(drawables.selectedText(keyEvent.modifiers.isShift)) + return@onKeyPressed true + } + return@onKeyPressed false + } } configState.onSetValue { reparse() From 4ab4828541805b4e3ab07705397bcbe9a12919fd Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:22:19 +1000 Subject: [PATCH 27/43] Review: drop functions in favour of directly modifying the listener lists --- api/Elementa.api | 6 ++---- .../gg/essential/elementa/UIComponent.kt | 18 +++++------------- .../gg/essential/elementa/WindowScreen.kt | 4 ++-- .../gg/essential/elementa/components/Window.kt | 8 ++++---- .../components/input/AbstractTextInput.kt | 4 ++-- .../elementa/markdown/MarkdownComponent.kt | 6 +++--- 6 files changed, 18 insertions(+), 28 deletions(-) diff --git a/api/Elementa.api b/api/Elementa.api index 6ef01a5c..643186e4 100644 --- a/api/Elementa.api +++ b/api/Elementa.api @@ -71,7 +71,6 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, protected final fun fireScrollEvent (Lgg/essential/elementa/events/UIScrollEvent;)V public final fun focus ()V public fun getBottom ()F - public final fun getCharTypedEventListeners ()Ljava/util/List; public fun getChildren ()Lgg/essential/elementa/utils/ObservableList; public fun getColor ()Ljava/awt/Color; public final fun getComponentName ()Ljava/lang/String; @@ -80,7 +79,6 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun getFontProvider ()Lgg/essential/elementa/font/FontProvider; public fun getHasParent ()Z public fun getHeight ()F - public final fun getKeyPressedEventListeners ()Ljava/util/List; public final fun getKeyTypedListeners ()Ljava/util/List; public final fun getLastDraggedMouseX ()Ljava/lang/Double; public final fun getLastDraggedMouseY ()Ljava/lang/Double; @@ -92,6 +90,8 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, protected final fun getMousePosition ()Lkotlin/Pair; public final fun getMouseReleaseListeners ()Ljava/util/List; public final fun getMouseScrollListeners ()Ljava/util/List; + public final fun getOnCharTyped ()Ljava/util/List; + public final fun getOnKeyPressed ()Ljava/util/List; public fun getParent ()Lgg/essential/elementa/UIComponent; public fun getRadius ()F public fun getRight ()F @@ -123,10 +123,8 @@ public abstract class gg/essential/elementa/UIComponent : java/util/Observable, public fun mouseRelease ()V public fun mouseScroll (D)V public fun mouseScroll (DD)V - public final fun onCharTyped (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; public final fun onFocus (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/UIComponent; public final fun onFocusLost (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/UIComponent; - public final fun onKeyPressed (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; public final fun onKeyType (Lkotlin/jvm/functions/Function3;)Lgg/essential/elementa/UIComponent; public final fun onKeyTypeConsumer (Ljava/util/function/BiConsumer;)V public final fun onMouseClick (Lkotlin/jvm/functions/Function2;)Lgg/essential/elementa/UIComponent; diff --git a/src/main/kotlin/gg/essential/elementa/UIComponent.kt b/src/main/kotlin/gg/essential/elementa/UIComponent.kt index d3d4a64e..1677e542 100644 --- a/src/main/kotlin/gg/essential/elementa/UIComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/UIComponent.kt @@ -121,12 +121,12 @@ abstract class UIComponent : Observable(), ReferenceHolder { get() = field.also { ownFlags += Flags.RequiresMouseMove } val mouseDragListeners = mutableListOf Unit>() get() = field.also { ownFlags += Flags.RequiresMouseDrag } - @Deprecated("See [ElementaVersion.V12].") + @Deprecated("See [ElementaVersion.V12]. Replaced by either the `onKeyPressed` or `onCharTyped` listener lists.") val keyTypedListeners = mutableListOf Unit>() // Requires ElementaVersion.V12 - val keyPressedEventListeners = mutableListOf Boolean>() + val onKeyPressed = mutableListOf Boolean>() // Requires ElementaVersion.V12 - val charTypedEventListeners = mutableListOf Boolean>() + val onCharTyped = mutableListOf Boolean>() private var currentlyHovered = false private val beforeHideAnimations = mutableListOf Unit>() @@ -1030,21 +1030,13 @@ abstract class UIComponent : Observable(), ReferenceHolder { mouseScrollListeners.add { method.accept(it) } } - @Deprecated("See [ElementaVersion.V12].") + @Deprecated("See [ElementaVersion.V12]. Replaced by either the `onKeyPressed` or `onCharTyped` listener lists.") fun onKeyType(method: UIComponent.(typedChar: Char, keyCode: Int) -> Unit) = apply { @Suppress("DEPRECATION") keyTypedListeners.add(method) } - fun onKeyPressed(method: UIComponent.(keyEvent: UIKeyEvent) -> Boolean) = apply { - keyPressedEventListeners.add(method) - } - - fun onCharTyped(method: UIComponent.(keyEvent: UICharEvent) -> Boolean) = apply { - charTypedEventListeners.add(method) - } - - @Deprecated("See [ElementaVersion.V12].") + @Deprecated("See [ElementaVersion.V12]. Replaced by either the `onKeyPressed` or `onCharTyped` listener lists.") fun onKeyTypeConsumer(method: BiConsumer) { @Suppress("DEPRECATION") keyTypedListeners.add { t: Char, u: Int -> method.accept(t, u) } diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index e8de23d3..3bc9d1f7 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -38,10 +38,10 @@ abstract class WindowScreen @JvmOverloads constructor( init { if (version >= ElementaVersion.v12) { - window.onKeyPressed { keyEvent -> + window.onKeyPressed.add { keyEvent -> super.uKeyPressed(keyEvent.key, keyEvent.scanCode, keyEvent.modifiers) } - window.onCharTyped { charEvent -> + window.onCharTyped.add { charEvent -> super.uCharTyped(charEvent.codepoint) } } else { diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 539ba378..6250b88f 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -358,12 +358,12 @@ class Window @JvmOverloads constructor( requireMainThread() focusedComponent?.run { - for (listener in keyPressedEventListeners) { + for (listener in onKeyPressed) { if (listener(keyEvent)) return true } } - for (listener in keyPressedEventListeners) { + for (listener in onKeyPressed) { if (listener(keyEvent)) return true } @@ -377,12 +377,12 @@ class Window @JvmOverloads constructor( requireMainThread() focusedComponent?.run { - for (listener in charTypedEventListeners) { + for (listener in onCharTyped) { if (listener(charEvent)) return true } } - for (listener in charTypedEventListeners) { + for (listener in onCharTyped) { if (listener(charEvent)) return true } diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 3791316d..7f72f077 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -258,8 +258,8 @@ abstract class AbstractTextInput( } // ElementaVersion.V12+ functions - onCharTyped { charEvent -> handleCodePoint(charEvent.codepoint) } - onKeyPressed { keyEvent -> handleKeyCode(keyEvent.key) } + onCharTyped.add { charEvent -> handleCodePoint(charEvent.codepoint) } + onKeyPressed.add { keyEvent -> handleKeyCode(keyEvent.key) } onMouseScroll { val heightDifference = getHeight() - visualLines.size * lineHeight diff --git a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt index 28966475..ab165ed7 100644 --- a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt @@ -119,12 +119,12 @@ class MarkdownComponent( } // ElementaVersion.V12+ function - onKeyPressed { keyEvent -> + onKeyPressed.add { keyEvent -> if (selection != null && keyEvent.key == UKeyboard.KEY_C && keyEvent.modifiers.isCtrl) { UDesktop.setClipboardString(drawables.selectedText(keyEvent.modifiers.isShift)) - return@onKeyPressed true + return@add true } - return@onKeyPressed false + return@add false } } configState.onSetValue { From 413d6ae263ff0fde6f762cd22e2649d31c4f70d0 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:25:34 +1000 Subject: [PATCH 28/43] Review: OVERRIDE_DEPRECATION -> actual deprecation message --- src/main/kotlin/gg/essential/elementa/WindowScreen.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index 3bc9d1f7..a3830e5c 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -107,7 +107,8 @@ abstract class WindowScreen @JvmOverloads constructor( return false // TODO returning implementation, see uKeyPressed() } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") + @Deprecated("See [ElementaVersion.V12]") + @Suppress("DEPRECATION") override fun onMouseReleased(mouseX: Double, mouseY: Double, state: Int) { super.onMouseReleased(mouseX, mouseY, state) @@ -139,7 +140,8 @@ abstract class WindowScreen @JvmOverloads constructor( } } - @Suppress("DEPRECATION", "OVERRIDE_DEPRECATION") + @Deprecated("See [ElementaVersion.V12]") + @Suppress("DEPRECATION") override fun onMouseScrolled(mouseX: Double, mouseY: Double, deltaHorizontal: Double, deltaVertical: Double) { super.onMouseScrolled(mouseX, mouseY, deltaHorizontal, deltaVertical) From 952c49dcc90044804cc52c7897514d10f2065586 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:29:22 +1000 Subject: [PATCH 29/43] Review: remove use of `run { }` --- .../gg/essential/elementa/components/Window.kt | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/Window.kt b/src/main/kotlin/gg/essential/elementa/components/Window.kt index 6250b88f..6a54f5d4 100644 --- a/src/main/kotlin/gg/essential/elementa/components/Window.kt +++ b/src/main/kotlin/gg/essential/elementa/components/Window.kt @@ -357,12 +357,11 @@ class Window @JvmOverloads constructor( requireMainThread() - focusedComponent?.run { - for (listener in onKeyPressed) { - if (listener(keyEvent)) return true - } + for (listener in focusedComponent?.onKeyPressed ?: emptyList()) { + if (listener(keyEvent)) return true } + for (listener in onKeyPressed) { if (listener(keyEvent)) return true } @@ -376,10 +375,8 @@ class Window @JvmOverloads constructor( requireMainThread() - focusedComponent?.run { - for (listener in onCharTyped) { - if (listener(charEvent)) return true - } + for (listener in focusedComponent?.onCharTyped ?: emptyList()) { + if (listener(charEvent)) return true } for (listener in onCharTyped) { From 1412578b0769a79070c245103925da2c9d91c775 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:30:39 +1000 Subject: [PATCH 30/43] Review: uSuperInputHandler() -> super --- src/main/kotlin/gg/essential/elementa/WindowScreen.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt index a3830e5c..c138522d 100644 --- a/src/main/kotlin/gg/essential/elementa/WindowScreen.kt +++ b/src/main/kotlin/gg/essential/elementa/WindowScreen.kt @@ -99,7 +99,7 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uMouseClicked(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { - uSuperInputHandler().uMouseClicked(x, y, button, modifiers) + super.uMouseClicked(x, y, button, modifiers) // We also need to pass along clicks window.mouseClick(x, y, button) @@ -118,7 +118,7 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uMouseReleased(x: Double, y: Double, button: Int, modifiers: UKeyboard.Modifiers): Boolean { - uSuperInputHandler().uMouseReleased(x, y, button, modifiers) + super.uMouseReleased(x, y, button, modifiers) // We also need to pass along mouse releases window.mouseRelease() @@ -152,7 +152,7 @@ abstract class WindowScreen @JvmOverloads constructor( // Called only when ElementaVersion >= V12 override fun uMouseScrolled(x: Double, y: Double, scrollX: Double, scrollY: Double): Boolean { - uSuperInputHandler().uMouseScrolled(x, y, scrollX, scrollY) + super.uMouseScrolled(x, y, scrollX, scrollY) window.mouseScroll(scrollX, scrollY) From 1f717a0178f5d23b0c5c6778bf5e3d8810e3a3c7 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:31:43 +1000 Subject: [PATCH 31/43] Review: remove redundant checks --- .../gg/essential/elementa/components/input/AbstractTextInput.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 7f72f077..c7b6035f 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -93,7 +93,7 @@ abstract class AbstractTextInput( // Only used when ElementaVersion >= V12 fun handleCodePoint(codepoint: Int): Boolean { // Replicated from old input check - if (codepoint != 167 && codepoint >= ' '.code && codepoint != 127) { + if (codepoint != 167) { commitTextAddition(String(Character.toChars(codepoint))) return true } From 5f6f1a67f4d1f229822dd5336eef59480579de85 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:32:07 +1000 Subject: [PATCH 32/43] Review: space --- .../gg/essential/elementa/components/input/AbstractTextInput.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index c7b6035f..11ddca05 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -134,7 +134,7 @@ abstract class AbstractTextInput( operationToRedo.redo() undoStack.push(operationToRedo) return true - }else if (keyCode == UKeyboard.KEY_LEFT) { + } else if (keyCode == UKeyboard.KEY_LEFT) { val holdingShift = UKeyboard.isShiftKeyDown() val holdingCtrl = UKeyboard.isCtrlKeyDown() From 41039e1c379976953cbadf7cbd18f50fc158e89e Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:33:08 +1000 Subject: [PATCH 33/43] Review: return true --- .../essential/elementa/components/input/AbstractTextInput.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 11ddca05..5404e0ee 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -146,7 +146,7 @@ abstract class AbstractTextInput( if (!holdingShift) { setCursorPosition(newCursorPosition) - return false + return true } cursor = newCursorPosition @@ -164,7 +164,7 @@ abstract class AbstractTextInput( if (!holdingShift) { setCursorPosition(newCursorPosition) - return false + return true } cursor = newCursorPosition From d749d5cc9b18b56f7c89d2be0068c5cf624aff01 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:35:02 +1000 Subject: [PATCH 34/43] Review: call handleChar only if keycode wasn't handled --- .../essential/elementa/components/input/AbstractTextInput.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 5404e0ee..47d49def 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -253,8 +253,7 @@ abstract class AbstractTextInput( // Pre ElementaVersion.V12 function @Suppress("DEPRECATION") onKeyType { char, key -> - handleKeyCode(key) - handleChar(char) + handleKeyCode(key) || handleChar(char) } // ElementaVersion.V12+ functions From d6b9656d5cec367043f6f3092e0b33372cf6ec83 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:35:49 +1000 Subject: [PATCH 35/43] Review: missing condition --- .../essential/elementa/components/input/AbstractTextInput.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 47d49def..3b06eccb 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -83,6 +83,8 @@ abstract class AbstractTextInput( @Deprecated("See [ElementaVersion.V12]") fun handleChar(typedChar: Char): Boolean { + if (!active) return false + if (isAllowedCharacter(typedChar)) { // Most of the ASCII characters commitTextAddition(typedChar.toString()) return true @@ -92,6 +94,8 @@ abstract class AbstractTextInput( // Only used when ElementaVersion >= V12 fun handleCodePoint(codepoint: Int): Boolean { + if (!active) return false + // Replicated from old input check if (codepoint != 167) { commitTextAddition(String(Character.toChars(codepoint))) From aa2a4d8379962c6136c060cd4b761d6c5323caaa Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:40:09 +1000 Subject: [PATCH 36/43] Review: fixup docs --- src/main/kotlin/gg/essential/elementa/ElementaVersion.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt index 886ec1ef..c3205481 100644 --- a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt +++ b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt @@ -219,8 +219,8 @@ enum class ElementaVersion { * `keyType(typedChar: Char, keyCode: Int)` in [Window] has been split into: * - keyPressed(keyEvent: UIKeyEvent) * - charTyped(charEvent: UICharEvent) - * As well as all related setup functions. E.G. `onKeyType { }` - * For now any key listeners setup via the old methods will still be called. + * With the backing list for the new listeners now requiring manual modification to set up. + * E.G. `UIComponent.onKeyPressed.add { }` & `UIComponent.onCharTyped.add { }` replace the old `UIComponent.onKeyType { }` * * Also see [gg.essential.elementa.events.UIKeyEvent] & [gg.essential.elementa.events.UICharEvent]. * From 443e7ba3e4f133651ea00ef8693aef9cf1dae4ff Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:54:16 +1000 Subject: [PATCH 37/43] Review: add isCopy, isCut, and isPaste helper functions to UIKeyEvent --- .../gg/essential/elementa/events/UIKeyboardEvents.kt | 11 ++++++++++- .../essential/elementa/markdown/MarkdownComponent.kt | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt index 0f8d65ca..c155e032 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -11,7 +11,16 @@ class UIKeyEvent( val key: Int, val scanCode: Int, val modifiers: UKeyboard.Modifiers, -) +) { + + // These checks read the current keyboard state to account for OS differences where we cannot fully rely on `modifiers`. + // E.G. macOS using CMD + C for copy + val isCopy get() = UKeyboard.isKeyComboCtrlC(key) + @Suppress("unused") + val isCut get() = UKeyboard.isKeyComboCtrlX(key) + @Suppress("unused") + val isPaste get() = UKeyboard.isKeyComboCtrlV(key) +} /** diff --git a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt index ab165ed7..f9437aab 100644 --- a/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/markdown/MarkdownComponent.kt @@ -120,7 +120,7 @@ class MarkdownComponent( // ElementaVersion.V12+ function onKeyPressed.add { keyEvent -> - if (selection != null && keyEvent.key == UKeyboard.KEY_C && keyEvent.modifiers.isCtrl) { + if (selection != null && keyEvent.isCopy) { UDesktop.setClipboardString(drawables.selectedText(keyEvent.modifiers.isShift)) return@add true } From 7b7264cb3e930c306495c23f0804c94836939bb4 Mon Sep 17 00:00:00 2001 From: Traben Date: Thu, 9 Apr 2026 12:54:56 +1000 Subject: [PATCH 38/43] API --- api/Elementa.api | 3 +++ 1 file changed, 3 insertions(+) diff --git a/api/Elementa.api b/api/Elementa.api index 643186e4..f225c31a 100644 --- a/api/Elementa.api +++ b/api/Elementa.api @@ -2771,6 +2771,9 @@ public final class gg/essential/elementa/events/UIKeyEvent { public final fun getKey ()I public final fun getModifiers ()Lgg/essential/universal/UKeyboard$Modifiers; public final fun getScanCode ()I + public final fun isCopy ()Z + public final fun isCut ()Z + public final fun isPaste ()Z } public final class gg/essential/elementa/events/UIScrollEvent : gg/essential/elementa/events/UIEvent { From 304a3dd2ff55dd11310c60457ba244616eb72968 Mon Sep 17 00:00:00 2001 From: Traben Date: Sun, 26 Apr 2026 19:50:42 +1000 Subject: [PATCH 39/43] Review: remove suppressions --- .../kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt index c155e032..d6189917 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -16,9 +16,7 @@ class UIKeyEvent( // These checks read the current keyboard state to account for OS differences where we cannot fully rely on `modifiers`. // E.G. macOS using CMD + C for copy val isCopy get() = UKeyboard.isKeyComboCtrlC(key) - @Suppress("unused") val isCut get() = UKeyboard.isKeyComboCtrlX(key) - @Suppress("unused") val isPaste get() = UKeyboard.isKeyComboCtrlV(key) } From 849f4941a5da5656ceadb033684f83e694a8f0b5 Mon Sep 17 00:00:00 2001 From: Traben Date: Sun, 26 Apr 2026 20:22:26 +1000 Subject: [PATCH 40/43] bump UC --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b5e2ef15..81b7e7d8 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -2,7 +2,7 @@ kotlin = "1.9.25" kotlinx-coroutines = "1.5.2" jetbrains-annotations = "23.0.0" -universalcraft = "474+feature-em-1645-closing-uscreen-via-esc-on-119-puts-you-in-the-menu-rather" +universalcraft = "485+feature-em-1645-closing-uscreen-via-esc-on-119-puts-you-in-the-menu-rather" commonmark = "0.17.1" dom4j = "2.1.1" From 49d637e50408614d85fabf0397efeb90850d166e Mon Sep 17 00:00:00 2001 From: Traben Date: Sun, 26 Apr 2026 20:23:19 +1000 Subject: [PATCH 41/43] implement UC modifier changes --- .../components/input/AbstractTextInput.kt | 46 ++++++++++--------- .../components/input/UIMultilineTextInput.kt | 4 +- .../elementa/components/input/UITextInput.kt | 3 +- .../elementa/events/UIKeyboardEvents.kt | 9 ++-- 4 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt index 3b06eccb..3c5bccfe 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/AbstractTextInput.kt @@ -105,33 +105,35 @@ abstract class AbstractTextInput( } // Return value only used when ElementaVersion >= V12 - fun handleKeyCode(keyCode: Int): Boolean { + fun handleKeyCode(keyCode: Int, modifiers: UKeyboard.Modifiers): Boolean { if (!active) return false if (keyCode == UKeyboard.KEY_ESCAPE) { releaseWindowFocus() return true - } else if (UKeyboard.isKeyComboCtrlA(keyCode)) { + } else if (keyCode == UKeyboard.KEY_A && modifiers.isOnlyPlatformModifierActive()) { selectAll() return true - } else if (UKeyboard.isKeyComboCtrlC(keyCode) && hasSelection()) { + } else if (keyCode == UKeyboard.KEY_C && modifiers.isOnlyPlatformModifierActive() && hasSelection()) { copySelection() return true - } else if (UKeyboard.isKeyComboCtrlX(keyCode) && hasSelection()) { + } else if (keyCode == UKeyboard.KEY_X && modifiers.isOnlyPlatformModifierActive() && hasSelection()) { copySelection() deleteSelection() return true - } else if (UKeyboard.isKeyComboCtrlV(keyCode)) { + } else if (keyCode == UKeyboard.KEY_V && modifiers.isOnlyPlatformModifierActive()) { commitTextAddition(UDesktop.getClipboardString()) return true - } else if (UKeyboard.isKeyComboCtrlZ(keyCode)) { + } else if (keyCode == UKeyboard.KEY_Z && modifiers.isOnlyPlatformModifierActive()) { if (undoStack.isEmpty()) return false val operationToUndo = undoStack.pop() operationToUndo.undo() redoStack.push(operationToUndo) return true - } else if (UKeyboard.isKeyComboCtrlShiftZ(keyCode) || UKeyboard.isKeyComboCtrlY(keyCode)) { + } else if ((keyCode == UKeyboard.KEY_Z && modifiers.isPlatformModifierActive() && modifiers.isShift && !modifiers.isAlt) + || (keyCode == UKeyboard.KEY_Y && modifiers.isOnlyPlatformModifierActive()) + ) { if (redoStack.isEmpty()) return false val operationToRedo = redoStack.pop() @@ -139,8 +141,8 @@ abstract class AbstractTextInput( undoStack.push(operationToRedo) return true } else if (keyCode == UKeyboard.KEY_LEFT) { - val holdingShift = UKeyboard.isShiftKeyDown() - val holdingCtrl = UKeyboard.isCtrlKeyDown() + val holdingShift = modifiers.isShift + val holdingCtrl = modifiers.isCtrl val newCursorPosition = when { holdingCtrl -> getNearestWordBoundary(cursor, Direction.Left) @@ -157,8 +159,8 @@ abstract class AbstractTextInput( cursorNeedsRefocus = true return true } else if (keyCode == UKeyboard.KEY_RIGHT) { - val holdingShift = UKeyboard.isShiftKeyDown() - val holdingCtrl = UKeyboard.isCtrlKeyDown() + val holdingShift = modifiers.isShift + val holdingCtrl = modifiers.isCtrl val newCursorPosition = when { holdingCtrl -> getNearestWordBoundary(cursor, Direction.Right) @@ -182,7 +184,7 @@ abstract class AbstractTextInput( screenPosToVisualPos(currX, currY - lineHeight) } - if (UKeyboard.isShiftKeyDown()) { + if (modifiers.isShift) { cursor = newVisualPos cursorNeedsRefocus = true } else { @@ -197,7 +199,7 @@ abstract class AbstractTextInput( screenPosToVisualPos(currX, currY + lineHeight) } - if (UKeyboard.isShiftKeyDown()) { + if (modifiers.isShift) { cursor = newVisualPos cursorNeedsRefocus = true } else { @@ -208,7 +210,7 @@ abstract class AbstractTextInput( if (hasSelection()) { deleteSelection() } else if (!cursor.isAtAbsoluteStart) { - val startPos = if (UKeyboard.isCtrlKeyDown()) { + val startPos = if (modifiers.isCtrl) { getNearestWordBoundary(cursor, Direction.Left) } else cursor.offsetColumn(-1).toTextualPos() val endPos = cursor.toTextualPos() @@ -221,7 +223,7 @@ abstract class AbstractTextInput( deleteSelection() } else if (!cursor.isAtAbsoluteEnd) { val startPos = cursor.toTextualPos() - val endPos = if (UKeyboard.isCtrlKeyDown()) { + val endPos = if (modifiers.isCtrl) { getNearestWordBoundary(cursor, Direction.Right) } else cursor.offsetColumn(1).toTextualPos() @@ -229,7 +231,7 @@ abstract class AbstractTextInput( } return true } else if (keyCode == UKeyboard.KEY_HOME) { - if (UKeyboard.isShiftKeyDown()) { + if (modifiers.isShift) { cursor = cursor.withColumn(0) cursorNeedsRefocus = true } else { @@ -238,7 +240,7 @@ abstract class AbstractTextInput( return true } else if (keyCode == UKeyboard.KEY_END) { cursor.withColumn(visualLines[cursor.line].length).also { - if (UKeyboard.isShiftKeyDown()) { + if (modifiers.isShift) { cursor = it cursorNeedsRefocus = true } else { @@ -247,7 +249,7 @@ abstract class AbstractTextInput( } return true } else if (keyCode == UKeyboard.KEY_ENTER) { // Enter - onEnterPressed() + onEnterPressed(modifiers) return true } @@ -257,12 +259,12 @@ abstract class AbstractTextInput( // Pre ElementaVersion.V12 function @Suppress("DEPRECATION") onKeyType { char, key -> - handleKeyCode(key) || handleChar(char) + handleKeyCode(key, UKeyboard.getKeyModifiers()) || handleChar(char) } // ElementaVersion.V12+ functions onCharTyped.add { charEvent -> handleCodePoint(charEvent.codepoint) } - onKeyPressed.add { keyEvent -> handleKeyCode(keyEvent.key) } + onKeyPressed.add { keyEvent -> handleKeyCode(keyEvent.key, keyEvent.modifiers) } onMouseScroll { val heightDifference = getHeight() - visualLines.size * lineHeight @@ -428,7 +430,9 @@ abstract class AbstractTextInput( protected abstract fun screenPosToVisualPos(x: Float, y: Float): LinePosition protected abstract fun recalculateDimensions() protected abstract fun textToLines(text: String): List - protected abstract fun onEnterPressed() + @Deprecated("Inconsistent with key input event modifiers.", replaceWith = ReplaceWith("onEnterPressed(modifiers)")) + protected open fun onEnterPressed() = onEnterPressed(UKeyboard.getKeyModifiers()) + protected abstract fun onEnterPressed(modifiers: UKeyboard.Modifiers) fun setActive(isActive: Boolean) = apply { active = isActive diff --git a/src/main/kotlin/gg/essential/elementa/components/input/UIMultilineTextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/UIMultilineTextInput.kt index 41670ecd..1fda414a 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/UIMultilineTextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/UIMultilineTextInput.kt @@ -63,8 +63,8 @@ class UIMultilineTextInput @JvmOverloads constructor( setHeight((lineHeight * visualLines.size).pixels().coerceAtMost(maxHeight!!)) } - override fun onEnterPressed() { - if (UKeyboard.isShiftKeyDown()) { + override fun onEnterPressed(modifiers: UKeyboard.Modifiers) { + if (modifiers.isShift) { commitTextAddition("\n") updateAction(getText()) } else { diff --git a/src/main/kotlin/gg/essential/elementa/components/input/UITextInput.kt b/src/main/kotlin/gg/essential/elementa/components/input/UITextInput.kt index 6bac59d1..2ceae62f 100644 --- a/src/main/kotlin/gg/essential/elementa/components/input/UITextInput.kt +++ b/src/main/kotlin/gg/essential/elementa/components/input/UITextInput.kt @@ -2,6 +2,7 @@ package gg.essential.elementa.components.input import gg.essential.elementa.constraints.WidthConstraint import gg.essential.elementa.dsl.* +import gg.essential.universal.UKeyboard import gg.essential.universal.UMatrixStack import java.awt.Color @@ -102,7 +103,7 @@ open class UITextInput @JvmOverloads constructor( return listOf(text) } - override fun onEnterPressed() { + override fun onEnterPressed(modifiers: UKeyboard.Modifiers) { activateAction(getText()) } diff --git a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt index d6189917..d003e07e 100644 --- a/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt +++ b/src/main/kotlin/gg/essential/elementa/events/UIKeyboardEvents.kt @@ -12,12 +12,9 @@ class UIKeyEvent( val scanCode: Int, val modifiers: UKeyboard.Modifiers, ) { - - // These checks read the current keyboard state to account for OS differences where we cannot fully rely on `modifiers`. - // E.G. macOS using CMD + C for copy - val isCopy get() = UKeyboard.isKeyComboCtrlC(key) - val isCut get() = UKeyboard.isKeyComboCtrlX(key) - val isPaste get() = UKeyboard.isKeyComboCtrlV(key) + val isCopy get() = key == UKeyboard.KEY_C && modifiers.isOnlyPlatformModifierActive() + val isCut get() = key == UKeyboard.KEY_X && modifiers.isOnlyPlatformModifierActive() + val isPaste get() = key == UKeyboard.KEY_V && modifiers.isOnlyPlatformModifierActive() } From 75bda3e315f3179fb6e3e62e7e070a287ec2a85d Mon Sep 17 00:00:00 2001 From: Traben Date: Sun, 26 Apr 2026 20:27:21 +1000 Subject: [PATCH 42/43] suppress deprecated UKeyboard usages and note future changes to be made --- src/main/kotlin/gg/essential/elementa/ElementaVersion.kt | 2 ++ .../kotlin/gg/essential/elementa/components/ScrollComponent.kt | 2 ++ .../gg/essential/elementa/components/TreeListComponent.kt | 1 + .../gg/essential/elementa/components/inspector/InspectorNode.kt | 1 + 4 files changed, 6 insertions(+) diff --git a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt index c3205481..e7eeabc6 100644 --- a/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt +++ b/src/main/kotlin/gg/essential/elementa/ElementaVersion.kt @@ -224,6 +224,8 @@ enum class ElementaVersion { * * Also see [gg.essential.elementa.events.UIKeyEvent] & [gg.essential.elementa.events.UICharEvent]. * + * Note: The other improved inputs provided by [UScreen.InputHandler] have not been implemented by V12. (e.g. mouse inputs) + * * See [UniversalCraft#125](https://github.com/SparkUniverse/UniversalCraft/pull/125) for the underlying changes. */ V12, diff --git a/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt b/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt index f50f698e..8ae3e41a 100644 --- a/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/components/ScrollComponent.kt @@ -144,6 +144,7 @@ class ScrollComponent constructor( private val mouseScrollLambda: UIComponent.(UIScrollEvent) -> Unit = { if (Window.of(this).version >= ElementaVersion.v5) { // new behavior + @Suppress("DEPRECATION") // Modifiers not provided for mouse sroll events val scrollDirection = if (!UKeyboard.isShiftKeyDown()) primaryScrollDirection else secondaryScrollDirection if (scrollDirection != null) { if (onScroll(it.delta.toFloat(), isHorizontal = scrollDirection == Direction.Horizontal) || !passthroughScroll) { @@ -157,6 +158,7 @@ class ScrollComponent constructor( } } else { // old behavior + @Suppress("DEPRECATION") // Modifiers not provided for mouse sroll events if (UKeyboard.isShiftKeyDown() && horizontalScrollEnabled) { onScroll(it.delta.toFloat(), isHorizontal = true) } else if (!UKeyboard.isShiftKeyDown() && verticalScrollEnabled) { diff --git a/src/main/kotlin/gg/essential/elementa/components/TreeListComponent.kt b/src/main/kotlin/gg/essential/elementa/components/TreeListComponent.kt index d811e9a2..6a396212 100644 --- a/src/main/kotlin/gg/essential/elementa/components/TreeListComponent.kt +++ b/src/main/kotlin/gg/essential/elementa/components/TreeListComponent.kt @@ -124,6 +124,7 @@ abstract class TreeNode { arrowComponent.onMouseClick { event -> event.stopImmediatePropagation() + @Suppress("DEPRECATION") //TODO pass modifiers when they become available, see [ElementaVersion.V12] val isRecursive = UKeyboard.isShiftKeyDown() if (opened) { diff --git a/src/main/kotlin/gg/essential/elementa/components/inspector/InspectorNode.kt b/src/main/kotlin/gg/essential/elementa/components/inspector/InspectorNode.kt index 71876b8e..de67dfbc 100644 --- a/src/main/kotlin/gg/essential/elementa/components/inspector/InspectorNode.kt +++ b/src/main/kotlin/gg/essential/elementa/components/inspector/InspectorNode.kt @@ -50,6 +50,7 @@ class InspectorNode(private val inspector: Inspector, val targetComponent: UICom event.stopImmediatePropagation() toggleSelection() }.onMouseScroll { event -> + @Suppress("DEPRECATION") // Modifiers not provided for mouse sroll events if (!UKeyboard.isShiftKeyDown()) return@onMouseScroll event.stopImmediatePropagation() inspector.scrollSource(this@InspectorNode, event.delta > 0) From a37641e14d43babf900ac852b338683b24883f69 Mon Sep 17 00:00:00 2001 From: Traben Date: Sun, 26 Apr 2026 20:42:56 +1000 Subject: [PATCH 43/43] api changes --- api/Elementa.api | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/api/Elementa.api b/api/Elementa.api index f225c31a..75dab717 100644 --- a/api/Elementa.api +++ b/api/Elementa.api @@ -948,7 +948,8 @@ public abstract class gg/essential/elementa/components/input/AbstractTextInput : public final fun isActive ()Z protected fun isBreakingCharacter (C)Z public final fun onActivate (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/components/input/AbstractTextInput; - protected abstract fun onEnterPressed ()V + protected fun onEnterPressed ()V + protected abstract fun onEnterPressed (Lgg/essential/universal/UKeyboard$Modifiers;)V public final fun onUpdate (Lkotlin/jvm/functions/Function1;)Lgg/essential/elementa/components/input/AbstractTextInput; protected fun recalculateAllVisualLines ()V protected abstract fun recalculateDimensions ()V @@ -1129,7 +1130,7 @@ public class gg/essential/elementa/components/input/UITextInput : gg/essential/e protected final fun getPlaceholderWidth ()F public fun getText ()Ljava/lang/String; protected fun getTextForRender ()Ljava/lang/String; - protected fun onEnterPressed ()V + protected fun onEnterPressed (Lgg/essential/universal/UKeyboard$Modifiers;)V protected fun recalculateDimensions ()V protected fun screenPosToVisualPos (FF)Lgg/essential/elementa/components/input/AbstractTextInput$LinePosition; protected fun scrollIntoView (Lgg/essential/elementa/components/input/AbstractTextInput$LinePosition;)V