gui: confirmation dialog, chat/text clicking

This commit is contained in:
Bixilon 2022-02-15 17:04:56 +01:00
parent 2cb688bf7b
commit bdfafd1e76
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
22 changed files with 269 additions and 52 deletions

View File

@ -0,0 +1,21 @@
/*
* 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.data.text.events
import javafx.scene.text.Text
interface ChatEvent {
fun applyJavaFX(text: Text) {}
}

View File

@ -0,0 +1,27 @@
/*
* 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.data.text.events
import de.bixilon.kutil.json.JsonObject
interface ChatEventFactory {
val JsonObject.data: Any
get() {
var text: Any = this
this["value"]?.let { text = it }
this["contents"]?.let { text = it }
return text
}
}

View File

@ -13,20 +13,13 @@
package de.bixilon.minosoft.data.text.events.click
import de.bixilon.kutil.json.JsonObject
import javafx.scene.text.Text
import de.bixilon.minosoft.data.text.events.ChatEvent
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
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 ClickEvent {
interface ClickEvent : ChatEvent {
fun applyJavaFX(text: Text) {}
companion object {
val JsonObject.data: Any
get() {
var text: Any = this
this["value"]?.let { text = it }
this["contents"]?.let { text = it }
return text
}
}
fun onClick(guiRenderer: GUIRenderer, position: Vec2i, button: MouseButtons, action: MouseActions) {}
}

View File

@ -15,8 +15,9 @@ package de.bixilon.minosoft.data.text.events.click
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.registries.factory.name.NameFactory
import de.bixilon.minosoft.data.text.events.ChatEventFactory
interface ClickEventFactory<T : ClickEvent> : NameFactory<T> {
interface ClickEventFactory<T : ClickEvent> : NameFactory<T>, ChatEventFactory {
fun build(json: JsonObject, restrictedMode: Boolean): T
}

View File

@ -14,8 +14,12 @@
package de.bixilon.minosoft.data.text.events.click
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.text.events.hover.HoverEvent.Companion.data
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil.file
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation.OpenFileConfirmationDialog
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons
import glm_.vec2.Vec2i
import javafx.scene.text.Text
class OpenFileClickEvent(
@ -26,6 +30,14 @@ class OpenFileClickEvent(
text.file(path)
}
override fun onClick(guiRenderer: GUIRenderer, position: Vec2i, button: MouseButtons, action: MouseActions) {
if (button != MouseButtons.LEFT || action != MouseActions.PRESS) {
return
}
val dialog = OpenFileConfirmationDialog(guiRenderer, path)
dialog.open()
}
companion object : ClickEventFactory<OpenFileClickEvent> {
override val name: String = "open_file"

View File

@ -16,8 +16,12 @@ package de.bixilon.minosoft.data.text.events.click
import de.bixilon.kutil.json.JsonObject
import de.bixilon.kutil.url.URLUtil.checkWeb
import de.bixilon.kutil.url.URLUtil.toURL
import de.bixilon.minosoft.data.text.events.hover.HoverEvent.Companion.data
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil.hyperlink
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.confirmation.URLConfirmationDialog
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseActions
import de.bixilon.minosoft.gui.rendering.gui.input.mouse.MouseButtons
import glm_.vec2.Vec2i
import javafx.scene.text.Text
import java.net.URL
@ -29,6 +33,14 @@ class OpenURLClickEvent(
text.hyperlink(url.toString())
}
override fun onClick(guiRenderer: GUIRenderer, position: Vec2i, button: MouseButtons, action: MouseActions) {
if (button != MouseButtons.LEFT || action != MouseActions.PRESS) {
return
}
val dialog = URLConfirmationDialog(guiRenderer, url)
dialog.open()
}
companion object : ClickEventFactory<OpenURLClickEvent> {
override val name: String = "open_url"

View File

@ -15,7 +15,6 @@ package de.bixilon.minosoft.data.text.events.click
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.registries.factory.name.MultiNameFactory
import de.bixilon.minosoft.data.text.events.hover.HoverEvent.Companion.data
class SuggestChatClickEvent(
val message: String,

View File

@ -15,7 +15,6 @@ package de.bixilon.minosoft.data.text.events.hover
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.text.events.hover.HoverEvent.Companion.data
import de.bixilon.minosoft.util.KUtil.toResourceLocation
class AchievementHoverEvent(

View File

@ -19,7 +19,6 @@ import de.bixilon.kutil.json.JsonObject
import de.bixilon.kutil.uuid.UUIDUtil.toUUID
import de.bixilon.minosoft.data.registries.ResourceLocation
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.events.hover.HoverEvent.Companion.data
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.json.Jackson
import java.util.*

View File

@ -13,20 +13,6 @@
package de.bixilon.minosoft.data.text.events.hover
import de.bixilon.kutil.json.JsonObject
import javafx.scene.text.Text
import de.bixilon.minosoft.data.text.events.ChatEvent
interface HoverEvent {
fun applyJavaFX(text: Text) {}
companion object {
val JsonObject.data: Any
get() {
var text: Any = this
this["value"]?.let { text = it }
this["contents"]?.let { text = it }
return text
}
}
}
interface HoverEvent : ChatEvent

View File

@ -15,8 +15,9 @@ package de.bixilon.minosoft.data.text.events.hover
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.registries.factory.name.NameFactory
import de.bixilon.minosoft.data.text.events.ChatEventFactory
interface HoverEventFactory<T : HoverEvent> : NameFactory<T> {
interface HoverEventFactory<T : HoverEvent> : NameFactory<T>, ChatEventFactory {
fun build(json: JsonObject, restrictedMode: Boolean): T
}

View File

@ -16,7 +16,6 @@ package de.bixilon.minosoft.data.text.events.hover
import de.bixilon.kutil.json.JsonObject
import de.bixilon.minosoft.data.inventory.ItemStack
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.events.hover.HoverEvent.Companion.data
class ItemHoverEvent(
val item: ItemStack?,

View File

@ -15,7 +15,6 @@ 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.data.text.events.hover.HoverEvent.Companion.data
import javafx.scene.text.Text
class TextHoverEvent(

View File

@ -176,7 +176,7 @@ open class TextElement(
return
}
val text = getTextComponentAt(position) ?: return
println(text) // ToDo: remove
text.clickEvent?.onClick(guiRenderer, position, button, action)
}
private fun getTextComponentAt(position: Vec2i): TextComponent? {

View File

@ -21,6 +21,7 @@ import de.bixilon.minosoft.config.key.KeyCodes
import de.bixilon.minosoft.gui.rendering.gui.GUIElement
import de.bixilon.minosoft.gui.rendering.gui.GUIElementDrawer
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.LayoutedElement
import de.bixilon.minosoft.gui.rendering.gui.elements.Pollable
import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.pause.PauseMenu
import de.bixilon.minosoft.gui.rendering.gui.hud.Initializable
@ -157,21 +158,34 @@ class GUIManager(
}
fun push(builder: GUIBuilder<*>) {
_push(this[builder])
}
private fun _push(element: GUIElement) {
if (elementOrder.isEmpty()) {
renderWindow.inputHandler.inputHandler = guiRenderer
}
val element = this[builder]
elementOrder.firstOrNull()?.onHide()
elementOrder.add(0, element)
element.onOpen()
}
@Deprecated("Only use for dynamic gui (e.g. dialogs, ...)")
fun push(element: LayoutedElement) {
val layouted = LayoutedGUIElement(element)
layouted.init()
layouted.postInit()
_push(layouted)
}
fun pop() {
val previous = elementOrder.removeFirstOrNull() ?: return
previous.onClose()
if (elementOrder.isEmpty()) {
renderWindow.inputHandler.inputHandler = null
}
val now = elementOrder.firstOrNull() ?: return
now.onOpen()
}
fun clear() {

View File

@ -0,0 +1,60 @@
/*
* 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.gui.screen.menu.confirmation
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.HorizontalAlignments
import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.ButtonElement
import de.bixilon.minosoft.gui.rendering.gui.elements.spacer.SpacerElement
import de.bixilon.minosoft.gui.rendering.gui.elements.text.TextElement
import de.bixilon.minosoft.gui.rendering.gui.gui.screen.menu.Menu
import glm_.vec2.Vec2i
abstract class AbstractConfirmationMenu(
guiRenderer: GUIRenderer,
private val text: Any,
private val subtext: Any,
) : Menu(guiRenderer) {
protected abstract fun createButtons(): Array<ButtonElement>
fun close() {
guiRenderer.gui.pop()
}
fun open() {
guiRenderer.gui.push(this)
}
protected fun createCopyToClipboardButton(text: String): ButtonElement {
return ButtonElement(guiRenderer, "Copy to clipboard") {
renderWindow.window.clipboardText = text
close()
}
}
protected fun initButtons() {
add(TextElement(guiRenderer, text, HorizontalAlignments.CENTER, false, scale = 1.5f))
add(TextElement(guiRenderer, subtext, HorizontalAlignments.CENTER, false))
add(SpacerElement(guiRenderer, Vec2i(0, 30)))
for (button in createButtons()) {
add(button)
}
add(ButtonElement(guiRenderer, "Cancel") {
close()
})
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.gui.screen.menu.confirmation
import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.ButtonElement
class OpenFileConfirmationDialog(
guiRenderer: GUIRenderer,
val path: String,
) : AbstractConfirmationMenu(
guiRenderer, "Do you want to open that file?",
TextComponent(path).color(ChatColors.DARK_BLUE),
) {
override fun createButtons(): Array<ButtonElement> {
return arrayOf(
ButtonElement(guiRenderer, "Yes, open it!") {
println("ToDo: Can not open file: $path")
close()
},
createCopyToClipboardButton(path)
)
}
init {
initButtons()
}
}

View File

@ -0,0 +1,55 @@
/*
* 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.gui.screen.menu.confirmation
import de.bixilon.minosoft.data.text.ChatColors
import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil
import de.bixilon.minosoft.gui.rendering.gui.GUIRenderer
import de.bixilon.minosoft.gui.rendering.gui.elements.input.button.ButtonElement
import de.bixilon.minosoft.terminal.RunConfiguration
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import java.net.URL
class URLConfirmationDialog(
guiRenderer: GUIRenderer,
val url: URL,
) : AbstractConfirmationMenu(
guiRenderer, "Do you want to open that link?",
TextComponent(url).color(ChatColors.DARK_BLUE),
) {
override fun createButtons(): Array<ButtonElement> {
return arrayOf(
ButtonElement(guiRenderer, "Yes, open it!") {
if (RunConfiguration.DISABLE_EROS) {
return@ButtonElement Log.log(LogMessageType.GENERAL, LogLevels.INFO) { "Can not open url: $url" }
}
try {
JavaFXUtil.HOST_SERVICES.showDocument(url.toString())
} catch (exception: Throwable) {
exception.printStackTrace()
}
close()
},
createCopyToClipboardButton(url.toString())
)
}
init {
initButtons()
}
}

View File

@ -52,7 +52,7 @@ class DebugMenu(guiRenderer: GUIRenderer) : Menu(guiRenderer) {
companion object : GUIBuilder<LayoutedGUIElement<DebugMenu>> {
override fun build(guiRenderer: GUIRenderer): LayoutedGUIElement<DebugMenu> {
return LayoutedGUIElement((DebugMenu(guiRenderer)))
return LayoutedGUIElement(DebugMenu(guiRenderer))
}
}
}

View File

@ -51,7 +51,7 @@ class PauseMenu(guiRenderer: GUIRenderer) : Menu(guiRenderer) {
companion object : GUIBuilder<LayoutedGUIElement<PauseMenu>> {
override fun build(guiRenderer: GUIRenderer): LayoutedGUIElement<PauseMenu> {
return LayoutedGUIElement((PauseMenu(guiRenderer)))
return LayoutedGUIElement(PauseMenu(guiRenderer))
}
}
}

View File

@ -125,16 +125,14 @@ class ChatElement(guiRenderer: GUIRenderer) : Element(guiRenderer), LayoutedElem
override fun onOpen() {
active = true
input.onOpen()
messages.onOpen()
}
override fun onClose() {
active = false
input.value = ""
}
override fun onHide() {
active = false
input.value = ""
input.onClose()
messages.onClose()
}
override fun onCharPress(char: Int) {
@ -254,8 +252,8 @@ class ChatElement(guiRenderer: GUIRenderer) : Element(guiRenderer), LayoutedElem
companion object : HUDBuilder<LayoutedGUIElement<ChatElement>>, GUIBuilder<LayoutedGUIElement<ChatElement>> {
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:chat_hud".toResourceLocation()
private const val CHAT_INPUT_HEIGHT = Font.TOTAL_CHAR_HEIGHT * 3 + Font.CHAR_MARGIN * 2
private const val CHAT_INPUT_MARGIN = 2
const val CHAT_INPUT_HEIGHT = Font.TOTAL_CHAR_HEIGHT * 3 + Font.CHAR_MARGIN * 2
const val CHAT_INPUT_MARGIN = 2
override fun build(guiRenderer: GUIRenderer): LayoutedGUIElement<ChatElement> {
return LayoutedGUIElement(ChatElement(guiRenderer))

View File

@ -39,7 +39,7 @@ class InternalMessagesElement(guiRenderer: GUIRenderer) : TextFlowElement(guiRen
override val layoutOffset: Vec2i
get() = super.size.let { return@let guiRenderer.scaledSize - Vec2i(it.x, it.y + BOTTOM_OFFSET) }
get() = super.size.let { return@let guiRenderer.scaledSize - Vec2i(it.x, it.y + ChatElement.CHAT_INPUT_HEIGHT + ChatElement.CHAT_INPUT_MARGIN * 2) }
init {
super.prefMaxSize = Vec2i(internalChatProfile.width, internalChatProfile.height)
@ -55,7 +55,6 @@ class InternalMessagesElement(guiRenderer: GUIRenderer) : TextFlowElement(guiRen
companion object : HUDBuilder<LayoutedGUIElement<InternalMessagesElement>> {
override val RESOURCE_LOCATION: ResourceLocation = "minosoft:internal_messages_hud".toResourceLocation()
private const val BOTTOM_OFFSET = 30
override fun build(guiRenderer: GUIRenderer): LayoutedGUIElement<InternalMessagesElement> {
return LayoutedGUIElement(InternalMessagesElement(guiRenderer))