mirror of
https://gitlab.bixilon.de/bixilon/minosoft.git
synced 2025-09-19 04:15:14 -04:00
input handler tests, fix modifier key action
This commit is contained in:
parent
745d982fa0
commit
1c6f1b4b9a
@ -14,10 +14,15 @@
|
||||
package de.bixilon.minosoft.gui.rendering.input.key.manager
|
||||
|
||||
import de.bixilon.kutil.time.TimeUtil.millis
|
||||
import de.bixilon.minosoft.config.key.KeyActions
|
||||
import de.bixilon.minosoft.config.key.KeyBinding
|
||||
import de.bixilon.minosoft.config.key.KeyCodes
|
||||
import de.bixilon.minosoft.gui.rendering.input.InputHandler
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputTestUtil.create
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputTestUtil.simulate
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.CursorModes
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
|
||||
import org.testng.Assert
|
||||
import org.testng.Assert.assertFalse
|
||||
import org.testng.Assert.assertTrue
|
||||
import org.testng.annotations.Test
|
||||
@ -76,4 +81,75 @@ class InputManagerTest {
|
||||
input.simulate(KeyCodes.KEY_1, KeyChangeTypes.PRESS)
|
||||
assertTrue(input.getLastPressed(KeyCodes.KEY_1) - time < 10)
|
||||
}
|
||||
|
||||
fun `check char invoke`() {
|
||||
val input = create()
|
||||
|
||||
var pressed: Int? = null
|
||||
input.handler.handler = object : InputHandler {
|
||||
override fun onCharPress(char: Int): Boolean {
|
||||
pressed = char
|
||||
return true
|
||||
}
|
||||
}
|
||||
input.simulate(123)
|
||||
Assert.assertEquals(pressed, 123)
|
||||
}
|
||||
|
||||
fun `don't skip next char if just pushed and popped`() {
|
||||
val input = create()
|
||||
|
||||
var pressed: Int? = null
|
||||
val handler1 = object : InputHandler {
|
||||
override fun onCharPress(char: Int): Boolean {
|
||||
pressed = char
|
||||
return true
|
||||
}
|
||||
}
|
||||
val handler2 = object : InputHandler {
|
||||
override fun onCharPress(char: Int): Boolean {
|
||||
pressed = char + 5000
|
||||
return true
|
||||
}
|
||||
}
|
||||
input.handler.handler = handler1
|
||||
input.handler.handler = null
|
||||
input.handler.handler = handler2
|
||||
input.simulate(123)
|
||||
Assert.assertEquals(pressed, 5123)
|
||||
}
|
||||
|
||||
fun `don't skip next char if just pushed and popped and opened via key binding`() {
|
||||
val input = create()
|
||||
|
||||
var pressed: Int? = null
|
||||
val handler1 = object : InputHandler {
|
||||
override fun onCharPress(char: Int): Boolean {
|
||||
pressed = char
|
||||
return true
|
||||
}
|
||||
}
|
||||
val handler2 = object : InputHandler {
|
||||
override fun onCharPress(char: Int): Boolean {
|
||||
pressed = char + 5000
|
||||
return true
|
||||
}
|
||||
}
|
||||
input.bindings.register(InputTestUtil.dummy, KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_1)))) { input.handler.handler = handler1 }
|
||||
input.handler.handler = handler1
|
||||
input.handler.handler = null
|
||||
input.handler.handler = handler2
|
||||
input.simulate(123)
|
||||
Assert.assertEquals(pressed, 5123)
|
||||
}
|
||||
|
||||
fun `check correct set of cursor mode`() {
|
||||
val input = create()
|
||||
|
||||
input.context.window.cursorMode = CursorModes.DISABLED
|
||||
input.handler.handler = InputTestUtil.Handler
|
||||
Assert.assertEquals(input.context.window.cursorMode, CursorModes.NORMAL)
|
||||
input.handler.handler = null
|
||||
Assert.assertEquals(input.context.window.cursorMode, CursorModes.DISABLED)
|
||||
}
|
||||
}
|
||||
|
@ -19,11 +19,13 @@ import de.bixilon.minosoft.config.key.KeyCodes
|
||||
import de.bixilon.minosoft.config.profile.profiles.controls.ControlsProfile
|
||||
import de.bixilon.minosoft.data.registries.identified.Namespaces
|
||||
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
|
||||
import de.bixilon.minosoft.gui.rendering.RenderContext
|
||||
import de.bixilon.minosoft.gui.rendering.input.InputHandler
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.BindingsManager
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.actions.bindingsPressed
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.actions.keysPressed
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.dummy.DummyWindow
|
||||
import de.bixilon.minosoft.test.IT
|
||||
import de.bixilon.minosoft.util.KUtil.set
|
||||
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap
|
||||
@ -32,11 +34,15 @@ object InputTestUtil {
|
||||
private val profile = BindingsManager::class.java.getDeclaredField("profile").apply { isAccessible = true }
|
||||
private val bindings = BindingsManager::class.java.getDeclaredField("bindings").apply { isAccessible = true }
|
||||
private val onKey = InputManager::class.java.getDeclaredMethod("onKey", KeyCodes::class.java, KeyChangeTypes::class.java).apply { isAccessible = true }
|
||||
private val onChar = InputManager::class.java.getDeclaredMethod("onChar", Int::class.java).apply { isAccessible = true }
|
||||
private val times = InputManager::class.java.getDeclaredField("times").apply { isAccessible = true }
|
||||
|
||||
|
||||
fun create(): InputManager {
|
||||
val manager = IT.OBJENESIS.newInstance(InputManager::class.java)
|
||||
val context = IT.OBJENESIS.newInstance(RenderContext::class.java)
|
||||
context::window.forceSet(DummyWindow())
|
||||
manager::context.forceSet(context)
|
||||
|
||||
val bindings = IT.OBJENESIS.newInstance(BindingsManager::class.java)
|
||||
bindings::input.forceSet(manager)
|
||||
@ -60,6 +66,10 @@ object InputTestUtil {
|
||||
onKey.invoke(this, code, change)
|
||||
}
|
||||
|
||||
fun InputManager.simulate(char: Int) {
|
||||
onChar.invoke(this, char)
|
||||
}
|
||||
|
||||
object Handler : InputHandler
|
||||
|
||||
val dummy = Namespaces.minosoft("dummy")
|
||||
|
@ -16,6 +16,8 @@ package de.bixilon.minosoft.gui.rendering.input.key.manager.binding
|
||||
import de.bixilon.minosoft.config.key.KeyActions
|
||||
import de.bixilon.minosoft.config.key.KeyBinding
|
||||
import de.bixilon.minosoft.config.key.KeyCodes
|
||||
import de.bixilon.minosoft.gui.rendering.input.InputHandler
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputTestUtil
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputTestUtil.create
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputTestUtil.dummy
|
||||
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputTestUtil.simulate
|
||||
@ -93,5 +95,97 @@ class BindingManagerTest {
|
||||
assertTrue(input.bindings.isDown(dummy))
|
||||
}
|
||||
|
||||
// TODO: input handler test
|
||||
fun `ignore if consumer is set`() {
|
||||
val input = create()
|
||||
|
||||
var invoked = 0
|
||||
input.bindings.register(dummy, KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_1)))) { invoked++ }
|
||||
input.handler.handler = InputTestUtil.Handler
|
||||
input.simulate(KeyCodes.KEY_1, KeyChangeTypes.PRESS)
|
||||
assertEquals(invoked, 0)
|
||||
}
|
||||
|
||||
fun `consumer set and ignore that`() {
|
||||
val input = create()
|
||||
|
||||
var invoked = 0
|
||||
input.bindings.register(dummy, KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_1)), ignoreConsumer = true)) { invoked++ }
|
||||
input.handler.handler = InputTestUtil.Handler
|
||||
input.simulate(KeyCodes.KEY_1, KeyChangeTypes.PRESS)
|
||||
assertEquals(invoked, 1)
|
||||
}
|
||||
|
||||
|
||||
fun `skip next char if opened via keybinding`() {
|
||||
val input = create()
|
||||
|
||||
var pressed: Int? = null
|
||||
val handler = object : InputHandler {
|
||||
override fun onCharPress(char: Int): Boolean {
|
||||
pressed = char
|
||||
return true
|
||||
}
|
||||
}
|
||||
input.bindings.register(dummy, KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_1)))) { input.handler.handler = handler }
|
||||
input.simulate(KeyCodes.KEY_1, KeyChangeTypes.PRESS)
|
||||
assertNull(pressed)
|
||||
input.simulate(10)
|
||||
assertNull(pressed)
|
||||
input.simulate(12)
|
||||
assertEquals(pressed, 12)
|
||||
}
|
||||
|
||||
fun `don't skip unprintable skip next char if opened via keybinding`() {
|
||||
val input = create()
|
||||
|
||||
var pressed: Int? = null
|
||||
val handler = object : InputHandler {
|
||||
override fun onCharPress(char: Int): Boolean {
|
||||
pressed = char
|
||||
return true
|
||||
}
|
||||
}
|
||||
input.bindings.register(dummy, KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_ESCAPE)))) { input.handler.handler = handler }
|
||||
input.simulate(KeyCodes.KEY_ESCAPE, KeyChangeTypes.PRESS)
|
||||
assertNull(pressed)
|
||||
input.simulate(10)
|
||||
assertEquals(pressed, 10)
|
||||
}
|
||||
|
||||
fun `unpress key combination if handler was set`() {
|
||||
val input = create()
|
||||
|
||||
var state = false
|
||||
input.bindings.register(dummy, KeyBinding(mapOf(KeyActions.CHANGE to setOf(KeyCodes.KEY_1)))) { state = it }
|
||||
input.simulate(KeyCodes.KEY_1, KeyChangeTypes.PRESS)
|
||||
assertTrue(state)
|
||||
input.handler.handler = InputTestUtil.Handler
|
||||
assertFalse(state)
|
||||
}
|
||||
|
||||
fun `press key before modifier`() {
|
||||
val input = create()
|
||||
|
||||
var pressed = 0
|
||||
input.bindings.register(dummy, KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_1), KeyActions.MODIFIER to setOf(KeyCodes.KEY_2)))) { pressed++ }
|
||||
input.simulate(KeyCodes.KEY_1, KeyChangeTypes.PRESS)
|
||||
assertEquals(pressed, 0)
|
||||
input.simulate(KeyCodes.KEY_2, KeyChangeTypes.PRESS)
|
||||
assertEquals(pressed, 0)
|
||||
input.simulate(KeyCodes.KEY_2, KeyChangeTypes.RELEASE)
|
||||
assertEquals(pressed, 0)
|
||||
}
|
||||
|
||||
fun `press modifier before key`() {
|
||||
val input = create()
|
||||
|
||||
var pressed = 0
|
||||
input.bindings.register(dummy, KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_1), KeyActions.MODIFIER to setOf(KeyCodes.KEY_2)))) { pressed++ }
|
||||
input.simulate(KeyCodes.KEY_2, KeyChangeTypes.PRESS)
|
||||
assertEquals(pressed, 0)
|
||||
input.simulate(KeyCodes.KEY_1, KeyChangeTypes.PRESS)
|
||||
assertEquals(pressed, 1)
|
||||
input.simulate(KeyCodes.KEY_2, KeyChangeTypes.RELEASE)
|
||||
assertEquals(pressed, 1)
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +261,7 @@ class Modifier {
|
||||
assertFalse(state.satisfied)
|
||||
}
|
||||
|
||||
fun `multiple keys, not all pressed`() {
|
||||
fun `multiple keys, one pressed`() {
|
||||
val state = KeyBindingFilterState(true)
|
||||
KeyActionFilter.Modifier.check(
|
||||
state, setOf(KeyCodes.KEY_0, KeyCodes.KEY_1, KeyCodes.KEY_2), input(), name,
|
||||
@ -271,7 +271,7 @@ class Modifier {
|
||||
0L,
|
||||
)
|
||||
|
||||
assertFalse(state.satisfied)
|
||||
assertTrue(state.satisfied)
|
||||
}
|
||||
|
||||
fun `multiple keys, all pressed`() {
|
||||
|
@ -175,5 +175,9 @@ enum class KeyCodes {
|
||||
|
||||
KEY_CODE_MAP = keyCodeMap
|
||||
}
|
||||
|
||||
fun KeyCodes.isPrintable(): Boolean {
|
||||
return this.ordinal in KEY_SPACE.ordinal..KEY_WORLD_2.ordinal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ package de.bixilon.minosoft.gui.rendering.input.key.manager
|
||||
|
||||
import de.bixilon.kotlinglm.vec2.Vec2
|
||||
import de.bixilon.minosoft.config.key.KeyCodes
|
||||
import de.bixilon.minosoft.config.key.KeyCodes.Companion.isPrintable
|
||||
import de.bixilon.minosoft.gui.rendering.input.InputHandler
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.CursorModes
|
||||
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
|
||||
@ -28,7 +29,12 @@ class InputHandlerManager(
|
||||
if (field == value) {
|
||||
return
|
||||
}
|
||||
val previous = field
|
||||
field = value
|
||||
if ((previous == null) == (value == null)) return
|
||||
if (previous == null) {
|
||||
skipMouse = true
|
||||
}
|
||||
|
||||
if (value == null) {
|
||||
disable()
|
||||
@ -86,4 +92,16 @@ class InputHandlerManager(
|
||||
private fun disable() {
|
||||
context.window.cursorMode = CursorModes.DISABLED
|
||||
}
|
||||
|
||||
fun checkSkip(code: KeyCodes, pressed: Boolean, previous: InputHandler?) {
|
||||
val next = handler
|
||||
if (next == null || previous == next) return
|
||||
|
||||
if (pressed) {
|
||||
this.skipKey = true
|
||||
}
|
||||
if (code.isPrintable()) {
|
||||
this.skipChar = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,11 +94,14 @@ class InputManager(
|
||||
this.pressed -= code
|
||||
}
|
||||
|
||||
val handler = this.handler.handler
|
||||
bindings.onKey(code, pressed, millis)
|
||||
|
||||
if (pressed) {
|
||||
times[code] = millis
|
||||
}
|
||||
|
||||
this.handler.checkSkip(code, pressed, handler)
|
||||
}
|
||||
|
||||
private fun onChar(char: Int) {
|
||||
|
@ -59,15 +59,11 @@ interface KeyActionFilter {
|
||||
object Modifier : KeyActionFilter {
|
||||
|
||||
override fun check(filter: KeyBindingFilterState, codes: Set<KeyCodes>, input: InputManager, name: ResourceLocation, state: KeyBindingState, code: KeyCodes, pressed: Boolean, millis: Long) {
|
||||
if (code !in codes) {
|
||||
filter.satisfied = false
|
||||
return
|
||||
}
|
||||
if (codes.size == 1) { // optimize if (as most) key has just one modifier key
|
||||
if (pressed) return
|
||||
if (!pressed) {
|
||||
filter.satisfied = false
|
||||
return
|
||||
}
|
||||
if (code in codes) return
|
||||
if (input.areKeysDown(codes)) return
|
||||
|
||||
filter.satisfied = false
|
||||
|
Loading…
x
Reference in New Issue
Block a user