From 704de95b948ac4d56a12b5572cefff6d0fbc4ed6 Mon Sep 17 00:00:00 2001 From: Bixilon Date: Fri, 18 Feb 2022 12:58:23 +0100 Subject: [PATCH] gui: poppers (wip), mouse movement fixes --- .../events/click/CopyToClipboardClickEvent.kt | 2 +- .../text/events/click/OpenFileClickEvent.kt | 2 +- .../text/events/click/OpenURLClickEvent.kt | 2 +- .../events/click/SendMessageClickEvent.kt | 2 +- .../data/text/events/hover/HoverEvent.kt | 9 +- .../data/text/events/hover/TextHoverEvent.kt | 22 +++ .../minosoft/gui/rendering/gui/GUIRenderer.kt | 6 + .../gui/elements/text/TextElement.kt | 33 +++- .../gui/elements/text/TextFlowElement.kt | 15 +- .../gui/rendering/gui/gui/ActiveMouseMove.kt | 54 +++++++ .../gui/rendering/gui/gui/GUIManager.kt | 10 +- .../gui/rendering/gui/gui/screen/menu/Menu.kt | 33 +--- .../confirmation/AbstractConfirmationMenu.kt | 2 +- .../gui/hud/elements/LayoutedGUIElement.kt | 8 +- .../hud/elements/chat/AbstractChatElement.kt | 5 +- .../gui/hud/elements/chat/ChatElement.kt | 10 +- .../hud/elements/chat/InternalChatElement.kt | 10 +- .../gui/rendering/gui/input/InputElement.kt | 5 +- .../rendering/gui/input/MouseInputElement.kt | 23 +++ .../gui/rendering/gui/popper/Popper.kt | 49 ++++++ .../gui/rendering/gui/popper/PopperManager.kt | 148 ++++++++++++++++++ .../rendering/gui/popper/text/TextPopper.kt | 35 +++++ .../gui/rendering/util/ScreenshotTaker.kt | 4 +- 23 files changed, 409 insertions(+), 80 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/ActiveMouseMove.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/MouseInputElement.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/Popper.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/PopperManager.kt create mode 100644 src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/text/TextPopper.kt diff --git a/src/main/java/de/bixilon/minosoft/data/text/events/click/CopyToClipboardClickEvent.kt b/src/main/java/de/bixilon/minosoft/data/text/events/click/CopyToClipboardClickEvent.kt index fbb766992..8bd3a2cf6 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/events/click/CopyToClipboardClickEvent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/events/click/CopyToClipboardClickEvent.kt @@ -33,7 +33,7 @@ class CopyToClipboardClickEvent( return } val dialog = CopyToClipboardDialog(guiRenderer, text) - dialog.open() + dialog.show() } companion object : ClickEventFactory { diff --git a/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenFileClickEvent.kt b/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenFileClickEvent.kt index b968c6ce4..e7c293d63 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenFileClickEvent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenFileClickEvent.kt @@ -40,7 +40,7 @@ class OpenFileClickEvent( return } val dialog = OpenFileConfirmationDialog(guiRenderer, path) - dialog.open() + dialog.show() } companion object : ClickEventFactory { diff --git a/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenURLClickEvent.kt b/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenURLClickEvent.kt index 928e8a905..4ff5e78e6 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenURLClickEvent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/events/click/OpenURLClickEvent.kt @@ -43,7 +43,7 @@ class OpenURLClickEvent( return } val dialog = URLConfirmationDialog(guiRenderer, url) - dialog.open() + dialog.show() } companion object : ClickEventFactory { diff --git a/src/main/java/de/bixilon/minosoft/data/text/events/click/SendMessageClickEvent.kt b/src/main/java/de/bixilon/minosoft/data/text/events/click/SendMessageClickEvent.kt index dfeb1bdba..9234e20f6 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/events/click/SendMessageClickEvent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/events/click/SendMessageClickEvent.kt @@ -34,7 +34,7 @@ class SendMessageClickEvent( return } val dialog = SendMessageDialog(guiRenderer, message) - dialog.open() + dialog.show() } companion object : ClickEventFactory, MultiNameFactory { diff --git a/src/main/java/de/bixilon/minosoft/data/text/events/hover/HoverEvent.kt b/src/main/java/de/bixilon/minosoft/data/text/events/hover/HoverEvent.kt index 51a571b03..a68d600ed 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/events/hover/HoverEvent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/events/hover/HoverEvent.kt @@ -14,5 +14,12 @@ package de.bixilon.minosoft.data.text.events.hover import de.bixilon.minosoft.data.text.events.ChatEvent +import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer +import glm_.vec2.Vec2i -interface HoverEvent : ChatEvent +interface HoverEvent : ChatEvent { + + fun onMouseEnter(guiRenderer: GUIRenderer, position: Vec2i) = false + fun onMouseMove(guiRenderer: GUIRenderer, position: Vec2i) = false + fun onMouseLeave(guiRenderer: GUIRenderer) = false +} diff --git a/src/main/java/de/bixilon/minosoft/data/text/events/hover/TextHoverEvent.kt b/src/main/java/de/bixilon/minosoft/data/text/events/hover/TextHoverEvent.kt index 78d6bf8d5..27d563a61 100644 --- a/src/main/java/de/bixilon/minosoft/data/text/events/hover/TextHoverEvent.kt +++ b/src/main/java/de/bixilon/minosoft/data/text/events/hover/TextHoverEvent.kt @@ -15,17 +15,39 @@ package de.bixilon.minosoft.data.text.events.hover import de.bixilon.kutil.json.JsonObject import de.bixilon.minosoft.data.text.ChatComponent +import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer +import de.bixilon.minosoft.gui.rendering.gui.popper.text.TextPopper +import glm_.vec2.Vec2i import javafx.scene.text.Text class TextHoverEvent( text: Any?, ) : HoverEvent { val text = ChatComponent.of(text) + private var popper: TextPopper? = null override fun applyJavaFX(text: Text) { text.accessibleText = this.text.message } + override fun onMouseEnter(guiRenderer: GUIRenderer, position: Vec2i): Boolean { + val popper = TextPopper(guiRenderer, position, text) + popper.show() + this.popper = popper + return true + } + + override fun onMouseMove(guiRenderer: GUIRenderer, position: Vec2i): Boolean { + popper?.position = position + return true + } + + override fun onMouseLeave(guiRenderer: GUIRenderer): Boolean { + this.popper?.let { guiRenderer.popper -= it } + this.popper = null + return true + } + companion object : HoverEventFactory { override val name: String = "show_text" diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/GUIRenderer.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/GUIRenderer.kt index bdbd2b5b5..c995532b9 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/GUIRenderer.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/GUIRenderer.kt @@ -21,6 +21,7 @@ import de.bixilon.minosoft.gui.rendering.gui.atlas.AtlasManager import de.bixilon.minosoft.gui.rendering.gui.gui.GUIManager import de.bixilon.minosoft.gui.rendering.gui.hud.HUDManager import de.bixilon.minosoft.gui.rendering.gui.input.ModifierKeys +import de.bixilon.minosoft.gui.rendering.gui.popper.PopperManager import de.bixilon.minosoft.gui.rendering.input.InputHandler import de.bixilon.minosoft.gui.rendering.modding.events.ResizeWindowEvent import de.bixilon.minosoft.gui.rendering.renderer.Renderer @@ -47,6 +48,7 @@ class GUIRenderer( var scaledSize: Vec2i = renderWindow.window.size val gui = GUIManager(this) val hud = HUDManager(this) + val popper = PopperManager(this) var matrix: Mat4 = Mat4() private set var matrixChange = true @@ -64,6 +66,7 @@ class GUIRenderer( atlasManager.init() gui.init() hud.init() + popper.init() } override fun postInit(latch: CountUpAndDownLatch) { @@ -77,6 +80,7 @@ class GUIRenderer( gui.postInit() hud.postInit() + popper.postInit() } private fun recalculateMatrices(windowSize: Vec2i = renderWindow.window.size, scale: Float = profile.scale) { @@ -86,6 +90,7 @@ class GUIRenderer( gui.onMatrixChange() hud.onMatrixChange() + popper.onMatrixChange() } fun setup() { @@ -119,6 +124,7 @@ class GUIRenderer( override fun drawOther() { hud.draw() gui.draw() + popper.draw() if (this.matrixChange) { this.matrixChange = false } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt index e3be43af6..11948b1ce 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextElement.kt @@ -47,6 +47,7 @@ open class TextElement( parent: Element? = null, scale: Float = 1.0f, ) : Element(guiRenderer, text.toString().length * 6 * GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX), Labeled { + private var activeElement: TextComponent? = null lateinit var renderInfo: TextRenderInfo private set @@ -175,12 +176,36 @@ open class TextElement( if (action != MouseActions.PRESS || button != MouseButtons.LEFT) { return true } - val text = getTextComponentAt(position) ?: return false - text.clickEvent?.onClick(guiRenderer, position, button, action) + val pair = getTextComponentAt(position) ?: return false + pair.first.clickEvent?.onClick(guiRenderer, pair.second, button, action) return true } - private fun getTextComponentAt(position: Vec2i): TextComponent? { + override fun onMouseEnter(position: Vec2i): Boolean { + val pair = getTextComponentAt(position) ?: return false + activeElement = pair.first + pair.first.hoverEvent?.onMouseEnter(guiRenderer, pair.second) + return true + } + + override fun onMouseMove(position: Vec2i): Boolean { + val pair = getTextComponentAt(position) + + if (activeElement != pair?.first) { + val activeElement = activeElement + this.activeElement = pair?.first + return (activeElement?.hoverEvent?.onMouseLeave(guiRenderer) ?: false) || (pair?.first?.hoverEvent?.onMouseEnter(guiRenderer, pair.second) ?: false) + } + return pair?.first?.hoverEvent?.onMouseMove(guiRenderer, pair.second) ?: false + } + + override fun onMouseLeave(): Boolean { + activeElement?.hoverEvent?.onMouseLeave(guiRenderer) ?: return false + activeElement = null + return true + } + + private fun getTextComponentAt(position: Vec2i): Pair? { val offset = Vec2i(position) val line = renderInfo.lines.getOrNull(offset.y / charHeight) ?: return null offset.y = offset.y % charHeight @@ -191,7 +216,7 @@ open class TextElement( offset.x += fontAlignment.getOffset(size.x, line.width) - return line.text.getTextAt(textElement.renderInfo.lines.getOrNull(0)?.text?.message?.length ?: return null) + return Pair(line.text.getTextAt(textElement.renderInfo.lines.getOrNull(0)?.text?.message?.length ?: return null), offset) } override fun toString(): String { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt index 083763dcf..c1ad0c97b 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/elements/text/TextFlowElement.kt @@ -22,8 +22,7 @@ import de.bixilon.minosoft.gui.rendering.font.Font import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.elements.Element import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ColorElement -import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions -import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons +import de.bixilon.minosoft.gui.rendering.gui.gui.ActiveMouseMove import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY @@ -34,9 +33,10 @@ import glm_.vec2.Vec2i open class TextFlowElement( guiRenderer: GUIRenderer, var messageExpireTime: Long, -) : Element(guiRenderer) { +) : Element(guiRenderer), ActiveMouseMove { private val messages: MutableList = synchronizedListOf() // all messages **from newest to oldest** private var visibleLines: List = listOf() // all visible lines **from bottom to top** + override var activeElement: TextElement? = null private val background = ColorElement(guiRenderer, size, RenderConstants.TEXT_BACKGROUND_COLOR) @@ -181,13 +181,12 @@ open class TextFlowElement( } } - override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { - val pair = getAt(position) ?: return false - pair.first.textElement.onMouseAction(pair.second, button, action) - return true + override fun getAt(position: Vec2i): Pair? { + val line = getLineAt(position) ?: return null + return Pair(line.first.textElement, line.second) } - private fun getAt(position: Vec2i): Pair? { + private fun getLineAt(position: Vec2i): Pair? { val reversedY = size.y - position.y val line = visibleLines.getOrNull(reversedY / Font.TOTAL_CHAR_HEIGHT) ?: return null if (position.x > line.textElement.size.x) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/ActiveMouseMove.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/ActiveMouseMove.kt new file mode 100644 index 000000000..d514deaca --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/ActiveMouseMove.kt @@ -0,0 +1,54 @@ +/* + * Minosoft + * Copyright (C) 2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.gui.gui + +import de.bixilon.minosoft.gui.rendering.gui.elements.Element +import de.bixilon.minosoft.gui.rendering.gui.input.InputElement +import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions +import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons +import glm_.vec2.Vec2i + +interface ActiveMouseMove : InputElement { + var activeElement: T? + + fun getAt(position: Vec2i): Pair? + + override fun onMouseEnter(position: Vec2i): Boolean { + val pair = getAt(position) + activeElement = pair?.first + return pair?.first?.onMouseEnter(pair.second) ?: false + } + + override fun onMouseMove(position: Vec2i): Boolean { + val pair = getAt(position) + + if (activeElement != pair?.first) { + val activeElement = activeElement + this.activeElement = pair?.first + return (activeElement?.onMouseLeave() ?: false) || (pair?.first?.onMouseEnter(pair.second) ?: false) + } + return pair?.first?.onMouseMove(pair.second) ?: false + } + + override fun onMouseLeave(): Boolean { + val activeElement = activeElement + this.activeElement = null + return activeElement?.onMouseLeave() ?: false + } + + override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { + val pair = getAt(position) ?: return false + return pair.first.onMouseAction(pair.second, button, action) + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIManager.kt index 90b077d4e..48148dbae 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIManager.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/GUIManager.kt @@ -80,6 +80,11 @@ class GUIManager( fun draw() { val order = elementOrder.reversed() + val time = TimeUtil.time + val tick = time - lastTickTime > ProtocolDefinition.TICK_TIME + if (tick) { + lastTickTime = time + } for ((index, element) in order.withIndex()) { if (!element.enabled) { continue @@ -87,8 +92,7 @@ class GUIManager( if (index != order.size - 1 && !element.activeWhenHidden) { continue } - val time = TimeUtil.time - if (time - lastTickTime > ProtocolDefinition.TICK_TIME) { + if (tick) { element.tick() if (element is Pollable) { if (element.poll()) { @@ -220,6 +224,7 @@ class GUIManager( previous.onClose() if (elementOrder.isEmpty()) { renderWindow.inputHandler.inputHandler = null + guiRenderer.popper.clear() } val now = elementOrder.firstOrNull() ?: return now.onOpen() @@ -230,6 +235,7 @@ class GUIManager( element.onClose() } elementOrder.clear() + guiRenderer.popper.clear() renderWindow.inputHandler.inputHandler = null } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/Menu.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/Menu.kt index 38e896d72..b46e445a0 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/Menu.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/Menu.kt @@ -16,6 +16,7 @@ package de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu import de.bixilon.minosoft.config.key.KeyCodes import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.elements.Element +import de.bixilon.minosoft.gui.rendering.gui.gui.ActiveMouseMove import de.bixilon.minosoft.gui.rendering.gui.gui.screen.Screen import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons @@ -29,13 +30,13 @@ import glm_.vec2.Vec2i abstract class Menu( guiRenderer: GUIRenderer, val preferredElementWidth: Int = 150, -) : Screen(guiRenderer) { +) : Screen(guiRenderer), ActiveMouseMove { private val elements: MutableList = mutableListOf() private var maxElementWidth = -1 private var totalHeight = -1 - private var activeElement: Element? = null + override var activeElement: Element? = null override fun forceSilentApply() { val elementWidth = maxOf(minOf(preferredElementWidth, size.x / 3), 0) @@ -75,32 +76,6 @@ abstract class Menu( } } - override fun onMouseLeave(): Boolean { - activeElement?.onMouseLeave() - activeElement = null - return true - } - - override fun onMouseMove(position: Vec2i): Boolean { - val pair = getAt(position) - - if (activeElement != pair?.first) { - activeElement?.onMouseLeave() - pair?.first?.onMouseEnter(pair.second) - activeElement = pair?.first - return true - } - pair?.first?.onMouseMove(pair.second) - return true - } - - override fun onMouseEnter(position: Vec2i): Boolean { - val pair = getAt(position) - pair?.first?.onMouseEnter(pair.second) - activeElement = pair?.first - return true - } - override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { val (element, delta) = getAt(position) ?: return true element.onMouseAction(delta, button, action) @@ -111,7 +86,7 @@ abstract class Menu( forceSilentApply() } - fun getAt(position: Vec2i): Pair? { + override fun getAt(position: Vec2i): Pair? { var element: Element? = null val delta = Vec2i(position) val elementWidth = maxElementWidth diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/confirmation/AbstractConfirmationMenu.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/confirmation/AbstractConfirmationMenu.kt index 521a3160a..28cfbc03f 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/confirmation/AbstractConfirmationMenu.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/gui/screen/menu/confirmation/AbstractConfirmationMenu.kt @@ -34,7 +34,7 @@ abstract class AbstractConfirmationMenu( guiRenderer.gui.pop() } - fun open() { + fun show() { guiRenderer.gui.push(this) } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/LayoutedGUIElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/LayoutedGUIElement.kt index b3d9b0088..66d3d9288 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/LayoutedGUIElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/LayoutedGUIElement.kt @@ -121,9 +121,10 @@ class LayoutedGUIElement( return elementLayout.onMouseLeave() } val delta = position - offset + val previousOutside = lastPosition == INVALID_MOUSE_POSITION this.lastPosition = delta - if (lastPosition.isOutside(offset, size)) { + if (previousOutside) { return elementLayout.onMouseEnter(delta) } @@ -135,10 +136,7 @@ class LayoutedGUIElement( } override fun onKeyPress(type: KeyChangeTypes, key: KeyCodes): Boolean { - val mouseButton = MouseButtons[key] - if (mouseButton == null) { - return elementLayout.onKey(key, type) - } + val mouseButton = MouseButtons[key] ?: return elementLayout.onKey(key, type) val position = lastPosition if (position == INVALID_MOUSE_POSITION) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/AbstractChatElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/AbstractChatElement.kt index 901091336..bdd84f246 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/AbstractChatElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/AbstractChatElement.kt @@ -17,6 +17,7 @@ import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer import de.bixilon.minosoft.gui.rendering.gui.elements.Element import de.bixilon.minosoft.gui.rendering.gui.elements.LayoutedElement import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextFlowElement +import de.bixilon.minosoft.gui.rendering.gui.gui.ActiveMouseMove import de.bixilon.minosoft.gui.rendering.gui.hud.Initializable import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions @@ -24,16 +25,16 @@ import de.bixilon.minosoft.gui.rendering.renderer.Drawable import glm_.vec2.Vec2d import glm_.vec2.Vec2i -abstract class AbstractChatElement(guiRenderer: GUIRenderer) : Element(guiRenderer), LayoutedElement, Initializable, Drawable { +abstract class AbstractChatElement(guiRenderer: GUIRenderer) : Element(guiRenderer), LayoutedElement, Initializable, Drawable, ActiveMouseMove { protected val connection = renderWindow.connection protected val profile = connection.profiles.gui protected val messages = TextFlowElement(guiRenderer, 20000).apply { parent = this@AbstractChatElement } + override var activeElement: Element? = null override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { messages.render(offset + Vec2i(ChatElement.CHAT_INPUT_MARGIN, 0), consumer, options) } - override fun onScroll(position: Vec2i, scrollOffset: Vec2d): Boolean { val size = messages.size if (position.y > size.y || position.x > messages.size.x) { diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/ChatElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/ChatElement.kt index ca3c572b2..1549f9569 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/ChatElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/ChatElement.kt @@ -27,8 +27,6 @@ import de.bixilon.minosoft.gui.rendering.gui.gui.GUIBuilder import de.bixilon.minosoft.gui.rendering.gui.gui.elements.input.TextInputElement import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder import de.bixilon.minosoft.gui.rendering.gui.hud.elements.LayoutedGUIElement -import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions -import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes @@ -202,13 +200,7 @@ class ChatElement(guiRenderer: GUIRenderer) : AbstractChatElement(guiRenderer) { return true } - override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { - val pair = getAt(position) ?: return false - pair.first.onMouseAction(pair.second, button, action) - return true - } - - private fun getAt(position: Vec2i): Pair? { + override fun getAt(position: Vec2i): Pair? { if (position.x < CHAT_INPUT_MARGIN) { return null } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/InternalChatElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/InternalChatElement.kt index c2290de97..e178ed323 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/InternalChatElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/hud/elements/chat/InternalChatElement.kt @@ -21,8 +21,6 @@ import de.bixilon.minosoft.gui.rendering.gui.elements.Element import de.bixilon.minosoft.gui.rendering.gui.gui.GUIBuilder import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder import de.bixilon.minosoft.gui.rendering.gui.hud.elements.LayoutedGUIElement -import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions -import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons import de.bixilon.minosoft.modding.event.events.InternalMessageReceiveEvent import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.util.KUtil.toResourceLocation @@ -82,13 +80,7 @@ class InternalChatElement(guiRenderer: GUIRenderer) : AbstractChatElement(guiRen guiRenderer.gui.pop() // pop normal chat } - override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { - val pair = getAt(position) ?: return false - pair.first.onMouseAction(pair.second, button, action) - return true - } - - private fun getAt(position: Vec2i): Pair? { + override fun getAt(position: Vec2i): Pair? { if (position.x < ChatElement.CHAT_INPUT_MARGIN) { return null } diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/InputElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/InputElement.kt index 3b2f9cef4..1ea54d144 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/InputElement.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/InputElement.kt @@ -20,11 +20,8 @@ import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes import glm_.vec2.Vec2d import glm_.vec2.Vec2i -interface InputElement { +interface InputElement : MouseInputElement { - fun onMouseMove(position: Vec2i) = false - fun onMouseEnter(position: Vec2i) = false - fun onMouseLeave() = false fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions) = false fun onScroll(position: Vec2i, scrollOffset: Vec2d) = false diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/MouseInputElement.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/MouseInputElement.kt new file mode 100644 index 000000000..6a9636a18 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/input/MouseInputElement.kt @@ -0,0 +1,23 @@ +/* + * Minosoft + * Copyright (C) 2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.gui.input + +import glm_.vec2.Vec2i + +interface MouseInputElement { + + fun onMouseEnter(position: Vec2i) = false + fun onMouseMove(position: Vec2i) = false + fun onMouseLeave() = false +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/Popper.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/Popper.kt new file mode 100644 index 000000000..950883043 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/Popper.kt @@ -0,0 +1,49 @@ +/* + * Minosoft + * Copyright (C) 2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.gui.popper + +import de.bixilon.minosoft.data.text.ChatColors +import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer +import de.bixilon.minosoft.gui.rendering.gui.elements.Element +import de.bixilon.minosoft.gui.rendering.gui.elements.LayoutedElement +import de.bixilon.minosoft.gui.rendering.gui.elements.primitive.ColorElement +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions +import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY +import glm_.vec2.Vec2i + +abstract class Popper( + guiRenderer: GUIRenderer, + var position: Vec2i, +) : Element(guiRenderer), LayoutedElement { + private val background = ColorElement(guiRenderer, Vec2i.EMPTY, color = ChatColors.YELLOW) + open var dead = false + override val layoutOffset: Vec2i = position + + override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { + background.render(offset, consumer, options) + } + + override fun forceSilentApply() { + background.size = size + } + + fun show() { + guiRenderer.popper += this + } + + fun hide() { + dead = true + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/PopperManager.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/PopperManager.kt new file mode 100644 index 000000000..77e51f559 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/PopperManager.kt @@ -0,0 +1,148 @@ +/* + * Minosoft + * Copyright (C) 2020-2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.gui.popper + +import de.bixilon.kutil.time.TimeUtil +import de.bixilon.minosoft.config.key.KeyCodes +import de.bixilon.minosoft.gui.rendering.gui.GUIElementDrawer +import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer +import de.bixilon.minosoft.gui.rendering.gui.hud.Initializable +import de.bixilon.minosoft.gui.rendering.gui.hud.elements.LayoutedGUIElement +import de.bixilon.minosoft.gui.rendering.input.InputHandler +import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes +import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition +import glm_.vec2.Vec2d +import glm_.vec2.Vec2i + +class PopperManager( + override val guiRenderer: GUIRenderer, +) : Initializable, InputHandler, GUIElementDrawer { + var poppers: MutableList> = mutableListOf() + private val renderWindow = guiRenderer.renderWindow + override var lastTickTime: Long = -1L + + + fun onMatrixChange() { + for (element in poppers) { + element.layout.forceSilentApply() + } + } + + fun draw() { + val toRemove: MutableSet> = mutableSetOf() + val time = TimeUtil.time + val tick = time - lastTickTime > ProtocolDefinition.TICK_TIME + if (tick) { + lastTickTime = time + } + + for (popper in poppers) { + if (popper.layout.dead) { + toRemove += popper + popper.onClose() + continue + } + if (tick) { + popper.tick() + } + + if (!popper.skipDraw) { + popper.draw() + } + popper.prepare() + + guiRenderer.setup() + if (!popper.enabled || popper.mesh.data.isEmpty) { + continue + } + popper.mesh.draw() + } + } + + override fun onCharPress(char: Int): Boolean { + for ((index, element) in poppers.toList().withIndex()) { + if (index != 0 && !element.activeWhenHidden) { + continue + } + if (element.onCharPress(char)) { + return true + } + } + return false + } + + override fun onMouseMove(position: Vec2i): Boolean { + for ((index, element) in poppers.toList().withIndex()) { + if (index != 0 && !element.activeWhenHidden) { + continue + } + if (element.onMouseMove(position)) { + return true + } + } + return false + } + + override fun onKeyPress(type: KeyChangeTypes, key: KeyCodes): Boolean { + for ((index, element) in poppers.toList().withIndex()) { + if (index != 0 && !element.activeWhenHidden) { + continue + } + if (element.onKeyPress(type, key)) { + return true + } + } + return false + } + + override fun onScroll(scrollOffset: Vec2d): Boolean { + for ((index, element) in poppers.toList().withIndex()) { + if (index != 0 && !element.activeWhenHidden) { + continue + } + if (element.onScroll(scrollOffset)) { + return true + } + } + return false + } + + fun add(popper: Popper) { + poppers += LayoutedGUIElement(popper) + popper.onOpen() + } + + operator fun plusAssign(popper: Popper) { + add(popper) + } + + fun remove(popper: Popper?) { + for (popperEntry in poppers) { + if (popperEntry.layout == popper) { + poppers.remove(popperEntry) + popperEntry.onClose() + break + } + } + } + + operator fun minusAssign(popper: Popper?) = remove(popper) + + fun clear() { + for (popper in poppers) { + popper.onClose() + } + poppers.clear() + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/text/TextPopper.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/text/TextPopper.kt new file mode 100644 index 000000000..c4e6b6008 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/gui/popper/text/TextPopper.kt @@ -0,0 +1,35 @@ +/* + * Minosoft + * Copyright (C) 2022 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 . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.gui.rendering.gui.popper.text + +import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer +import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer +import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions +import de.bixilon.minosoft.gui.rendering.gui.popper.Popper +import glm_.vec2.Vec2i + +class TextPopper( + guiRenderer: GUIRenderer, + position: Vec2i, + text: Any, +) : Popper(guiRenderer, position) { + private val textElement = TextElement(guiRenderer, text, parent = this) + + override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { + super.forceRender(offset, consumer, options) + + textElement.render(offset, consumer, options) + } +} diff --git a/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt b/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt index 99ada7a31..8f433e1f3 100644 --- a/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt +++ b/src/main/java/de/bixilon/minosoft/gui/rendering/util/ScreenshotTaker.kt @@ -52,7 +52,7 @@ class ScreenshotTaker( while (File(path).exists()) { path = "${basePath}_${i++}.png" if (i > MAX_FILES_CHECK) { - throw StackOverflowError("There are already > $MAX_FILES_CHECK screenshots with this date! Please try again!") + throw StackOverflowError("There are already > $MAX_FILES_CHECK screenshots with this date! Please try again later!") } } @@ -87,7 +87,7 @@ class ScreenshotTaker( TextComponent("[DELETE]").apply { color = ChatColors.RED bold() - clickEvent = ClickCallbackClickEvent { DeleteScreenshotDialog(renderWindow.renderer[GUIRenderer] ?: return@ClickCallbackClickEvent, file).open() } + clickEvent = ClickCallbackClickEvent { DeleteScreenshotDialog(renderWindow.renderer[GUIRenderer] ?: return@ClickCallbackClickEvent, file).show() } hoverEvent = TextHoverEvent("Click to delete screenshot") }, ))