Skip to content

Commit

Permalink
outsource javafx text component rendering
Browse files Browse the repository at this point in the history
  • Loading branch information
Bixilon committed Nov 23, 2023
1 parent 89e4254 commit e236585
Show file tree
Hide file tree
Showing 7 changed files with 135 additions and 101 deletions.
10 changes: 5 additions & 5 deletions src/main/java/de/bixilon/minosoft/Minosoft.kt
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,14 @@ object Minosoft {
val taskWorker = TaskWorker(errorHandler = { _, error -> error.printStackTrace(); error.crash() }, forcePool = true)

MinosoftBoot.register(taskWorker)

taskWorker += WorkerTask(identifier = BootTasks.LANGUAGE_FILES, dependencies = arrayOf(BootTasks.PROFILES), executor = this::loadLanguageFiles)

if (!RunConfiguration.DISABLE_EROS) {
javafx(taskWorker)
}
if (RunConfiguration.DISABLE_EROS && !RunConfiguration.DISABLE_RENDERING) {
// eros is disabled, but rendering not, force initialize the desktop, otherwise eros will do so
// eros is disabled, but rendering not, force initialize the desktop, because eros won't
DefaultThreadPool += { SystemUtil.api = DesktopAPI() }
}

Expand Down Expand Up @@ -160,9 +161,8 @@ object Minosoft {
}

private fun checkMacOS() {
if (RunConfiguration.X_START_ON_FIRST_THREAD_SET && (!RunConfiguration.DISABLE_RENDERING || !RunConfiguration.DISABLE_EROS)) {
Log.log(LogMessageType.GENERAL, LogLevels.WARN) { "You are using macOS. To use rendering you must not set the jvm argument §9-XstartOnFirstThread§r. Please remove it!" }
ShutdownManager.shutdown(reason = AbstractShutdownReason.CRASH)
}
if (!RunConfiguration.X_START_ON_FIRST_THREAD_SET || !(!RunConfiguration.DISABLE_RENDERING || !RunConfiguration.DISABLE_EROS)) return
Log.log(LogMessageType.GENERAL, LogLevels.WARN) { "You are using macOS. To use rendering you must not set the jvm argument §9-XstartOnFirstThread§r. Please remove it!" }
ShutdownManager.shutdown(reason = AbstractShutdownReason.CRASH)
}
}
15 changes: 5 additions & 10 deletions src/main/java/de/bixilon/minosoft/data/text/BaseComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,8 @@ import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.KUtil.format
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.nbt.tag.NBTUtil.get
import javafx.collections.ObservableList
import javafx.scene.Node

class BaseComponent : ChatComponent {
class BaseComponent : ChatComponent, Iterable<ChatComponent> {
val parts: MutableList<ChatComponent> = mutableListOf()

constructor(parts: MutableList<ChatComponent>) {
Expand Down Expand Up @@ -165,13 +163,6 @@ class BaseComponent : ChatComponent {
return stringBuilder.toString()
}

override fun getJavaFXText(nodes: ObservableList<Node>): ObservableList<Node> {
for (part in parts) {
part.getJavaFXText(nodes)
}
return nodes
}

override fun obfuscate(): BaseComponent {
for (part in parts) part.obfuscate(); return this
}
Expand Down Expand Up @@ -284,4 +275,8 @@ class BaseComponent : ChatComponent {

return parts.toTypedArray()
}

override fun iterator(): Iterator<ChatComponent> {
return parts.iterator()
}
}
14 changes: 0 additions & 14 deletions src/main/java/de/bixilon/minosoft/data/text/ChatComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,6 @@ import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.gui.eros.util.JavaFXUtil.text
import de.bixilon.minosoft.util.json.Jackson
import javafx.collections.FXCollections
import javafx.collections.ObservableList
import javafx.scene.Node
import javafx.scene.text.TextFlow

/**
Expand All @@ -48,17 +45,6 @@ interface ChatComponent {

fun getJson(): Any

/**
* @return Returns a list of Nodes, drawable in JavaFX (TextFlow)
*/
fun getJavaFXText(nodes: ObservableList<Node>): ObservableList<Node>

/**
* @return Returns a list of Nodes, drawable in JavaFX (TextFlow)
*/
val javaFXText: ObservableList<Node>
get() = getJavaFXText(FXCollections.observableArrayList())

val textFlow: TextFlow
get() {
val textFlow = TextFlow()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,14 @@
package de.bixilon.minosoft.data.text

import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import javafx.collections.ObservableList
import javafx.scene.Node

object EmptyComponent : ChatComponent {
override val ansi: String get() = ""
override val legacy: String get() = ""
override val message: String get() = ""

override fun getJson(): Any = emptyList<Any>()

override fun getJavaFXText(nodes: ObservableList<Node>): ObservableList<Node> = nodes

override fun setFallbackColor(color: RGBColor) = this

override fun getTextAt(pointer: Int): TextComponent = throw IllegalArgumentException()
Expand Down
67 changes: 0 additions & 67 deletions src/main/java/de/bixilon/minosoft/data/text/TextComponent.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ package de.bixilon.minosoft.data.text

import de.bixilon.kutil.enums.BitEnumSet
import de.bixilon.kutil.json.MutableJsonObject
import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager
import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.data.text.events.click.ClickEvent
import de.bixilon.minosoft.data.text.events.hover.HoverEvent
Expand All @@ -23,15 +22,6 @@ import de.bixilon.minosoft.data.text.formatting.TextStyle
import de.bixilon.minosoft.data.text.formatting.color.ChatColors
import de.bixilon.minosoft.data.text.formatting.color.RGBColor
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import javafx.animation.Animation
import javafx.animation.KeyFrame
import javafx.animation.Timeline
import javafx.collections.ObservableList
import javafx.scene.Node
import javafx.scene.paint.Color
import javafx.scene.text.Text
import javafx.util.Duration
import java.util.concurrent.atomic.AtomicInteger


open class TextComponent(
Expand Down Expand Up @@ -118,63 +108,6 @@ open class TextComponent(
return builder.toString()
}

override fun getJavaFXText(nodes: ObservableList<Node>): ObservableList<Node> {
val text = Text(this.message)
val color = this.color
if (color == null) {
text.styleClass += "text-default-color"
} else {
if (ErosProfileManager.selected.text.colored) {
text.fill = Color.rgb(color.red, color.green, color.blue)
}
}
if (FormattingCodes.OBFUSCATED in formatting) {
// ToDo: This is just slow
val obfuscatedTimeline = if (ErosProfileManager.selected.text.obfuscated) {
val index = AtomicInteger()
Timeline(
KeyFrame(Duration.millis(50.0), {
val chars = text.text.toCharArray()
for (i in chars.indices) {
chars[i] = ProtocolDefinition.OBFUSCATED_CHARS[index.getAndIncrement() % ProtocolDefinition.OBFUSCATED_CHARS.size]
}
text.text = String(chars)
}),
)
} else {
Timeline(
KeyFrame(Duration.millis(500.0), {
text.isVisible = false
}),
KeyFrame(Duration.millis(1000.0), {
text.isVisible = true
}),
)
}

obfuscatedTimeline.cycleCount = Animation.INDEFINITE
obfuscatedTimeline.play()
text.styleClass.add("obfuscated")
}
if (FormattingCodes.BOLD in formatting) {
text.style += "-fx-font-weight: bold;"
}
if (FormattingCodes.STRIKETHROUGH in formatting) {
text.style += "-fx-strikethrough: true;"
}
if (FormattingCodes.UNDERLINED in formatting) {
text.style += "-fx-underline: true;"
}
if (FormattingCodes.ITALIC in formatting) {
text.style += "-fx-font-style: italic;"
}
nodes.add(text)

clickEvent?.applyJavaFX(text)
hoverEvent?.applyJavaFX(text)
return nodes
}

override fun getJson(): Any {
if (message.isEmpty()) {
return emptyMap<String, Any>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import de.bixilon.minosoft.data.registries.identified.ResourceLocation
import de.bixilon.minosoft.gui.eros.controller.EmbeddedJavaFXController
import de.bixilon.minosoft.gui.eros.controller.JavaFXController
import de.bixilon.minosoft.gui.eros.controller.JavaFXWindowController
import de.bixilon.minosoft.gui.eros.util.text.JavaFXTextRenderer
import de.bixilon.minosoft.util.KUtil.toResourceLocation
import de.bixilon.minosoft.util.crash.freeze.FreezeDumpUtil
import de.bixilon.minosoft.util.delegate.JavaFXDelegate.observeFX
Expand Down Expand Up @@ -148,7 +149,7 @@ object JavaFXUtil {
var TextFlow.text: Any?
get() = TODO("Can not get the text of a TextFlow (yet)")
set(value) {
this.children.setAll(IntegratedLanguage.LANGUAGE.translate(value).javaFXText)
this.children.setAll(JavaFXTextRenderer.render(IntegratedLanguage.LANGUAGE.translate(value)))
}

var TextField.placeholder: Any?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
/*
* Minosoft
* Copyright (C) 2020-2023 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.eros.util.text

import de.bixilon.minosoft.config.profile.profiles.eros.ErosProfileManager
import de.bixilon.minosoft.data.text.BaseComponent
import de.bixilon.minosoft.data.text.ChatComponent
import de.bixilon.minosoft.data.text.EmptyComponent
import de.bixilon.minosoft.data.text.TextComponent
import de.bixilon.minosoft.data.text.formatting.FormattingCodes
import de.bixilon.minosoft.protocol.protocol.ProtocolDefinition
import de.bixilon.minosoft.util.logging.Log
import de.bixilon.minosoft.util.logging.LogLevels
import de.bixilon.minosoft.util.logging.LogMessageType
import javafx.animation.Animation
import javafx.animation.KeyFrame
import javafx.animation.Timeline
import javafx.scene.Node
import javafx.scene.paint.Color
import javafx.scene.text.Text
import javafx.util.Duration
import java.util.concurrent.atomic.AtomicInteger

interface JavaFXTextRenderer<C> {

fun render(nodes: MutableList<Node>, text: C)


object BaseComponentRenderer : JavaFXTextRenderer<BaseComponent> {
override fun render(nodes: MutableList<Node>, text: BaseComponent) {
for (part in text.parts) {
render(nodes, part)
}
}
}

object TextComponentRenderer : JavaFXTextRenderer<TextComponent> {
override fun render(nodes: MutableList<Node>, text: TextComponent) {
val node = Text(text.message)
val color = text.color
if (color == null) {
node.styleClass += "text-default-color"
} else {
if (ErosProfileManager.selected.text.colored) {
node.fill = Color.rgb(color.red, color.green, color.blue)
}
}
if (FormattingCodes.OBFUSCATED in text.formatting) {
// ToDo: This is just slow
val obfuscatedTimeline = if (ErosProfileManager.selected.text.obfuscated) {
val index = AtomicInteger()
Timeline(
KeyFrame(Duration.millis(50.0), {
val chars = node.text.toCharArray()
for (i in chars.indices) {
chars[i] = ProtocolDefinition.OBFUSCATED_CHARS[index.getAndIncrement() % ProtocolDefinition.OBFUSCATED_CHARS.size]
}
node.text = String(chars)
}),
)
} else {
Timeline(
KeyFrame(Duration.millis(500.0), {
node.isVisible = false
}),
KeyFrame(Duration.millis(1000.0), {
node.isVisible = true
}),
)
}

obfuscatedTimeline.cycleCount = Animation.INDEFINITE
obfuscatedTimeline.play()
node.styleClass.add("obfuscated")
}
if (FormattingCodes.BOLD in text.formatting) {
node.style += "-fx-font-weight: bold;"
}
if (FormattingCodes.STRIKETHROUGH in text.formatting) {
node.style += "-fx-strikethrough: true;"
}
if (FormattingCodes.UNDERLINED in text.formatting) {
node.style += "-fx-underline: true;"
}
if (FormattingCodes.ITALIC in text.formatting) {
node.style += "-fx-font-style: italic;"
}
nodes.add(node)

text.clickEvent?.applyJavaFX(node)
text.hoverEvent?.applyJavaFX(node)
}
}

companion object : JavaFXTextRenderer<ChatComponent> {

fun render(text: ChatComponent): MutableList<Node> {
val nodes: MutableList<Node> = mutableListOf()
render(nodes, text)

return nodes
}

override fun render(nodes: MutableList<Node>, text: ChatComponent) = when (text) {
is EmptyComponent -> Unit
is BaseComponent -> BaseComponentRenderer.render(nodes, text)
is TextComponent -> TextComponentRenderer.render(nodes, text)
else -> Log.log(LogMessageType.OTHER, LogLevels.WARN) { "Can not render $text" }
}
}
}

0 comments on commit e236585

Please sign in to comment.