gui: poppers (wip), mouse movement fixes

This commit is contained in:
Bixilon 2022-02-18 12:58:23 +01:00
parent 9fd1688b39
commit 704de95b94
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
23 changed files with 409 additions and 80 deletions

View File

@ -33,7 +33,7 @@ class CopyToClipboardClickEvent(
return return
} }
val dialog = CopyToClipboardDialog(guiRenderer, text) val dialog = CopyToClipboardDialog(guiRenderer, text)
dialog.open() dialog.show()
} }
companion object : ClickEventFactory<CopyToClipboardClickEvent> { companion object : ClickEventFactory<CopyToClipboardClickEvent> {

View File

@ -40,7 +40,7 @@ class OpenFileClickEvent(
return return
} }
val dialog = OpenFileConfirmationDialog(guiRenderer, path) val dialog = OpenFileConfirmationDialog(guiRenderer, path)
dialog.open() dialog.show()
} }
companion object : ClickEventFactory<OpenFileClickEvent> { companion object : ClickEventFactory<OpenFileClickEvent> {

View File

@ -43,7 +43,7 @@ class OpenURLClickEvent(
return return
} }
val dialog = URLConfirmationDialog(guiRenderer, url) val dialog = URLConfirmationDialog(guiRenderer, url)
dialog.open() dialog.show()
} }
companion object : ClickEventFactory<OpenURLClickEvent> { companion object : ClickEventFactory<OpenURLClickEvent> {

View File

@ -34,7 +34,7 @@ class SendMessageClickEvent(
return return
} }
val dialog = SendMessageDialog(guiRenderer, message) val dialog = SendMessageDialog(guiRenderer, message)
dialog.open() dialog.show()
} }
companion object : ClickEventFactory<SendMessageClickEvent>, MultiNameFactory<SendMessageClickEvent> { companion object : ClickEventFactory<SendMessageClickEvent>, MultiNameFactory<SendMessageClickEvent> {

View File

@ -14,5 +14,12 @@
package de.bixilon.minosoft.data.text.events.hover package de.bixilon.minosoft.data.text.events.hover
import de.bixilon.minosoft.data.text.events.ChatEvent 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
}

View File

@ -15,17 +15,39 @@ package de.bixilon.minosoft.data.text.events.hover
import de.bixilon.kutil.json.JsonObject import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.text.ChatComponent 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 import javafx.scene.text.Text
class TextHoverEvent( class TextHoverEvent(
text: Any?, text: Any?,
) : HoverEvent { ) : HoverEvent {
val text = ChatComponent.of(text) val text = ChatComponent.of(text)
private var popper: TextPopper? = null
override fun applyJavaFX(text: Text) { override fun applyJavaFX(text: Text) {
text.accessibleText = this.text.message 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<TextHoverEvent> { companion object : HoverEventFactory<TextHoverEvent> {
override val name: String = "show_text" override val name: String = "show_text"

View File

@ -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.gui.GUIManager
import de.bixilon.minosoft.gui.rendering.gui.hud.HUDManager 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.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.input.InputHandler
import de.bixilon.minosoft.gui.rendering.modding.events.ResizeWindowEvent import de.bixilon.minosoft.gui.rendering.modding.events.ResizeWindowEvent
import de.bixilon.minosoft.gui.rendering.renderer.Renderer import de.bixilon.minosoft.gui.rendering.renderer.Renderer
@ -47,6 +48,7 @@ class GUIRenderer(
var scaledSize: Vec2i = renderWindow.window.size var scaledSize: Vec2i = renderWindow.window.size
val gui = GUIManager(this) val gui = GUIManager(this)
val hud = HUDManager(this) val hud = HUDManager(this)
val popper = PopperManager(this)
var matrix: Mat4 = Mat4() var matrix: Mat4 = Mat4()
private set private set
var matrixChange = true var matrixChange = true
@ -64,6 +66,7 @@ class GUIRenderer(
atlasManager.init() atlasManager.init()
gui.init() gui.init()
hud.init() hud.init()
popper.init()
} }
override fun postInit(latch: CountUpAndDownLatch) { override fun postInit(latch: CountUpAndDownLatch) {
@ -77,6 +80,7 @@ class GUIRenderer(
gui.postInit() gui.postInit()
hud.postInit() hud.postInit()
popper.postInit()
} }
private fun recalculateMatrices(windowSize: Vec2i = renderWindow.window.size, scale: Float = profile.scale) { private fun recalculateMatrices(windowSize: Vec2i = renderWindow.window.size, scale: Float = profile.scale) {
@ -86,6 +90,7 @@ class GUIRenderer(
gui.onMatrixChange() gui.onMatrixChange()
hud.onMatrixChange() hud.onMatrixChange()
popper.onMatrixChange()
} }
fun setup() { fun setup() {
@ -119,6 +124,7 @@ class GUIRenderer(
override fun drawOther() { override fun drawOther() {
hud.draw() hud.draw()
gui.draw() gui.draw()
popper.draw()
if (this.matrixChange) { if (this.matrixChange) {
this.matrixChange = false this.matrixChange = false
} }

View File

@ -47,6 +47,7 @@ open class TextElement(
parent: Element? = null, parent: Element? = null,
scale: Float = 1.0f, scale: Float = 1.0f,
) : Element(guiRenderer, text.toString().length * 6 * GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX), Labeled { ) : Element(guiRenderer, text.toString().length * 6 * GUIMesh.GUIMeshStruct.FLOATS_PER_VERTEX), Labeled {
private var activeElement: TextComponent? = null
lateinit var renderInfo: TextRenderInfo lateinit var renderInfo: TextRenderInfo
private set private set
@ -175,12 +176,36 @@ open class TextElement(
if (action != MouseActions.PRESS || button != MouseButtons.LEFT) { if (action != MouseActions.PRESS || button != MouseButtons.LEFT) {
return true return true
} }
val text = getTextComponentAt(position) ?: return false val pair = getTextComponentAt(position) ?: return false
text.clickEvent?.onClick(guiRenderer, position, button, action) pair.first.clickEvent?.onClick(guiRenderer, pair.second, button, action)
return true 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<TextComponent, Vec2i>? {
val offset = Vec2i(position) val offset = Vec2i(position)
val line = renderInfo.lines.getOrNull(offset.y / charHeight) ?: return null val line = renderInfo.lines.getOrNull(offset.y / charHeight) ?: return null
offset.y = offset.y % charHeight offset.y = offset.y % charHeight
@ -191,7 +216,7 @@ open class TextElement(
offset.x += fontAlignment.getOffset(size.x, line.width) 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 { override fun toString(): String {

View File

@ -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.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.Element 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.elements.primitive.ColorElement
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions import de.bixilon.minosoft.gui.rendering.gui.gui.ActiveMouseMove
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.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY import de.bixilon.minosoft.gui.rendering.util.vec.vec2.Vec2iUtil.EMPTY
@ -34,9 +33,10 @@ import glm_.vec2.Vec2i
open class TextFlowElement( open class TextFlowElement(
guiRenderer: GUIRenderer, guiRenderer: GUIRenderer,
var messageExpireTime: Long, var messageExpireTime: Long,
) : Element(guiRenderer) { ) : Element(guiRenderer), ActiveMouseMove<TextElement> {
private val messages: MutableList<TextFlowTextElement> = synchronizedListOf() // all messages **from newest to oldest** private val messages: MutableList<TextFlowTextElement> = synchronizedListOf() // all messages **from newest to oldest**
private var visibleLines: List<TextFlowLineElement> = listOf() // all visible lines **from bottom to top** private var visibleLines: List<TextFlowLineElement> = listOf() // all visible lines **from bottom to top**
override var activeElement: TextElement? = null
private val background = ColorElement(guiRenderer, size, RenderConstants.TEXT_BACKGROUND_COLOR) 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 { override fun getAt(position: Vec2i): Pair<TextElement, Vec2i>? {
val pair = getAt(position) ?: return false val line = getLineAt(position) ?: return null
pair.first.textElement.onMouseAction(pair.second, button, action) return Pair(line.first.textElement, line.second)
return true
} }
private fun getAt(position: Vec2i): Pair<TextFlowLineElement, Vec2i>? { private fun getLineAt(position: Vec2i): Pair<TextFlowLineElement, Vec2i>? {
val reversedY = size.y - position.y val reversedY = size.y - position.y
val line = visibleLines.getOrNull(reversedY / Font.TOTAL_CHAR_HEIGHT) ?: return null val line = visibleLines.getOrNull(reversedY / Font.TOTAL_CHAR_HEIGHT) ?: return null
if (position.x > line.textElement.size.x) { if (position.x > line.textElement.size.x) {

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<T : Element> : InputElement {
var activeElement: T?
fun getAt(position: Vec2i): Pair<T, Vec2i>?
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)
}
}

View File

@ -80,6 +80,11 @@ class GUIManager(
fun draw() { fun draw() {
val order = elementOrder.reversed() 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()) { for ((index, element) in order.withIndex()) {
if (!element.enabled) { if (!element.enabled) {
continue continue
@ -87,8 +92,7 @@ class GUIManager(
if (index != order.size - 1 && !element.activeWhenHidden) { if (index != order.size - 1 && !element.activeWhenHidden) {
continue continue
} }
val time = TimeUtil.time if (tick) {
if (time - lastTickTime > ProtocolDefinition.TICK_TIME) {
element.tick() element.tick()
if (element is Pollable) { if (element is Pollable) {
if (element.poll()) { if (element.poll()) {
@ -220,6 +224,7 @@ class GUIManager(
previous.onClose() previous.onClose()
if (elementOrder.isEmpty()) { if (elementOrder.isEmpty()) {
renderWindow.inputHandler.inputHandler = null renderWindow.inputHandler.inputHandler = null
guiRenderer.popper.clear()
} }
val now = elementOrder.firstOrNull() ?: return val now = elementOrder.firstOrNull() ?: return
now.onOpen() now.onOpen()
@ -230,6 +235,7 @@ class GUIManager(
element.onClose() element.onClose()
} }
elementOrder.clear() elementOrder.clear()
guiRenderer.popper.clear()
renderWindow.inputHandler.inputHandler = null renderWindow.inputHandler.inputHandler = null
} }

View File

@ -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.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer 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.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.gui.screen.Screen
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions 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.input.mouse.MouseButtons
@ -29,13 +30,13 @@ import glm_.vec2.Vec2i
abstract class Menu( abstract class Menu(
guiRenderer: GUIRenderer, guiRenderer: GUIRenderer,
val preferredElementWidth: Int = 150, val preferredElementWidth: Int = 150,
) : Screen(guiRenderer) { ) : Screen(guiRenderer), ActiveMouseMove<Element> {
private val elements: MutableList<Element> = mutableListOf() private val elements: MutableList<Element> = mutableListOf()
private var maxElementWidth = -1 private var maxElementWidth = -1
private var totalHeight = -1 private var totalHeight = -1
private var activeElement: Element? = null override var activeElement: Element? = null
override fun forceSilentApply() { override fun forceSilentApply() {
val elementWidth = maxOf(minOf(preferredElementWidth, size.x / 3), 0) 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 { override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean {
val (element, delta) = getAt(position) ?: return true val (element, delta) = getAt(position) ?: return true
element.onMouseAction(delta, button, action) element.onMouseAction(delta, button, action)
@ -111,7 +86,7 @@ abstract class Menu(
forceSilentApply() forceSilentApply()
} }
fun getAt(position: Vec2i): Pair<Element, Vec2i>? { override fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
var element: Element? = null var element: Element? = null
val delta = Vec2i(position) val delta = Vec2i(position)
val elementWidth = maxElementWidth val elementWidth = maxElementWidth

View File

@ -34,7 +34,7 @@ abstract class AbstractConfirmationMenu(
guiRenderer.gui.pop() guiRenderer.gui.pop()
} }
fun open() { fun show() {
guiRenderer.gui.push(this) guiRenderer.gui.push(this)
} }

View File

@ -121,9 +121,10 @@ class LayoutedGUIElement<T : LayoutedElement>(
return elementLayout.onMouseLeave() return elementLayout.onMouseLeave()
} }
val delta = position - offset val delta = position - offset
val previousOutside = lastPosition == INVALID_MOUSE_POSITION
this.lastPosition = delta this.lastPosition = delta
if (lastPosition.isOutside(offset, size)) { if (previousOutside) {
return elementLayout.onMouseEnter(delta) return elementLayout.onMouseEnter(delta)
} }
@ -135,10 +136,7 @@ class LayoutedGUIElement<T : LayoutedElement>(
} }
override fun onKeyPress(type: KeyChangeTypes, key: KeyCodes): Boolean { override fun onKeyPress(type: KeyChangeTypes, key: KeyCodes): Boolean {
val mouseButton = MouseButtons[key] val mouseButton = MouseButtons[key] ?: return elementLayout.onKey(key, type)
if (mouseButton == null) {
return elementLayout.onKey(key, type)
}
val position = lastPosition val position = lastPosition
if (position == INVALID_MOUSE_POSITION) { if (position == INVALID_MOUSE_POSITION) {

View File

@ -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.Element
import de.bixilon.minosoft.gui.rendering.gui.elements.LayoutedElement 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.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.hud.Initializable
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexConsumer 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.mesh.GUIVertexOptions
@ -24,16 +25,16 @@ import de.bixilon.minosoft.gui.rendering.renderer.Drawable
import glm_.vec2.Vec2d import glm_.vec2.Vec2d
import glm_.vec2.Vec2i 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<Element> {
protected val connection = renderWindow.connection protected val connection = renderWindow.connection
protected val profile = connection.profiles.gui protected val profile = connection.profiles.gui
protected val messages = TextFlowElement(guiRenderer, 20000).apply { parent = this@AbstractChatElement } protected val messages = TextFlowElement(guiRenderer, 20000).apply { parent = this@AbstractChatElement }
override var activeElement: Element? = null
override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) { override fun forceRender(offset: Vec2i, consumer: GUIVertexConsumer, options: GUIVertexOptions?) {
messages.render(offset + Vec2i(ChatElement.CHAT_INPUT_MARGIN, 0), consumer, options) messages.render(offset + Vec2i(ChatElement.CHAT_INPUT_MARGIN, 0), consumer, options)
} }
override fun onScroll(position: Vec2i, scrollOffset: Vec2d): Boolean { override fun onScroll(position: Vec2i, scrollOffset: Vec2d): Boolean {
val size = messages.size val size = messages.size
if (position.y > size.y || position.x > messages.size.x) { if (position.y > size.y || position.x > messages.size.x) {

View File

@ -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.gui.elements.input.TextInputElement
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder 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.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.GUIVertexConsumer
import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions import de.bixilon.minosoft.gui.rendering.gui.mesh.GUIVertexOptions
import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
@ -202,13 +200,7 @@ class ChatElement(guiRenderer: GUIRenderer) : AbstractChatElement(guiRenderer) {
return true return true
} }
override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { override fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
val pair = getAt(position) ?: return false
pair.first.onMouseAction(pair.second, button, action)
return true
}
private fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
if (position.x < CHAT_INPUT_MARGIN) { if (position.x < CHAT_INPUT_MARGIN) {
return null return null
} }

View File

@ -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.gui.GUIBuilder
import de.bixilon.minosoft.gui.rendering.gui.hud.elements.HUDBuilder 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.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.events.InternalMessageReceiveEvent
import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker import de.bixilon.minosoft.modding.event.invoker.CallbackEventInvoker
import de.bixilon.minosoft.util.KUtil.toResourceLocation import de.bixilon.minosoft.util.KUtil.toResourceLocation
@ -82,13 +80,7 @@ class InternalChatElement(guiRenderer: GUIRenderer) : AbstractChatElement(guiRen
guiRenderer.gui.pop() // pop normal chat guiRenderer.gui.pop() // pop normal chat
} }
override fun onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions): Boolean { override fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
val pair = getAt(position) ?: return false
pair.first.onMouseAction(pair.second, button, action)
return true
}
private fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
if (position.x < ChatElement.CHAT_INPUT_MARGIN) { if (position.x < ChatElement.CHAT_INPUT_MARGIN) {
return null return null
} }

View File

@ -20,11 +20,8 @@ import de.bixilon.minosoft.gui.rendering.system.window.KeyChangeTypes
import glm_.vec2.Vec2d import glm_.vec2.Vec2d
import glm_.vec2.Vec2i 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 onMouseAction(position: Vec2i, button: MouseButtons, action: MouseActions) = false
fun onScroll(position: Vec2i, scrollOffset: Vec2d) = false fun onScroll(position: Vec2i, scrollOffset: Vec2d) = false

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<LayoutedGUIElement<Popper>> = 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<LayoutedGUIElement<Popper>> = 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()
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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)
}
}

View File

@ -52,7 +52,7 @@ class ScreenshotTaker(
while (File(path).exists()) { while (File(path).exists()) {
path = "${basePath}_${i++}.png" path = "${basePath}_${i++}.png"
if (i > MAX_FILES_CHECK) { 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 { TextComponent("[DELETE]").apply {
color = ChatColors.RED color = ChatColors.RED
bold() 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") hoverEvent = TextHoverEvent("Click to delete screenshot")
}, },
)) ))