1818package com.lambda.config.settings.complex
1919
2020import com.google.gson.reflect.TypeToken
21+ import com.lambda.brigadier.CommandResult.Companion.failure
22+ import com.lambda.brigadier.CommandResult.Companion.success
23+ import com.lambda.brigadier.argument.boolean
2124import com.lambda.brigadier.argument.value
2225import com.lambda.brigadier.argument.word
23- import com.lambda.brigadier.execute
26+ import com.lambda.brigadier.executeWithResult
27+ import com.lambda.brigadier.optional
2428import com.lambda.brigadier.required
2529import com.lambda.config.AbstractSetting
30+ import com.lambda.config.settings.complex.Bind.Companion.mouseBind
2631import com.lambda.gui.dsl.ImGuiBuilder
32+ import com.lambda.util.InputUtils
2733import com.lambda.util.KeyCode
28- import com.lambda.util.KeyboardUtils
34+ import com.lambda.util.Mouse
2935import com.lambda.util.StringUtils.capitalize
3036import com.lambda.util.extension.CommandBuilder
3137import imgui.ImGui.isMouseClicked
3238import imgui.flag.ImGuiCol
3339import imgui.flag.ImGuiHoveredFlags
3440import imgui.flag.ImGuiMouseButton
3541import net.minecraft.command.CommandRegistryAccess
36- import org.lwjgl.glfw.GLFW
42+ import org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT
43+ import org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SUPER
44+ import org.lwjgl.glfw.GLFW.GLFW_MOD_ALT
45+ import org.lwjgl.glfw.GLFW.GLFW_MOD_CAPS_LOCK
46+ import org.lwjgl.glfw.GLFW.GLFW_MOD_CONTROL
47+ import org.lwjgl.glfw.GLFW.GLFW_MOD_NUM_LOCK
48+ import org.lwjgl.glfw.GLFW.GLFW_MOD_SHIFT
49+ import org.lwjgl.glfw.GLFW.GLFW_MOD_SUPER
3750
3851class KeybindSetting (
3952 override var name : String ,
40- defaultValue : KeyCode ,
53+ defaultValue : Bind ,
4154 description : String ,
4255 visibility : () -> Boolean ,
43- ) : AbstractSetting<KeyCode >(
56+ ) : AbstractSetting<Bind >(
4457 name,
4558 defaultValue,
46- TypeToken .get(KeyCode ::class.java).type,
59+ TypeToken .get(Bind ::class.java).type,
4760 description,
4861 visibility
4962) {
5063 private var listening = false
5164
5265 override fun ImGuiBuilder.buildLayout () {
53- val key = value
54- val scancode = if (key == KeyCode . UNBOUND ) - 69 else GLFW .glfwGetKeyScancode(key.code)
55- val translated = if (key == KeyCode . UNBOUND ) key else KeyCode .virtualMapUS(key.code, scancode)
56- val preview = if (listening) " $name : Press any key… " else " $ name: $translated "
66+ val bind = value
67+ val preview =
68+ if (listening) " $name : Press any key… "
69+ else bind. name
5770
5871 if (listening) {
5972 withStyleColor(ImGuiCol .Button , 0.20f , 0.50f , 1.00f , 1.00f ) {
@@ -68,16 +81,8 @@ class KeybindSetting(
6881 }
6982
7083 lambdaTooltip {
71- if (! listening) {
72- description.ifBlank { " Click to set. Right-click to unbind. Esc cancels. Backspace/Delete unbinds." }
73- } else {
74- " Listening… Press a key to bind. Esc to cancel. Backspace/Delete to unbind."
75- }
76- }
77-
78- onItemClick(ImGuiMouseButton .Right ) {
79- value = KeyCode .UNBOUND
80- listening = false
84+ if (! listening) description.ifBlank { " Click to set. Esc cancels. Backspace/Delete unbinds." }
85+ else " Listening… Press a key to bind. Esc to cancel. Backspace/Delete to unbind."
8186 }
8287
8388 if (listening && ! isAnyItemHovered && isMouseClicked(ImGuiMouseButton .Left )) {
@@ -86,38 +91,108 @@ class KeybindSetting(
8691
8792 sameLine()
8893 smallButton(" Unbind" ) {
89- value = KeyCode . UNBOUND
94+ value = Bind . EMPTY
9095 listening = false
9196 }
9297 onItemHover(ImGuiHoveredFlags .Stationary ) {
9398 lambdaTooltip(" Clear binding" )
9499 }
95100
96- val poll = KeyboardUtils .lastEvent
97- if (listening && poll.isPressed) {
98- when (val key = poll.translated) {
99- KeyCode .ESCAPE -> listening = false
100- KeyCode .BACKSPACE , KeyCode .DELETE -> {
101- value = KeyCode .UNBOUND
101+ if (listening) {
102+ InputUtils .newMouseEvent()
103+ ?.let {
104+ value = Bind (0 , it.modifiers, it.button)
102105 listening = false
106+ return
103107 }
104- else -> {
105- value = key
106- listening = false
108+
109+ InputUtils .newKeyboardEvent()
110+ ?.let {
111+ val isModKey = it.keyCode in GLFW_KEY_LEFT_SHIFT .. GLFW_KEY_RIGHT_SUPER
112+
113+ // If a mod key is pressed first ignore it unless it was released without any other keys
114+ if ((it.isPressed && ! isModKey) || (it.isReleased && isModKey)) {
115+ when (it.translated) {
116+ KeyCode .ESCAPE -> {}
117+ KeyCode .BACKSPACE , KeyCode .DELETE -> value = Bind .EMPTY
118+ else -> value = Bind (it.keyCode, it.modifiers, - 1 )
119+ }
120+
121+ listening = false
122+ }
123+
124+ return
107125 }
108- }
109126 }
110127 }
111128
112129 override fun CommandBuilder.buildCommand (registry : CommandRegistryAccess ) {
113- required(word(name)) { parameter ->
130+ required(word(name)) { name ->
114131 suggests { _, builder ->
115132 KeyCode .entries.forEach { builder.suggest(it.name.capitalize()) }
133+ (1 .. 10 ).forEach { builder.suggest(it) }
116134 builder.buildFuture()
117135 }
118- execute {
119- trySetValue(KeyCode .valueOf(parameter().value()))
136+ optional(boolean(" mouse button" )) { isMouseButton ->
137+ executeWithResult {
138+ val isMouse = if (isMouseButton != null ) isMouseButton().value() else false
139+ var bind = Bind .EMPTY
140+ if (isMouse) {
141+ val num = try {
142+ name().value().toInt()
143+ } catch (_: NumberFormatException ) {
144+ return @executeWithResult failure(" ${name().value()} doesn't match with a mouse button" )
145+ }
146+ bind = mouseBind(num)
147+ } else {
148+ bind = try {
149+ Bind (KeyCode .valueOf(name().value()).code, 0 )
150+ } catch (_: IllegalArgumentException ) {
151+ return @executeWithResult failure(" ${name().value()} doesn't match with a bind" )
152+ }
153+ }
154+
155+ trySetValue(bind)
156+ return @executeWithResult success()
157+ }
120158 }
121159 }
122160 }
123161}
162+
163+ data class Bind (
164+ val key : Int ,
165+ val modifiers : Int ,
166+ val mouse : Int = -1 ,
167+ ) {
168+ val truemods = buildList {
169+ if (modifiers and GLFW_MOD_SHIFT != 0 ) add(KeyCode .LEFT_SHIFT )
170+ if (modifiers and GLFW_MOD_CONTROL != 0 ) add(KeyCode .LEFT_CONTROL )
171+ if (modifiers and GLFW_MOD_ALT != 0 ) add(KeyCode .LEFT_ALT )
172+ if (modifiers and GLFW_MOD_SUPER != 0 ) add(KeyCode .LEFT_SUPER )
173+ if (modifiers and GLFW_MOD_CAPS_LOCK != 0 ) add(KeyCode .CAPS_LOCK )
174+ if (modifiers and GLFW_MOD_NUM_LOCK != 0 ) add(KeyCode .NUM_LOCK )
175+ }
176+
177+ val name: String
178+ get() {
179+ if (mouse < 0 && modifiers <= 0 && key <= 0 ) return " Unbound"
180+
181+ val list = mutableListOf<Any >()
182+
183+ if (mouse >= 0 ) list.add(Mouse .entries[mouse])
184+ if (modifiers > 0 ) list.add(truemods.joinToString(separator = " +" ) { it.name })
185+ if (key > 0 ) list.add(KeyCode .fromKeyCode(key))
186+
187+ return list.joinToString(separator = " +" ) { it.toString() }
188+ }
189+
190+ override fun toString () =
191+ " Key Code: $key , Modifiers: ${truemods.joinToString(separator = " +" ) { it.name }} , Mouse Button: ${Mouse .entries.getOrNull(mouse) ? : " None" } "
192+
193+ companion object {
194+ val EMPTY = Bind (0 , 0 )
195+
196+ fun mouseBind (code : Int ) = Bind (0 , 0 , code)
197+ }
198+ }
0 commit comments