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
}
val dialog = CopyToClipboardDialog(guiRenderer, text)
dialog.open()
dialog.show()
}
companion object : ClickEventFactory<CopyToClipboardClickEvent> {

View File

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

View File

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

View File

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

View File

@ -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
}

View File

@ -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<TextHoverEvent> {
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.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
}

View File

@ -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<TextComponent, Vec2i>? {
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 {

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.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<TextElement> {
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**
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<TextElement, Vec2i>? {
val line = getLineAt(position) ?: return null
return Pair(line.first.textElement, line.second)
}
private fun getAt(position: Vec2i): Pair<TextFlowLineElement, Vec2i>? {
private fun getLineAt(position: Vec2i): Pair<TextFlowLineElement, Vec2i>? {
val reversedY = size.y - position.y
val line = visibleLines.getOrNull(reversedY / Font.TOTAL_CHAR_HEIGHT) ?: return null
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() {
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
}

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.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<Element> {
private val elements: MutableList<Element> = 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<Element, Vec2i>? {
override fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
var element: Element? = null
val delta = Vec2i(position)
val elementWidth = maxElementWidth

View File

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

View File

@ -121,9 +121,10 @@ class LayoutedGUIElement<T : LayoutedElement>(
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<T : LayoutedElement>(
}
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) {

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.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<Element> {
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) {

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.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<Element, Vec2i>? {
override fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
if (position.x < CHAT_INPUT_MARGIN) {
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.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<Element, Vec2i>? {
override fun getAt(position: Vec2i): Pair<Element, Vec2i>? {
if (position.x < ChatElement.CHAT_INPUT_MARGIN) {
return null
}

View File

@ -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

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()) {
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")
},
))