wip input manager refactor + tests

This commit is contained in:
Bixilon 2023-06-29 17:56:20 +02:00
parent e3666c80bd
commit 50045e8c70
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
36 changed files with 957 additions and 398 deletions

View File

@ -0,0 +1,362 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.input.key.manager.binding.actions
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.reflection.ReflectionUtil.forceSet
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.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputManager
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.BindingsManager
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.KeyBindingFilterState
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.KeyBindingState
import de.bixilon.minosoft.test.IT.OBJENESIS
import org.testng.Assert.assertFalse
import org.testng.Assert.assertTrue
import org.testng.annotations.Test
val keysPressed = InputManager::class.java.getDeclaredField("pressed").apply { isAccessible = true }
val bindingsPressed = BindingsManager::class.java.getDeclaredField("pressed").apply { isAccessible = true }
val name = minosoft("dummy")
fun input(): InputManager {
val manager = OBJENESIS.newInstance(InputManager::class.java)
val bindings = OBJENESIS.newInstance(BindingsManager::class.java)
bindingsPressed[bindings] = mutableSetOf<ResourceLocation>()
manager::bindings.forceSet(bindings)
keysPressed[manager] = mutableSetOf<KeyCodes>()
return manager
}
@Test(groups = ["input"])
class Press {
fun `simple press`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Press.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = true,
0L,
)
assertTrue(state.satisfied)
}
fun `wrong key`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Press.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = true,
0L,
)
assertFalse(state.satisfied)
}
fun `multiple keys`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Press.check(
state, setOf(KeyCodes.KEY_0, KeyCodes.KEY_1, KeyCodes.KEY_2), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = true,
0L,
)
assertTrue(state.result)
}
fun `not pressed`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Press.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.PRESS to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = false,
0L,
)
assertFalse(state.satisfied)
}
}
@Test(groups = ["input"])
class Release {
fun `simple release`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Release.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.RELEASE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = false,
0L,
)
assertTrue(state.result)
assertTrue(state.satisfied)
}
fun `wrong key`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Release.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.RELEASE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = false,
0L,
)
assertFalse(state.satisfied)
}
fun `multiple keys`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Release.check(
state, setOf(KeyCodes.KEY_0, KeyCodes.KEY_1, KeyCodes.KEY_2), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.RELEASE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = false,
0L,
)
assertTrue(state.satisfied)
}
fun pressed() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Release.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.RELEASE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = true,
0L,
)
assertFalse(state.satisfied)
}
}
@Test(groups = ["input"])
class Change {
fun `simple press`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Change.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.CHANGE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = true,
0L,
)
assertTrue(state.satisfied)
assertTrue(state.forceNotify)
}
fun `simple release`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Change.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.CHANGE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = false,
0L,
)
assertTrue(state.satisfied)
assertTrue(state.forceNotify)
}
fun `wrong key`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Change.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.CHANGE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = false,
0L,
)
assertFalse(state.satisfied)
assertFalse(state.forceNotify)
}
fun `multiple keys`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Change.check(
state, setOf(KeyCodes.KEY_0, KeyCodes.KEY_1, KeyCodes.KEY_2), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.CHANGE to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = true,
0L,
)
assertTrue(state.satisfied)
assertTrue(state.forceNotify)
}
}
@Test(groups = ["input"])
class Modifier {
fun `simple press`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Modifier.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.MODIFIER to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = true,
0L,
)
assertFalse(state.skip)
assertTrue(state.satisfied)
}
fun `simple release`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Modifier.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.MODIFIER to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = false,
0L,
)
assertFalse(state.skip)
assertFalse(state.satisfied)
}
fun `wrong key`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Modifier.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.MODIFIER to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = false,
0L,
)
assertTrue(state.skip)
}
fun `multiple keys, not all pressed`() {
val state = KeyBindingFilterState(true)
KeyActionFilter.Modifier.check(
state, setOf(KeyCodes.KEY_0, KeyCodes.KEY_1, KeyCodes.KEY_2), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.MODIFIER to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = true,
0L,
)
assertFalse(state.satisfied)
}
fun `multiple keys, all pressed`() {
val state = KeyBindingFilterState(true)
val input = input()
val pressed = keysPressed.get(input).unsafeCast<MutableSet<KeyCodes>>()
pressed += KeyCodes.KEY_0
pressed += KeyCodes.KEY_1
pressed += KeyCodes.KEY_2
KeyActionFilter.Modifier.check(
state, setOf(KeyCodes.KEY_0, KeyCodes.KEY_1, KeyCodes.KEY_2), input, name,
KeyBindingState(KeyBinding(mapOf(KeyActions.MODIFIER to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_1,
pressed = true,
0L,
)
assertTrue(state.satisfied)
assertFalse(state.skip)
}
}
@Test(groups = ["input"])
class Sticky {
fun `press key`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Sticky.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.STICKY to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = true,
0L,
)
assertTrue(state.result)
assertFalse(state.skip)
assertTrue(state.satisfied)
}
fun `release key`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Sticky.check(
state, setOf(KeyCodes.KEY_0), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.STICKY to setOf(KeyCodes.KEY_0)))),
KeyCodes.KEY_0,
pressed = false,
0L,
)
assertTrue(state.skip)
}
fun `wrong key`() {
val state = KeyBindingFilterState(false)
KeyActionFilter.Sticky.check(
state, setOf(KeyCodes.KEY_1), input(), name,
KeyBindingState(KeyBinding(mapOf(KeyActions.STICKY to setOf(KeyCodes.KEY_1)))),
KeyCodes.KEY_0,
pressed = true,
0L,
)
assertTrue(state.skip)
}
fun `unpress`() {
val state = KeyBindingFilterState(true)
val input = input()
val binding = KeyBinding(mapOf(KeyActions.STICKY to setOf(KeyCodes.KEY_0)))
val pressed = bindingsPressed[input.bindings].unsafeCast<MutableSet<ResourceLocation>>()
pressed += name
KeyActionFilter.Sticky.check(
state, setOf(KeyCodes.KEY_0), input, name,
KeyBindingState(binding),
KeyCodes.KEY_0,
pressed = true,
0L,
)
assertFalse(state.result)
assertFalse(state.skip)
}
}

View File

@ -36,7 +36,7 @@ class ViewManager(private val camera: Camera) {
fun init() {
camera.context.input.registerKeyCallback(
camera.context.input.bindings.register(
"minosoft:camera_debug_view".toResourceLocation(),
KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
@ -48,7 +48,7 @@ class ViewManager(private val camera: Camera) {
camera.context.connection.util.sendDebugMessage("Camera debug view: ${it.format()}")
}
camera.context.input.registerKeyCallback(
camera.context.input.bindings.register(
"minosoft:camera_third_person".toResourceLocation(),
KeyBinding(
KeyActions.STICKY to setOf(KeyCodes.KEY_F5),

View File

@ -78,12 +78,12 @@ class EntityRenderer(
profile.hitbox::enabled.observe(this) { this.hitboxes = it }
context.camera.offset::offset.observe(this) { reset = true }
context.input.registerKeyCallback(
context.input.bindings.register(
HITBOX_TOGGLE_KEY_COMBINATION,
KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F3),
KeyActions.STICKY to setOf(KeyCodes.KEY_B),
), defaultPressed = profile.hitbox.enabled
), pressed = profile.hitbox.enabled
) {
profile.hitbox.enabled = it
connection.util.sendDebugMessage("Entity hit boxes: ${it.format()}")

View File

@ -33,7 +33,7 @@ class FunEffectManager(
init {
context.input.registerKeyCallback(
context.input.bindings.register(
"minosoft:switch_fun_settings".toResourceLocation(),
KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.latch.AbstractLatch
import de.bixilon.kutil.observer.DataObserver.Companion.observe
import de.bixilon.kutil.observer.DataObserver.Companion.observed
@ -120,11 +119,11 @@ class GUIRenderer(
return popper.onCharPress(char) || dragged.onCharPress(char) || gui.onCharPress(char)
}
override fun onKey(type: KeyChangeTypes, key: KeyCodes): Boolean {
return popper.onKey(type, key) || dragged.onKey(type, key) || gui.onKey(type, key)
override fun onKey(code: KeyCodes, change: KeyChangeTypes): Boolean {
return popper.onKey(code, change) || dragged.onKey(code, change) || gui.onKey(code, change)
}
override fun onScroll(scrollOffset: Vec2d): Boolean {
override fun onScroll(scrollOffset: Vec2): Boolean {
return popper.onScroll(scrollOffset) || dragged.onScroll(scrollOffset) || gui.onScroll(scrollOffset)
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.elements.text
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.collections.CollectionUtil.synchronizedListOf
import de.bixilon.kutil.collections.CollectionUtil.toSynchronizedList
import de.bixilon.kutil.time.TimeUtil.millis
@ -93,7 +92,7 @@ open class TextFlowElement(
}
}
override fun onScroll(position: Vec2, scrollOffset: Vec2d): Boolean {
override fun onScroll(position: Vec2, scrollOffset: Vec2): Boolean {
this.scrollOffset += scrollOffset.y.toInt()
return true
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.gui
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments
@ -72,7 +71,7 @@ interface AbstractLayout<T : Element> : InputElement, DragTarget {
return activeElement?.onCharPress(char) ?: false
}
override fun onScroll(position: Vec2, scrollOffset: Vec2d): Boolean {
override fun onScroll(position: Vec2, scrollOffset: Vec2): Boolean {
val (element, offset) = getAt(position) ?: return false
return element.onScroll(offset, scrollOffset)
}
@ -104,7 +103,7 @@ interface AbstractLayout<T : Element> : InputElement, DragTarget {
return activeDragElement?.onDragLeave(draggable)
}
override fun onDragScroll(position: Vec2, scrollOffset: Vec2d, draggable: Dragged): Element? {
override fun onDragScroll(position: Vec2, scrollOffset: Vec2, draggable: Dragged): Element? {
val (element, offset) = getAt(position) ?: return null
return element.onDragScroll(offset, scrollOffset, draggable)
}

View File

@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.gui
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.cast.CastUtil.unsafeCast
import de.bixilon.kutil.concurrent.lock.simple.SimpleLock
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
@ -71,7 +70,7 @@ class GUIManager(
}
override fun postInit() {
context.input.registerKeyCallback(
context.input.bindings.register(
"minosoft:back".toResourceLocation(),
KeyBinding(
KeyActions.RELEASE to setOf(KeyCodes.KEY_ESCAPE),
@ -210,11 +209,11 @@ class GUIManager(
return runForEach { it.onMouseMove(position) }
}
override fun onKey(type: KeyChangeTypes, key: KeyCodes): Boolean {
return runForEach { it.onKey(type, key) }
override fun onKey(code: KeyCodes, change: KeyChangeTypes): Boolean {
return runForEach { it.onKey(code, change) }
}
override fun onScroll(scrollOffset: Vec2d): Boolean {
override fun onScroll(scrollOffset: Vec2): Boolean {
return runForEach { it.onScroll(scrollOffset) }
}
@ -240,7 +239,7 @@ class GUIManager(
return runForEachDrag { it.onDragKey(type, key, dragged) }
}
override fun onDragScroll(scrollOffset: Vec2d, dragged: Dragged): Element? {
override fun onDragScroll(scrollOffset: Vec2, dragged: Dragged): Element? {
return runForEachDrag { it.onDragScroll(scrollOffset, dragged) }
}
@ -266,7 +265,7 @@ class GUIManager(
private fun _push(element: GUIElement) {
if (elementOrder.isEmpty()) {
context.input.inputHandler = guiRenderer
context.input.handler.handler = guiRenderer
}
orderLock.acquire()
val copy = elementOrder.toList()
@ -313,7 +312,7 @@ class GUIManager(
orderLock.acquire()
if (elementOrder.isEmpty()) {
context.input.inputHandler = null
context.input.handler.handler = null
guiRenderer.popper.clear()
guiRenderer.dragged.element = null
}
@ -337,7 +336,7 @@ class GUIManager(
toPop.onClose()
orderLock.acquire()
if (elementOrder.isEmpty()) {
context.input.inputHandler = null
context.input.handler.handler = null
guiRenderer.popper.clear()
guiRenderer.dragged.element = null
orderLock.release()
@ -365,7 +364,7 @@ class GUIManager(
orderLock.unlock()
guiRenderer.popper.clear()
guiRenderer.dragged.element = null
context.input.inputHandler = null
context.input.handler.handler = null
}
operator fun <T : GUIElement> get(builder: GUIBuilder<T>): T {

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.gui
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.RenderContext
@ -147,16 +146,16 @@ open class GUIMeshElement<T : Element>(
return element.onMouseMove(position, position)
}
override fun onKey(type: KeyChangeTypes, key: KeyCodes): Boolean {
val mouseButton = MouseButtons[key] ?: return element.onKey(key, type)
override fun onKey(code: KeyCodes, change: KeyChangeTypes): Boolean {
val mouseButton = MouseButtons[code] ?: return element.onKey(code, change)
val position = Vec2(lastPosition ?: return false)
val mouseAction = MouseActions[type] ?: return false
val mouseAction = MouseActions[change] ?: return false
return element.onMouseAction(position, mouseButton, mouseAction, clickCounter.getClicks(mouseButton, mouseAction, position, millis()))
}
override fun onScroll(scrollOffset: Vec2d): Boolean {
override fun onScroll(scrollOffset: Vec2): Boolean {
val position = Vec2(lastPosition ?: return false)
return element.onScroll(position, scrollOffset)
}
@ -179,7 +178,7 @@ open class GUIMeshElement<T : Element>(
return element.onDragMouseAction(position, mouseButton, mouseAction, clickCounter.getClicks(mouseButton, mouseAction, position, millis()), dragged)
}
override fun onDragScroll(scrollOffset: Vec2d, dragged: Dragged): Element? {
override fun onDragScroll(scrollOffset: Vec2, dragged: Dragged): Element? {
return element.onDragScroll(Vec2(lastDragPosition ?: return null), scrollOffset, dragged)
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.gui.dragged
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
@ -31,7 +30,7 @@ abstract class Dragged(guiRenderer: GUIRenderer) : Element(guiRenderer) {
open fun onDragMove(position: Vec2, target: Element?) = Unit
open fun onDragEnd(position: Vec2, target: Element?) = Unit
open fun onDragScroll(position: Vec2, scrollOffset: Vec2d, target: Element?) = Unit
open fun onDragScroll(position: Vec2, scrollOffset: Vec2, target: Element?) = Unit
open fun onDragMouseAction(position: Vec2, button: MouseButtons, action: MouseActions, count: Int, target: Element?) = Unit
open fun onDragKey(key: KeyCodes, type: KeyChangeTypes, target: Element?) = Unit

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.gui.dragged
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
@ -120,22 +119,22 @@ class DraggedManager(
return true
}
override fun onKey(type: KeyChangeTypes, key: KeyCodes): Boolean {
override fun onKey(code: KeyCodes, change: KeyChangeTypes): Boolean {
val element = element ?: return false
val target = guiRenderer.gui.onDragKey(type, key, element.element)
val mouseButton = MouseButtons[key]
val target = guiRenderer.gui.onDragKey(change, code, element.element)
val mouseButton = MouseButtons[code]
if (mouseButton == null) {
element.element.onDragKey(key, type, target)
element.element.onDragKey(code, change, target)
return true
}
val mouseAction = MouseActions[type] ?: return false
val mouseAction = MouseActions[change] ?: return false
element.element.onDragMouseAction(guiRenderer.currentMousePosition, mouseButton, mouseAction, clickCounter.getClicks(mouseButton, mouseAction, guiRenderer.currentMousePosition, millis()), target)
return true
}
override fun onScroll(scrollOffset: Vec2d): Boolean {
override fun onScroll(scrollOffset: Vec2): Boolean {
val element = element ?: return false
val target = guiRenderer.gui.onDragScroll(scrollOffset, element.element)
element.element.onDragScroll(guiRenderer.currentMousePosition, scrollOffset, target)

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.gui.popper
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.concurrent.pool.DefaultThreadPool
import de.bixilon.kutil.latch.SimpleLatch
import de.bixilon.kutil.time.TimeUtil.millis
@ -111,19 +110,19 @@ class PopperManager(
return false
}
override fun onKey(type: KeyChangeTypes, key: KeyCodes): Boolean {
override fun onKey(code: KeyCodes, change: KeyChangeTypes): Boolean {
for ((index, element) in poppers.toList().withIndex()) {
if (index != 0 && !element.activeWhenHidden) {
continue
}
if (element.onKey(type, key)) {
if (element.onKey(code, change)) {
return true
}
}
return false
}
override fun onScroll(scrollOffset: Vec2d): Boolean {
override fun onScroll(scrollOffset: Vec2): Boolean {
for ((index, element) in poppers.toList().withIndex()) {
if (index != 0 && !element.activeWhenHidden) {
continue

View File

@ -28,7 +28,7 @@ import de.bixilon.minosoft.util.KUtil.toResourceLocation
object ContainerGUIManager {
private fun registerLocalContainerEvent(guiRenderer: GUIRenderer) {
guiRenderer.context.input.registerKeyCallback("minosoft:local_inventory".toResourceLocation(), KeyBinding(
guiRenderer.context.input.bindings.register("minosoft:local_inventory".toResourceLocation(), KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_E),
)) { guiRenderer.gui.open(LocalInventoryScreen) }
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
@ -191,7 +190,7 @@ abstract class Menu(
return true
}
override fun onScroll(position: Vec2, scrollOffset: Vec2d): Boolean {
override fun onScroll(position: Vec2, scrollOffset: Vec2): Boolean {
val (element, delta) = getAt(position) ?: return true
element.onScroll(delta, scrollOffset)
return true

View File

@ -61,7 +61,7 @@ class HUDManager(
val toggleKeyBinding = hudBuilder.ENABLE_KEY_BINDING ?: return
val toggleKeyBindingName = hudBuilder.ENABLE_KEY_BINDING_NAME ?: return
context.input.registerKeyCallback(toggleKeyBindingName, toggleKeyBinding, defaultPressed = hudBuilder.DEFAULT_ENABLED) { hudElement.enabled = it }
context.input.bindings.register(toggleKeyBindingName, toggleKeyBinding, pressed = hudBuilder.DEFAULT_ENABLED) { hudElement.enabled = it }
}
private fun registerDefaultElements() {
@ -93,10 +93,10 @@ class HUDManager(
element.init()
}
context.input.registerKeyCallback(
context.input.bindings.register(
"minosoft:enable_hud".toResourceLocation(), KeyBinding(
KeyActions.STICKY to setOf(KeyCodes.KEY_F1),
), defaultPressed = enabled
), pressed = enabled
) { enabled = it }
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.hud.elements.chat
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kotlinglm.vec2.Vec2i
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
@ -36,7 +35,7 @@ abstract class AbstractChatElement(guiRenderer: GUIRenderer) : Element(guiRender
messages.render(offset + Vec2i(ChatElement.CHAT_INPUT_MARGIN, 0), consumer, options)
}
override fun onScroll(position: Vec2, scrollOffset: Vec2d): Boolean {
override fun onScroll(position: Vec2, scrollOffset: Vec2): Boolean {
val size = messages.size
if (position.y > size.y || position.x > messages.size.x) {
return false

View File

@ -94,13 +94,13 @@ class ChatElement(guiRenderer: GUIRenderer) : AbstractChatElement(guiRenderer),
messages += it.message.text
}
context.input.registerKeyCallback(
context.input.bindings.register(
"minosoft:open_chat".toResourceLocation(), KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_T),
)
) { guiRenderer.gui.open(ChatElement) }
context.input.registerKeyCallback(
context.input.bindings.register(
minosoft("open_command_chat"),
KeyBinding(KeyActions.PRESS to setOf(KeyCodes.KEY_SLASH))
) {

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.input
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.cast.CastUtil.nullCast
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
@ -29,7 +28,7 @@ interface DragTarget {
fun onDragMove(position: Vec2, absolute: Vec2, draggable: Dragged): Element? = this.nullCast()
fun onDragLeave(draggable: Dragged): Element? = this.nullCast()
fun onDragScroll(position: Vec2, scrollOffset: Vec2d, draggable: Dragged): Element? = this.nullCast()
fun onDragScroll(position: Vec2, scrollOffset: Vec2, draggable: Dragged): Element? = this.nullCast()
fun onDragMouseAction(position: Vec2, button: MouseButtons, action: MouseActions, count: Int, draggable: Dragged): Element? = this.nullCast()
fun onDragKey(key: KeyCodes, type: KeyChangeTypes, draggable: Dragged): Element? = this.nullCast()

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.input
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.elements.Element
import de.bixilon.minosoft.gui.rendering.gui.gui.dragged.Dragged
@ -24,6 +23,6 @@ interface DraggableHandler {
fun onDragMove(position: Vec2, dragged: Dragged): Element? = null
fun onDragKey(type: KeyChangeTypes, key: KeyCodes, dragged: Dragged): Element? = null
fun onDragScroll(scrollOffset: Vec2d, dragged: Dragged): Element? = null
fun onDragScroll(scrollOffset: Vec2, dragged: Dragged): Element? = null
fun onDragChar(char: Int, dragged: Dragged): Element? = null
}

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,7 +14,6 @@
package de.bixilon.minosoft.gui.rendering.gui.input
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons
@ -23,7 +22,7 @@ import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
interface InputElement : MouseInputElement {
fun onMouseAction(position: Vec2, button: MouseButtons, action: MouseActions, count: Int) = false
fun onScroll(position: Vec2, scrollOffset: Vec2d) = false
fun onScroll(position: Vec2, scrollOffset: Vec2) = false
fun onKey(key: KeyCodes, type: KeyChangeTypes) = false
fun onCharPress(char: Int) = false

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -13,10 +13,12 @@
package de.bixilon.minosoft.gui.rendering.gui.input
enum class ModifierKeys {
CONTROL,
ALT,
SHIFT,
SUPER,
import de.bixilon.minosoft.config.key.KeyCodes
enum class ModifierKeys(vararg val codes: KeyCodes) {
CONTROL(KeyCodes.KEY_LEFT_CONTROL, KeyCodes.KEY_RIGHT_CONTROL),
ALT(KeyCodes.KEY_LEFT_ALT, KeyCodes.KEY_RIGHT_ALT),
SHIFT(KeyCodes.KEY_LEFT_SHIFT, KeyCodes.KEY_RIGHT_SHIFT),
SUPER(KeyCodes.KEY_LEFT_SUPER, KeyCodes.KEY_RIGHT_SUPER),
;
}

View File

@ -37,7 +37,7 @@ class CameraInput(
private var changeFly = false
private fun registerKeyBindings() {
context.input.registerCheckCallback(
context.input.bindings.registerCheck(
MOVE_SPRINT_KEYBINDING to KeyBinding(
KeyActions.CHANGE to setOf(KeyCodes.KEY_LEFT_CONTROL),
),
@ -74,7 +74,7 @@ class CameraInput(
)
context.input.registerKeyCallback(
context.input.bindings.register(
ZOOM_KEYBINDING, KeyBinding(
KeyActions.CHANGE to setOf(KeyCodes.KEY_C),
)
@ -87,19 +87,19 @@ class CameraInput(
fun updateInput(delta: Double) {
val input = PlayerMovementInput(
forward = context.input.isKeyBindingDown(MOVE_FORWARDS_KEYBINDING),
backward = context.input.isKeyBindingDown(MOVE_BACKWARDS_KEYBINDING),
left = context.input.isKeyBindingDown(MOVE_LEFT_KEYBINDING),
right = context.input.isKeyBindingDown(MOVE_RIGHT_KEYBINDING),
jump = context.input.isKeyBindingDown(JUMP_KEYBINDING),
sneak = context.input.isKeyBindingDown(SNEAK_KEYBINDING),
sprint = context.input.isKeyBindingDown(MOVE_SPRINT_KEYBINDING),
flyDown = context.input.isKeyBindingDown(FLY_DOWN_KEYBINDING),
flyUp = context.input.isKeyBindingDown(FLY_UP_KEYBINDING),
forward = MOVE_FORWARDS_KEYBINDING in context.input.bindings,
backward = MOVE_BACKWARDS_KEYBINDING in context.input.bindings,
left = MOVE_LEFT_KEYBINDING in context.input.bindings,
right = MOVE_RIGHT_KEYBINDING in context.input.bindings,
jump = JUMP_KEYBINDING in context.input.bindings,
sneak = SNEAK_KEYBINDING in context.input.bindings,
sprint = MOVE_SPRINT_KEYBINDING in context.input.bindings,
flyDown = FLY_DOWN_KEYBINDING in context.input.bindings,
flyUp = FLY_UP_KEYBINDING in context.input.bindings,
)
val changeFly = context.input.isKeyBindingDown(CHANGE_FLY_KEYBINDING)
val startElytraFly = context.input.isKeyBindingDown(START_ELYTRA_FLY_KEYBINDING)
val changeFly = CHANGE_FLY_KEYBINDING in context.input.bindings
val startElytraFly = START_ELYTRA_FLY_KEYBINDING in context.input.bindings
val inputActions = MovementInputActions(
toggleFly = changeFly != this.changeFly,
startElytraFly = startElytraFly,

View File

@ -1,6 +1,6 @@
/*
* Minosoft
* Copyright (C) 2020-2022 Moritz Zwerger
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
@ -14,14 +14,13 @@
package de.bixilon.minosoft.gui.rendering.input
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
interface InputHandler {
fun onMouseMove(position: Vec2) = false
fun onKey(type: KeyChangeTypes, key: KeyCodes) = false
fun onScroll(scrollOffset: Vec2d) = false
fun onKey(code: KeyCodes, change: KeyChangeTypes) = false
fun onScroll(scrollOffset: Vec2) = false
fun onCharPress(char: Int) = false
}

View File

@ -17,6 +17,7 @@ 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.data.container.types.PlayerInventory
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.gui.rendering.events.input.MouseScrollEvent
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputManager
import de.bixilon.minosoft.input.interaction.InteractionManager
@ -29,19 +30,19 @@ class InteractionManagerKeys(
) {
private fun registerAttack() {
input.registerKeyCallback(ATTACK, KeyBinding(
input.bindings.register(ATTACK, KeyBinding(
KeyActions.CHANGE to setOf(KeyCodes.MOUSE_BUTTON_LEFT),
)) { interactions.tryAttack(it) }
}
private fun registerInteraction() {
input.registerKeyCallback(USE_ITEM, KeyBinding(
input.bindings.register(USE_ITEM, KeyBinding(
KeyActions.CHANGE to setOf(KeyCodes.MOUSE_BUTTON_RIGHT),
)) { interactions.use.change(it) }
}
private fun registerPick() {
input.registerKeyCallback("minosoft:pick_item".toResourceLocation(), KeyBinding(
input.bindings.register(PICK, KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.MOUSE_BUTTON_MIDDLE),
)
) { interactions.pick.pickItem(false) } // ToDo: Combination for not copying nbt
@ -50,25 +51,25 @@ class InteractionManagerKeys(
private fun registerDrop() {
// ToDo: This creates a weird condition, because we first drop the stack and then the single item
// ToDo: Does this swing the arm?
input.registerKeyCallback(DROP_ITEM_STACK, KeyBinding(
input.bindings.register(DROP_ITEM_STACK, KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_Q),
KeyActions.MODIFIER to setOf(KeyCodes.KEY_LEFT_CONTROL)
)) { interactions.drop.dropItem(true) }
input.registerKeyCallback(DROP_ITEM, KeyBinding(
input.bindings.register(DROP_ITEM, KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_Q),
)) { interactions.drop.dropItem(false) }
}
private fun registerSpectate() {
input.registerKeyCallback(STOP_SPECTATING, KeyBinding(
input.bindings.register(STOP_SPECTATING, KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_LEFT_SHIFT),
)) { interactions.spectate.spectate(null) }
}
private fun registerHotbar() {
for (i in 1..PlayerInventory.HOTBAR_SLOTS) {
input.registerKeyCallback("minosoft:hotbar_slot_$i".toResourceLocation(), KeyBinding(
input.bindings.register("minosoft:hotbar_slot_$i".toResourceLocation(), KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_CODE_MAP["$i"]!!),
)) { interactions.hotbar.selectSlot(i - 1) }
}
@ -98,7 +99,7 @@ class InteractionManagerKeys(
}
input.registerKeyCallback("minosoft:swap_items".toResourceLocation(), KeyBinding(
input.bindings.register(SWAP, KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_F),
)) { interactions.hotbar.trySwap() }
}
@ -120,6 +121,8 @@ class InteractionManagerKeys(
companion object {
private val ATTACK = "minosoft:attack".toResourceLocation()
private val USE_ITEM = "minosoft:use_item".toResourceLocation()
private val SWAP = minosoft("swap_items")
private val PICK = minosoft("pick_item")
private val DROP_ITEM = "minosoft:drop_item".toResourceLocation()
private val DROP_ITEM_STACK = "minosoft:drop_item_stack".toResourceLocation()

View File

@ -20,9 +20,10 @@ import de.bixilon.minosoft.config.key.KeyBinding
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputManager
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.BindingsManager
import de.bixilon.minosoft.gui.rendering.system.base.PolygonModes
import de.bixilon.minosoft.gui.rendering.system.window.CursorModes
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.format
object DebugKeyBindings {
@ -33,14 +34,14 @@ object DebugKeyBindings {
val PAUSE_OUTGOING = minosoft("network_pause_outgoing")
fun register(context: RenderContext) {
val manager = context.input
val bindings = context.input.bindings
manager.registerNetwork()
manager.registerRendering()
bindings.registerNetwork(context.connection)
bindings.registerRendering(context)
}
private fun InputManager.registerNetwork() {
registerKeyCallback(PAUSE_INCOMING, KeyBinding(
private fun BindingsManager.registerNetwork(connection: PlayConnection) {
register(PAUSE_INCOMING, KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.STICKY to setOf(KeyCodes.KEY_I),
ignoreConsumer = true,
@ -49,7 +50,7 @@ object DebugKeyBindings {
connection.network.pauseReceiving(it)
}
registerKeyCallback(PAUSE_OUTGOING, KeyBinding(
register(PAUSE_OUTGOING, KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.STICKY to setOf(KeyCodes.KEY_O),
ignoreConsumer = true,
@ -59,8 +60,10 @@ object DebugKeyBindings {
}
}
private fun InputManager.registerRendering() {
registerKeyCallback(DEBUG_POLYGON, KeyBinding(
private fun BindingsManager.registerRendering(context: RenderContext) {
val connection = context.connection
register(DEBUG_POLYGON, KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.STICKY to setOf(KeyCodes.KEY_P),
)) {
@ -70,11 +73,11 @@ object DebugKeyBindings {
}
registerKeyCallback(CURSOR_MODE, KeyBinding(
register(CURSOR_MODE, KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.PRESS to setOf(KeyCodes.KEY_M),
ignoreConsumer = true,
), defaultPressed = StaticConfiguration.DEBUG_MODE) {
), pressed = StaticConfiguration.DEBUG_MODE) {
val next = when (context.window.cursorMode) {
CursorModes.DISABLED -> CursorModes.NORMAL
CursorModes.NORMAL -> CursorModes.DISABLED

View File

@ -25,17 +25,17 @@ object DefaultKeyBindings {
val FULLSCREEN = minosoft("toggle_fullscreen")
fun register(context: RenderContext) {
val inputHandler = context.input
val bindings = context.input.bindings
val window = context.window
val connection = context.connection
inputHandler.registerKeyCallback(SCREENSHOT, KeyBinding(
bindings.register(SCREENSHOT, KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_F2),
ignoreConsumer = true,
)) { context.screenshotTaker.takeScreenshot() }
inputHandler.registerKeyCallback(FULLSCREEN, KeyBinding(
bindings.register(FULLSCREEN, KeyBinding(
KeyActions.PRESS to setOf(KeyCodes.KEY_F11),
ignoreConsumer = true,
)) {

View File

@ -0,0 +1,89 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
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.gui.rendering.input.InputHandler
import de.bixilon.minosoft.gui.rendering.system.window.CursorModes
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
class InputHandlerManager(
val input: InputManager,
) {
private val context = input.context
var handler: InputHandler? = null
set(value) {
if (field == value) {
return
}
field = value
if (value == null) {
disable()
} else {
enable()
}
}
private var skipChar = false
private var skipMouse = false
private var skipKey = false
fun onMouse(position: Vec2): Boolean {
if (skipMouse) {
skipMouse = false
return true
}
val handler = this.handler ?: return false
handler.onMouseMove(position)
return true
}
fun onKey(code: KeyCodes, change: KeyChangeTypes) {
if (skipKey) {
skipKey = false
return
}
val handler = this.handler ?: return
handler.onKey(code, change)
}
fun onChar(char: Int) {
if (skipChar) {
skipChar = false
return
}
val handler = this.handler ?: return
handler.onCharPress(char)
}
fun onScroll(delta: Vec2): Boolean {
val handler = this.handler ?: return false
handler.onScroll(delta)
return true
}
private fun enable() {
context.window.cursorMode = CursorModes.NORMAL
// todo: disable all key combinations
}
private fun disable() {
context.window.cursorMode = CursorModes.DISABLED
}
}

View File

@ -15,16 +15,8 @@ package de.bixilon.minosoft.gui.rendering.input.key.manager
import de.bixilon.kotlinglm.vec2.Vec2
import de.bixilon.kotlinglm.vec2.Vec2d
import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf
import de.bixilon.kutil.collections.map.SynchronizedMap
import de.bixilon.kutil.observer.map.MapObserver.Companion.observeMap
import de.bixilon.kutil.time.TimeUtil.millis
import de.bixilon.minosoft.config.StaticConfiguration
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.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.events.input.CharInputEvent
import de.bixilon.minosoft.gui.rendering.events.input.KeyInputEvent
@ -32,311 +24,113 @@ import de.bixilon.minosoft.gui.rendering.events.input.MouseMoveEvent
import de.bixilon.minosoft.gui.rendering.events.input.MouseScrollEvent
import de.bixilon.minosoft.gui.rendering.gui.input.ModifierKeys
import de.bixilon.minosoft.gui.rendering.input.CameraInput
import de.bixilon.minosoft.gui.rendering.input.InputHandler
import de.bixilon.minosoft.gui.rendering.input.interaction.InteractionManagerKeys
import de.bixilon.minosoft.gui.rendering.input.key.KeyBindingRegister
import de.bixilon.minosoft.gui.rendering.system.window.CursorModes
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.BindingsManager
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2dUtil.EMPTY
import de.bixilon.minosoft.modding.EventPriorities
import de.bixilon.minosoft.modding.event.listener.CallbackEventListener.Companion.listen
import de.bixilon.minosoft.protocol.network.connection.play.PlayConnection
import de.bixilon.minosoft.util.KUtil.format
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import it.unimi.dsi.fastutil.objects.Object2LongMap
import it.unimi.dsi.fastutil.objects.Object2LongOpenHashMap
class InputManager(
val context: RenderContext,
) {
val connection: PlayConnection = context.connection
val cameraInput = CameraInput(context, context.camera.matrixHandler)
private val profile = connection.profiles.controls
val bindings = BindingsManager(this)
val handler = InputHandlerManager(this)
private val keyBindingCallbacks: SynchronizedMap<ResourceLocation, KeyBindingRegister> = synchronizedMapOf()
private val keysDown: MutableList<KeyCodes> = mutableListOf()
private val keyBindingsDown: MutableList<ResourceLocation> = mutableListOf()
private val keysLastDownTime: MutableMap<KeyCodes, Long> = mutableMapOf()
private val pressed: MutableSet<KeyCodes> = mutableSetOf()
private val times: Object2LongMap<KeyCodes> = Object2LongOpenHashMap<KeyCodes>().apply { defaultReturnValue(-1L) }
var mousePosition: Vec2d = Vec2d.EMPTY
private set
val interactionKeys = InteractionManagerKeys(this, connection.camera.interactions)
var inputHandler: InputHandler? = null
set(value) {
if (field == value) {
return
}
field = value
deactivateAll()
context.window.cursorMode = if (value == null) {
CursorModes.DISABLED
} else {
CursorModes.NORMAL
}
}
private var skipCharPress = false
private var skipMouseMove = false
init {
registerKeyCallback("minosoft:debug_change_cursor_mode".toResourceLocation(),
KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.PRESS to setOf(KeyCodes.KEY_M),
ignoreConsumer = true,
), defaultPressed = StaticConfiguration.DEBUG_MODE) {
val next = when (context.window.cursorMode) {
CursorModes.DISABLED -> CursorModes.NORMAL
CursorModes.NORMAL -> CursorModes.DISABLED
CursorModes.HIDDEN -> CursorModes.NORMAL
}
context.window.cursorMode = next
connection.util.sendDebugMessage("Cursor mode: ${next.format()}")
}
}
fun init() {
interactionKeys.register()
connection.events.listen<CharInputEvent> { charInput(it.char) }
connection.events.listen<KeyInputEvent> { keyInput(it.code, it.change) }
connection.events.listen<CharInputEvent> { onChar(it.char) }
connection.events.listen<KeyInputEvent> { onKey(it.code, it.change) }
connection.events.listen<MouseScrollEvent>(priority = EventPriorities.LOW) { scroll(it.offset, it) }
connection.events.listen<MouseMoveEvent> { onMouse(it.delta, it.position) }
connection.events.listen<MouseMoveEvent> {
val inputHandler = inputHandler
mousePosition = it.position
if (inputHandler != null) {
if (skipMouseMove) {
skipMouseMove = false
return@listen
}
inputHandler.onMouseMove(Vec2(it.position))
return@listen
}
cameraInput.updateMouse(it.delta)
}
profile::keyBindings.observeMap(this) {
for ((key, value) in it.adds) {
val binding = keyBindingCallbacks[key] ?: continue
binding.keyBinding = value
}
for ((key, value) in it.removes) {
val binding = keyBindingCallbacks[key] ?: continue
binding.keyBinding = binding.default
}
}
cameraInput.init()
}
private fun deactivateAll() {
keysDown.clear()
keysLastDownTime.clear()
pressed.clear()
times.clear()
for ((name, pair) in keyBindingCallbacks) {
val down = name in keyBindingsDown
if (!down || pair.defaultPressed) {
continue
}
// ToDo
if (pair.keyBinding.action[KeyActions.DOUBLE_PRESS] != null) {
continue
}
if (pair.keyBinding.action[KeyActions.STICKY] != null) {
continue
}
for (callback in pair.callback) {
callback(false)
}
keyBindingsDown -= name
}
// TODO: disable key bindings
}
private fun keyInput(keyCode: KeyCodes, keyChangeType: KeyChangeTypes) {
val inputHandler = inputHandler
inputHandler?.onKey(keyChangeType, keyCode)
private fun onMouse(delta: Vec2d, position: Vec2d) {
this.mousePosition = position
if (handler.onMouse(Vec2(position))) return
cameraInput.updateMouse(delta)
}
val keyDown = when (keyChangeType) {
private fun onKey(code: KeyCodes, change: KeyChangeTypes) {
this.handler.onKey(code, change)
val pressed = when (change) {
KeyChangeTypes.PRESS -> true
KeyChangeTypes.RELEASE -> false
KeyChangeTypes.REPEAT -> return
}
val currentTime = millis()
val millis = millis()
if (keyDown) {
keysDown += keyCode
if (pressed) {
this.pressed += code
} else {
keysDown -= keyCode
this.pressed -= code
}
for ((resourceLocation, pair) in keyBindingCallbacks) {
if (inputHandler != null && !pair.keyBinding.ignoreConsumer) {
continue
}
var thisKeyBindingDown = keyDown
var checksRun = 0
var thisIsChange = true
var saveDown = true
bindings.onKey(code, pressed, millis)
pair.keyBinding.action[KeyActions.PRESS]?.let {
if (!keyDown) {
thisIsChange = false
}
if (it.contains(keyCode)) {
saveDown = false
} else {
thisIsChange = false
}
checksRun++
}
pair.keyBinding.action[KeyActions.RELEASE]?.let {
if (keyDown) {
thisIsChange = false
}
if (it.contains(keyCode)) {
saveDown = false
} else {
thisIsChange = false
}
checksRun++
}
pair.keyBinding.action[KeyActions.CHANGE]?.let {
if (!it.contains(keyCode)) {
thisIsChange = false
}
checksRun++
}
pair.keyBinding.action[KeyActions.MODIFIER]?.let {
if (!keysDown.containsAll(it)) {
thisIsChange = false
}
checksRun++
}
pair.keyBinding.action[KeyActions.STICKY]?.let {
checksRun++
if (!it.contains(keyCode)) {
thisIsChange = false
return@let
}
if (!keyDown) {
thisIsChange = false
return@let
}
thisKeyBindingDown = !keyBindingsDown.contains(resourceLocation)
}
pair.keyBinding.action[KeyActions.DOUBLE_PRESS]?.let {
checksRun++
if (!keyDown) {
thisIsChange = false
return@let
}
if (!it.contains(keyCode)) {
thisIsChange = false
return@let
}
val lastDownTime = keysLastDownTime[keyCode]
if (lastDownTime == null) {
thisIsChange = false
return@let
}
if (currentTime - lastDownTime > RenderConstants.DOUBLE_PRESS_KEY_PRESS_MAX_DELAY) {
thisIsChange = false
return@let
}
if (currentTime - pair.lastChange <= RenderConstants.DOUBLE_PRESS_DELAY_BETWEEN_PRESSED) {
thisIsChange = false
return@let
}
thisKeyBindingDown = !isKeyBindingDown(resourceLocation)
}
if (!thisIsChange || checksRun == 0) {
continue
}
pair.lastChange = millis()
for (callback in pair.callback) {
callback(thisKeyBindingDown)
}
if (saveDown) {
if (thisKeyBindingDown) {
keyBindingsDown += resourceLocation
} else {
keyBindingsDown -= resourceLocation
}
}
skipCharPress = true
}
if (keyDown) {
keysLastDownTime[keyCode] = currentTime
}
if (this.inputHandler == null) {
skipCharPress = false
skipMouseMove = false
} else if (inputHandler != this.inputHandler) {
skipCharPress = true
skipMouseMove = true
if (pressed) {
times[code] = millis
}
}
private fun charInput(char: Int) {
val inputHandler = inputHandler ?: return
if (skipCharPress) {
skipCharPress = false
return
private fun onChar(char: Int) {
handler.onChar(char)
}
private fun scroll(scrollOffset: Vec2d, event: MouseScrollEvent) {
if (handler.onScroll(Vec2(scrollOffset))) return
event.cancelled = true
}
fun areKeysDown(vararg keys: KeyCodes): Boolean {
for (key in keys) {
if (key !in pressed) {
return false
}
}
inputHandler.onCharPress(char)
return
return true
}
private fun scroll(scrollOffset: Vec2d, event: MouseScrollEvent? = null) {
val inputHandler = inputHandler
if (inputHandler != null) {
inputHandler.onScroll(scrollOffset)
event?.cancelled = true
fun areKeysDown(keys: Collection<KeyCodes>): Boolean {
for (key in keys) {
if (key !in pressed) {
return false
}
}
}
fun registerKeyCallback(resourceLocation: ResourceLocation, defaultKeyBinding: KeyBinding, defaultPressed: Boolean = false, callback: ((keyDown: Boolean) -> Unit)) {
val keyBinding = profile.keyBindings.getOrPut(resourceLocation) { defaultKeyBinding }
val callbackPair = keyBindingCallbacks.synchronizedGetOrPut(resourceLocation) { KeyBindingRegister(keyBinding, defaultKeyBinding, defaultPressed) }
callbackPair.callback += callback
if (keyBinding.action.containsKey(KeyActions.STICKY) && defaultPressed) {
keyBindingsDown += resourceLocation
}
}
fun registerCheckCallback(vararg checks: Pair<ResourceLocation, KeyBinding>) {
for ((resourceLocation, defaultKeyBinding) in checks) {
keyBindingCallbacks.synchronizedGetOrPut(resourceLocation) { KeyBindingRegister(profile.keyBindings.getOrPut(resourceLocation) { defaultKeyBinding }, defaultKeyBinding) }
}
}
fun isKeyBindingDown(resourceLocation: ResourceLocation): Boolean {
return keyBindingsDown.contains(resourceLocation)
}
fun unregisterKeyBinding(it: ResourceLocation) {
keyBindingCallbacks.remove(it)
return true
}
fun isKeyDown(vararg keys: KeyCodes): Boolean {
for (key in keys) {
if (keysDown.contains(key)) {
if (key in pressed) {
return true
}
}
@ -344,16 +138,15 @@ class InputManager(
}
fun isKeyDown(modifier: ModifierKeys): Boolean {
return context.inputManager.isKeyDown(*when (modifier) {
ModifierKeys.CONTROL -> arrayOf(KeyCodes.KEY_LEFT_CONTROL, KeyCodes.KEY_RIGHT_CONTROL)
ModifierKeys.ALT -> arrayOf(KeyCodes.KEY_LEFT_ALT, KeyCodes.KEY_RIGHT_ALT)
ModifierKeys.SHIFT -> arrayOf(KeyCodes.KEY_LEFT_SHIFT, KeyCodes.KEY_RIGHT_SHIFT)
ModifierKeys.SUPER -> arrayOf(KeyCodes.KEY_LEFT_SUPER, KeyCodes.KEY_RIGHT_SUPER)
})
return isKeyDown(*modifier.codes)
}
fun draw(delta: Double) {
cameraInput.updateInput(delta)
interactionKeys.draw()
}
fun getLastPressed(key: KeyCodes): Long {
return this.times.getLong(key)
}
}

View File

@ -0,0 +1,142 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.input.key.manager.binding
import de.bixilon.kutil.collections.CollectionUtil.synchronizedMapOf
import de.bixilon.kutil.collections.map.SynchronizedMap
import de.bixilon.kutil.observer.map.MapObserver.Companion.observeMap
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.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputManager
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.actions.KeyActionFilter.Companion.filter
class BindingsManager(
val input: InputManager,
) {
private val connection = input.context.connection
private val profile = connection.profiles.controls
private val bindings: SynchronizedMap<ResourceLocation, KeyBindingState> = synchronizedMapOf()
private val pressed: MutableSet<ResourceLocation> = mutableSetOf()
init {
profile::keyBindings.observeMap(this) {
for ((key, value) in it.adds) {
val binding = bindings[key] ?: continue
binding.binding = value
}
for ((key, value) in it.removes) {
val binding = bindings[key] ?: continue
binding.binding = binding.default
}
}
}
private fun deactivateAll() {
for ((name, pair) in bindings) {
val down = name in pressed
if (!down || pair.pressed) {
continue
}
// ToDo
if (pair.binding.action[KeyActions.DOUBLE_PRESS] != null) {
continue
}
if (pair.binding.action[KeyActions.STICKY] != null) {
continue
}
for (callback in pair.callback) {
callback(false)
}
pressed -= name
}
}
private fun onKey(name: ResourceLocation, state: KeyBindingState, pressed: Boolean, code: KeyCodes, millis: Long) {
val filterState = KeyBindingFilterState(pressed)
val binding = state.binding
if (binding.action.isEmpty()) return
for ((action, keys) in binding.action) {
val filter = action.filter()
filter.check(filterState, keys, input, name, state, code, pressed, millis)
}
if (filterState.skip) return
val result = if (filterState.satisfied) filterState.result else false
val previous = name in this
if (previous == result && !filterState.forceNotify) return
for (callback in state.callback) {
callback(result)
}
if (previous == result) return
state.lastChange = millis
if (result) {
this.pressed += name
} else {
this.pressed -= name
}
// skipCharPress = true
}
fun onKey(code: KeyCodes, pressed: Boolean, millis: Long) {
val handler = input.handler.handler
for ((name, state) in bindings) {
if (handler != null && !state.binding.ignoreConsumer) {
continue
}
onKey(name, state, pressed, code, millis)
}
}
fun register(name: ResourceLocation, default: KeyBinding, pressed: Boolean = false, callback: KeyBindingCallback) {
val keyBinding = profile.keyBindings.getOrPut(name) { default }
val callbackPair = bindings.synchronizedGetOrPut(name) { KeyBindingState(keyBinding, default, pressed) }
callbackPair.callback += callback
if (keyBinding.action.containsKey(KeyActions.STICKY) && pressed) {
this.pressed += name
}
}
fun registerCheck(vararg checks: Pair<ResourceLocation, KeyBinding>) {
for ((name, binding) in checks) {
bindings.synchronizedGetOrPut(name) { KeyBindingState(profile.keyBindings.getOrPut(name) { binding }, binding) }
}
}
fun isDown(name: ResourceLocation): Boolean {
return name in pressed
}
operator fun contains(name: ResourceLocation) = isDown(name)
fun unregister(name: ResourceLocation) {
bindings.remove(name)
}
operator fun minusAssign(name: ResourceLocation) = unregister(name)
}

View File

@ -0,0 +1,16 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.input.key.manager.binding
typealias KeyBindingCallback = (Boolean) -> Unit

View File

@ -0,0 +1,21 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.input.key.manager.binding
data class KeyBindingFilterState(
var result: Boolean,
var satisfied: Boolean = true,
var skip: Boolean = false,
var forceNotify: Boolean = false,
)

View File

@ -11,15 +11,15 @@
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.input.key
package de.bixilon.minosoft.gui.rendering.input.key.manager.binding
import de.bixilon.minosoft.config.key.KeyBinding
data class KeyBindingRegister(
var keyBinding: KeyBinding,
val default: KeyBinding,
val defaultPressed: Boolean = false,
val callback: MutableSet<(keyDown: Boolean) -> Unit> = mutableSetOf(),
data class KeyBindingState(
var binding: KeyBinding,
val default: KeyBinding = binding,
val pressed: Boolean = false,
val callback: MutableSet<KeyBindingCallback> = mutableSetOf(),
) {
var lastChange = 0L
}

View File

@ -0,0 +1,139 @@
/*
* Minosoft
* Copyright (C) 2020-2023 Moritz Zwerger
*
* This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* This software is not affiliated with Mojang AB, the original developer of Minecraft.
*/
package de.bixilon.minosoft.gui.rendering.input.key.manager.binding.actions
import de.bixilon.minosoft.config.key.KeyActions
import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.rendering.RenderConstants
import de.bixilon.minosoft.gui.rendering.input.key.manager.InputManager
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.KeyBindingFilterState
import de.bixilon.minosoft.gui.rendering.input.key.manager.binding.KeyBindingState
interface KeyActionFilter {
fun check(filter: KeyBindingFilterState, codes: Set<KeyCodes>, input: InputManager, name: ResourceLocation, state: KeyBindingState, code: KeyCodes, pressed: Boolean, millis: Long)
object Press : 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) return
filter.satisfied = false
}
}
object Release : 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) return
filter.satisfied = false
}
}
object Change : 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
}
filter.forceNotify = true
}
}
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.skip = true
return
}
if (codes.size == 1) { // optimize if (as most) key has just one modifier key
if (pressed) return
filter.satisfied = false
return
}
if (input.areKeysDown(codes)) return
filter.satisfied = false
}
}
object Sticky : KeyActionFilter {
override fun check(filter: KeyBindingFilterState, codes: Set<KeyCodes>, input: InputManager, name: ResourceLocation, state: KeyBindingState, code: KeyCodes, pressed: Boolean, millis: Long) {
if (!pressed) {
// sticky keys are invoked on press and not on release
filter.skip = true
return
}
if (code !in codes) {
filter.skip = true
return
}
val wasPressed = name in input.bindings
filter.result = !wasPressed
}
}
object DoublePress : KeyActionFilter {
override fun check(filter: KeyBindingFilterState, codes: Set<KeyCodes>, input: InputManager, name: ResourceLocation, state: KeyBindingState, code: KeyCodes, pressed: Boolean, millis: Long) {
if (!pressed) {
filter.skip = true
return
}
if (code !in codes) {
filter.skip = true
return
}
val previous = input.getLastPressed(code)
if (previous < 0L) {
filter.skip = true
return
}
if (millis - previous > RenderConstants.DOUBLE_PRESS_KEY_PRESS_MAX_DELAY) {
filter.skip = true
return
}
if (millis - state.lastChange <= RenderConstants.DOUBLE_PRESS_DELAY_BETWEEN_PRESSED) {
filter.skip = false
return
}
filter.result = input.bindings.isDown(name)
}
}
companion object {
fun KeyActions.filter(): KeyActionFilter {
return when (this) {
KeyActions.PRESS -> Press
KeyActions.RELEASE -> Release
KeyActions.CHANGE -> Change
KeyActions.MODIFIER -> Modifier
KeyActions.STICKY -> Sticky
KeyActions.DOUBLE_PRESS -> DoublePress
}
}
}
}

View File

@ -115,7 +115,7 @@ class WorldRenderer(
}
context.camera.offset::offset.observe(this) { silentlyClearChunkCache() }
context.input.registerKeyCallback("minosoft:clear_chunk_cache".toResourceLocation(), KeyBinding(
context.input.bindings.register("minosoft:clear_chunk_cache".toResourceLocation(), KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F3),
KeyActions.PRESS to setOf(KeyCodes.KEY_A),
)) { clearChunkCache() }

View File

@ -59,12 +59,12 @@ class ChunkBorderRenderer(
get() = mesh == null || !profile.chunkBorder.enabled
override fun init(latch: AbstractLatch) {
context.input.registerKeyCallback(
context.input.bindings.register(
CHUNK_BORDER_TOGGLE_KEY_COMBINATION,
KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F3),
KeyActions.STICKY to setOf(KeyCodes.KEY_G),
), defaultPressed = profile.chunkBorder.enabled
), pressed = profile.chunkBorder.enabled
) {
profile.chunkBorder.enabled = it
connection.util.sendDebugMessage("Chunk borders: ${it.format()}")

View File

@ -18,12 +18,12 @@ import de.bixilon.minosoft.config.DebugOptions
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.data.registries.identified.Namespaces.minosoft
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil
import de.bixilon.minosoft.gui.rendering.RenderContext
import de.bixilon.minosoft.gui.rendering.RenderingStates
import de.bixilon.minosoft.gui.rendering.world.light.debug.LightmapDebugWindow
import de.bixilon.minosoft.util.KUtil.format
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.delegate.JavaFXDelegate.observeFX
class RenderLight(val context: RenderContext) {
@ -34,25 +34,23 @@ class RenderLight(val context: RenderContext) {
fun init() {
map.init()
context.input.registerKeyCallback(
"minosoft:recalculate_light".toResourceLocation(),
KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.PRESS to setOf(KeyCodes.KEY_A),
)
context.input.bindings.register(RECALCULATE, KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.PRESS to setOf(KeyCodes.KEY_A),
)
) {
DefaultThreadPool += {
connection.world.recalculateLight()
connection.util.sendDebugMessage("Light recalculated and chunk cache cleared!")
}
}
context.input.registerKeyCallback(
"minosoft:toggle_fullbright".toResourceLocation(),
context.input.bindings.register(
FULLBRIGHT,
KeyBinding(
KeyActions.MODIFIER to setOf(KeyCodes.KEY_F4),
KeyActions.STICKY to setOf(KeyCodes.KEY_C),
),
defaultPressed = connection.profiles.rendering.light.fullbright,
pressed = connection.profiles.rendering.light.fullbright,
) {
connection.profiles.rendering.light.fullbright = it
connection.util.sendDebugMessage("Fullbright: ${it.format()}")
@ -76,4 +74,9 @@ class RenderLight(val context: RenderContext) {
map.update()
debugWindow?.update()
}
private companion object {
val RECALCULATE = minosoft("recalculate")
val FULLBRIGHT = minosoft("fullbright")
}
}