From 986be8fa1a54fd781efde6a324161c6f438794b5 Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Tue, 7 Mar 2023 20:41:12 +0700 Subject: [PATCH] Un-hellish panel focus logic --- .../mc/otm/client/screen/MatteryScreen.kt | 2 +- .../otm/client/screen/panels/EditablePanel.kt | 194 +++++++----------- .../screen/panels/input/EditBoxPanel.kt | 22 +- .../panels/input/NetworkNumberInputPanel.kt | 2 +- .../panels/input/NetworkedStringInputPanel.kt | 6 +- .../screen/panels/input/TextInputPanel.kt | 4 +- 6 files changed, 99 insertions(+), 131 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt index 1f7d24ad1..be943534f 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/MatteryScreen.kt @@ -358,7 +358,7 @@ abstract class MatteryScreen(menu: T, inventory: Inventory, tit for (panel in panels) { if (click || !panel.mouseClickedChecked(x, y, button)) { - focusKilled = panel.killFocusForEverythingExceptInner() || focusKilled + focusKilled = panel.killFocus() || focusKilled } else { if (returnSlot != null) { super.mouseClicked(x, y, button) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt index 5f7f9d528..ff5518306 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/EditablePanel.kt @@ -560,19 +560,23 @@ open class EditablePanel @JvmOverloads constructor( protected open fun onHovered() {} protected open fun onUnHovered() {} - var isFocused = false - set(value) { + var hasFocusedChildren = false + private set(value) { if (field != value) { - val old = field field = value - - onFocusChanged(value, old) - findAbsoluteRoot().updateFocus() + onHierarchicalFocusChanged() } } - var autoKillFocus = true - private var focusedAsParent = false + var isFocusedThis = false + private set(value) { + if (field != value) { + field = value + onFocusChanged() + } + } + + var autoRequestFocus = true val font: Font get() = if (screen is MatteryScreen<*>) screen.font else minecraft.font @@ -1105,15 +1109,24 @@ open class EditablePanel @JvmOverloads constructor( } } - private fun killFocusInternal(): Boolean { - var status = false - + private fun killGrabMouseInput() { for (child in childrenInternal) { - status = child.killFocusInternal() || status + child.killGrabMouseInput() } - if (isFocused) { - isFocused = false + grabMouseInput = false + } + + private fun killFocusThis(children: Boolean): Boolean { + var status = false + + if (isFocusedThis) { + isFocusedThis = false + status = true + } + + if (children && hasFocusedChildren) { + hasFocusedChildren = false status = true } @@ -1125,29 +1138,19 @@ open class EditablePanel @JvmOverloads constructor( return status } - private fun killGrabMouseInput() { - for (child in childrenInternal) { - child.killGrabMouseInput() - } - - grabMouseInput = false - } - fun killFocus(): Boolean { - if (isEverFocused()) { - val status = killFocusInternal() - findAbsoluteRoot().updateFocus() - return status - } else if (isGrabbingMouseInput()) { - killGrabMouseInput() - return true + var status = false + status = killFocusThis(true) || status + + for (child in childrenInternal) { + status = child.killFocus() || status } - return false + return status } fun findHierarchicalFocus(): EditablePanel<*>? { - if (isFocused) { + if (isFocusedThis) { return this } @@ -1166,46 +1169,35 @@ open class EditablePanel @JvmOverloads constructor( return findHierarchicalFocus() != null } - protected open fun onHierarchicalFocusChanged(new: Boolean, old: Boolean) { - - } - - protected open fun onFocusChanged(new: Boolean, old: Boolean) { - - } - - private fun updateFocus() { - val old = focusedAsParent - focusedAsParent = hasHierarchicalFocus() - - if (focusedAsParent != old) { - onHierarchicalFocusChanged(focusedAsParent, old) - } - - for (child in childrenInternal) { - child.updateFocus() - } - } + protected open fun onHierarchicalFocusChanged() {} + protected open fun onFocusChanged() {} fun isEverFocused(): Boolean { - return isFocused || focusedAsParent + return isFocusedThis || hasFocusedChildren } fun requestFocus() { - if (isFocused) { + if (isFocusedThis || !isVisible()) { return } - if (focusedAsParent) { - var child = findHierarchicalFocus() + var filter: EditablePanel<*>? = null + var parent: EditablePanel<*>? = this - while (child != null) { - child.isFocused = false - child = findHierarchicalFocus() + while (parent != null) { + for (child in parent.childrenInternal) { + if (child !== filter) { + child.killFocus() + } } + + filter = parent + parent = parent.parent + parent?.killFocusThis(false) + parent?.hasFocusedChildren = true } - isFocused = true + isFocusedThis = true } protected open fun xPosUpdated(new: Float, old: Float) {} @@ -1311,69 +1303,52 @@ open class EditablePanel @JvmOverloads constructor( y in pos.y .. pos2.y } - fun killFocusForEverythingExcept(except: EditablePanel<*>) { - for (child in childrenInternal) { - if (child !== except) { - child.killFocusForEverythingExceptInner() - } - } - } - - fun killFocusForEverythingExceptInner(): Boolean { - var focusKilled = false - - for (child in childrenInternal) { - focusKilled = child.killFocusForEverythingExceptInner() || focusKilled - } - - if (autoKillFocus) { - focusKilled = killFocus() || focusKilled - } - - return focusKilled - } - final override fun mouseClicked(x: Double, y: Double, button: Int): Boolean { if (!isVisible() || !acceptMouseInput) return false if (flashAnyBlocker()) return true - if (grabMouseInput) return mouseClickedInner(x, y, button) + + if (grabMouseInput) { + if (mouseClickedInner(x, y, button)) { + if (autoRequestFocus) requestFocus() + return true + } + + return false + } for (child in visibleChildrenInternal) { if (child.isGrabbingMouseInput() && child.mouseClickedChecked(x, y, button)) { - killFocusForEverythingExcept(child) return true } } for (child in visibleChildrenInternal) { if (child.mouseClickedChecked(x, y, button)) { - killFocusForEverythingExcept(child) return true } } - return mouseClickedInner(x, y, button) + if (mouseClickedInner(x, y, button)) { + if (autoRequestFocus) requestFocus() + return true + } + + return false } fun mouseClickedChecked(x: Double, y: Double, button: Int): Boolean { if (!isVisible() || !acceptMouseInput) return false if (isGrabbingMouseInput() || withinBounds(x, y)) { - popup() return mouseClicked(x, y, button) } else if (withinExtendedBounds(x, y)) { popup(false) for (child in visibleChildrenInternal) { if (child.mouseClickedChecked(x, y, button)) { - killFocusForEverythingExcept(child) return true } } - - if (autoKillFocus) killFocus() - } else if (autoKillFocus) { - killFocus() } return false @@ -1509,13 +1484,9 @@ open class EditablePanel @JvmOverloads constructor( final override fun keyPressed(key: Int, scancode: Int, mods: Int): Boolean { if (!isVisible() || !acceptKeyboardInput) return false - if (!focusedAsParent) return false - - if (flashAnyBlocker(true)) { - return true - } - - if (isFocused) return keyPressedInternal(key, scancode, mods) + if (!isEverFocused()) return false + if (flashAnyBlocker(true)) return true + if (isFocusedThis) return keyPressedInternal(key, scancode, mods) for (child in visibleChildrenInternal) { if (child.keyPressed(key, scancode, mods)) { @@ -1523,7 +1494,7 @@ open class EditablePanel @JvmOverloads constructor( } } - if (focusedAsParent) return keyPressedInternal(key, scancode, mods) + if (hasFocusedChildren) return keyPressedInternal(key, scancode, mods) return false } @@ -1533,13 +1504,9 @@ open class EditablePanel @JvmOverloads constructor( final override fun keyReleased(key: Int, scancode: Int, mods: Int): Boolean { if (!isVisible() || !acceptKeyboardInput) return false - if (!focusedAsParent) return false - - if (flashAnyBlocker(false)) { - return true - } - - if (isFocused) return keyReleasedInternal(key, scancode, mods) + if (!isEverFocused()) return false + if (flashAnyBlocker(false)) return true + if (isFocusedThis) return keyReleasedInternal(key, scancode, mods) for (child in visibleChildrenInternal) { if (child.keyReleased(key, scancode, mods)) { @@ -1547,7 +1514,7 @@ open class EditablePanel @JvmOverloads constructor( } } - if (focusedAsParent) return keyReleasedInternal(key, scancode, mods) + if (hasFocusedChildren) return keyReleasedInternal(key, scancode, mods) return false } @@ -1557,13 +1524,9 @@ open class EditablePanel @JvmOverloads constructor( final override fun charTyped(codepoint: Char, mods: Int): Boolean { if (!isVisible() || !acceptKeyboardInput) return false - if (!focusedAsParent) return false - - if (flashAnyBlocker()) { - return true - } - - if (isFocused) return charTypedInternal(codepoint, mods) + if (!isEverFocused()) return false + if (flashAnyBlocker(false)) return true + if (isFocusedThis) return charTypedInternal(codepoint, mods) for (child in visibleChildrenInternal) { if (child.charTyped(codepoint, mods)) { @@ -1571,6 +1534,7 @@ open class EditablePanel @JvmOverloads constructor( } } + if (hasFocusedChildren) return charTypedInternal(codepoint, mods) return true } @@ -1638,7 +1602,7 @@ open class EditablePanel @JvmOverloads constructor( screen.popup(this as EditablePanel>) } - if (focus && !isEverFocused()) { + if (focus) { requestFocus() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/EditBoxPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/EditBoxPanel.kt index 2bdebef73..7266ec218 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/EditBoxPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/EditBoxPanel.kt @@ -1,5 +1,6 @@ package ru.dbotthepony.mc.otm.client.screen.panels.input +import com.mojang.blaze3d.platform.InputConstants import net.minecraft.client.gui.components.EditBox import net.minecraft.client.gui.screens.Screen import net.minecraft.network.chat.Component @@ -23,15 +24,11 @@ open class EditBoxPanel( } override fun isFocused(): Boolean { - return this@EditBoxPanel.isFocused + return this@EditBoxPanel.isFocusedThis } } } - init { - autoKillFocus = true - } - override fun copyValues(new_widget: EditBox, old_widget: EditBox) { new_widget.value = old_widget.value } @@ -42,11 +39,11 @@ open class EditBoxPanel( } override fun configureNew(widget: EditBox, recreation: Boolean) { - widget.setFocus(isFocused) + widget.setFocus(isFocusedThis) } - override fun onFocusChanged(new: Boolean, old: Boolean) { - widget?.setFocus(new) + override fun onFocusChanged() { + widget?.setFocus(isFocusedThis) } override fun mouseClickedInner(x: Double, y: Double, button: Int): Boolean { @@ -54,4 +51,13 @@ open class EditBoxPanel( requestFocus() return true } + + override fun keyPressedInternal(key: Int, scancode: Int, mods: Int): Boolean { + if (key == InputConstants.KEY_ESCAPE && widget?.isActive == true) { + widget?.setFocus(false) + return true + } + + return super.keyPressedInternal(key, scancode, mods) + } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkNumberInputPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkNumberInputPanel.kt index b92c12a83..53d3ac70d 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkNumberInputPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkNumberInputPanel.kt @@ -58,7 +58,7 @@ open class NetworkNumberInputPanel @JvmOverloads constructor( override fun tickInner() { super.tickInner() - if (isFocused) { + if (isFocusedThis) { if (!isAvailable.asBoolean) { killFocus() return diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkedStringInputPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkedStringInputPanel.kt index 4026ccdaa..be566b427 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkedStringInputPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/NetworkedStringInputPanel.kt @@ -19,10 +19,10 @@ open class NetworkedStringInputPanel( get() = backend.test(minecraft.player) set(value) {} - override fun onFocusChanged(new: Boolean, old: Boolean) { - super.onFocusChanged(new, old) + override fun onFocusChanged() { + super.onFocusChanged() - if (new && !backend.test(minecraft.player)) { + if (isFocusedThis && !backend.test(minecraft.player)) { killFocus() } } diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt index f9c850265..784eb3605 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/client/screen/panels/input/TextInputPanel.kt @@ -27,7 +27,6 @@ import ru.dbotthepony.mc.otm.client.screen.panels.EditablePanel import ru.dbotthepony.mc.otm.core.addAll import ru.dbotthepony.mc.otm.core.math.RGBAColor import ru.dbotthepony.mc.otm.milliTime -import kotlin.math.roundToInt open class TextInputPanel( screen: S, @@ -149,7 +148,6 @@ open class TextInputPanel( init { scissor = true dockPadding = DockProperty(2f, 2f, 2f, 2f) - autoKillFocus = true } private var oldText = ArrayList() @@ -1138,7 +1136,7 @@ open class TextInputPanel( break } - if (isFocused && milliTime % 1000L > 500L) { + if (isFocusedThis && milliTime % 1000L > 500L) { val activeLine = this[cursorLine] if (activeLine == null || cursorRow >= activeLine.length) {