From 147e6330966bc5edc73b9399cf15c77e30c95405 Mon Sep 17 00:00:00 2001 From: Stan <10871975+dosier@users.noreply.github.com> Date: Tue, 16 Apr 2024 19:57:23 +0200 Subject: [PATCH] Feature/label mapping (#14) * Upgraded kotlin, javafx plugin and various deps to latest version * Added support for label mapping * Added support for label mapping --- build.gradle.kts | 14 ++-- src/main/kotlin/stan/qodat/Properties.kt | 6 ++ .../stan/qodat/scene/control/LabeledHBox.kt | 76 +++++++++++++++++-- .../scene/runescape/animation/Animation.kt | 22 ++++-- .../scene/runescape/entity/AnimatedEntity.kt | 7 +- .../qodat/scene/runescape/entity/Entity.kt | 5 +- .../stan/qodat/scene/runescape/entity/Item.kt | 6 +- .../stan/qodat/scene/runescape/entity/NPC.kt | 6 +- .../qodat/scene/runescape/entity/Object.kt | 6 +- .../scene/runescape/entity/SpotAnimation.kt | 4 +- .../stan/qodat/scene/runescape/model/Model.kt | 2 +- .../scene/runescape/ui/InterfaceGroup.kt | 4 +- .../stan/qodat/scene/runescape/ui/Sprite.kt | 2 +- src/main/kotlin/stan/qodat/util/Labels.kt | 39 +++++++++- 14 files changed, 159 insertions(+), 40 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index bf08812..dbeb08b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - id("org.openjfx.javafxplugin") version "0.0.13" + id("org.openjfx.javafxplugin") version "0.1.0" kotlin("jvm") version "1.9.10" kotlin("plugin.serialization") version "1.9.10" application @@ -56,16 +56,16 @@ val javaFXOptions = the() dependencies { implementation(project("qodat-api")) - implementation("org.jsoup:jsoup:1.15.4") + implementation("org.jsoup:jsoup:1.17.2") implementation("us.ihmc:ihmc-javafx-toolkit:17-0.21.2") implementation("org.jcodec:jcodec:0.2.5") implementation("org.jcodec:jcodec-javase:0.2.5") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.6.4") - implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.8.0") + implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.5.0") implementation("org.orbisgis:poly2tri-core:0.1.2") - implementation("com.displee:rs-cache-library:6.9") + implementation("com.displee:rs-cache-library:7.1.3") implementation("org.joml:joml-primitives:1.10.0") implementation("org.joml:joml:1.10.5") implementation("no.tornado:tornadofx:1.7.20") diff --git a/src/main/kotlin/stan/qodat/Properties.kt b/src/main/kotlin/stan/qodat/Properties.kt index 2366c04..2787d7d 100644 --- a/src/main/kotlin/stan/qodat/Properties.kt +++ b/src/main/kotlin/stan/qodat/Properties.kt @@ -93,6 +93,7 @@ object Properties { val osrsCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/OS/rev203")) val qodatCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/qodat")) val legacyCachePath = SimpleObjectProperty(rootPath.get().resolve("caches/667")) + val labelMappingPath = SimpleObjectProperty(rootPath.get().resolve("labels.json")) val downloadsPath = SimpleObjectProperty(rootPath.get().resolve("downloads")) @@ -139,6 +140,8 @@ object Properties { val copyModelsFromNpc = SimpleBooleanProperty(true) val copyAnimationsFromNpc = SimpleBooleanProperty(true) + val showIdAfterLabel = SimpleBooleanProperty(true) + fun bind(sessionManager: PropertiesManager) { sessionManager.bind("anti-aliasing", antialiasing) { @@ -204,6 +207,7 @@ object Properties { sessionManager.bindPath("osrs-cache-path", osrsCachePath) sessionManager.bindPath("qodat-cache-path", qodatCachePath) sessionManager.bindPath("legacy-cache-path", legacyCachePath) + sessionManager.bindPath("label-mapping-path", labelMappingPath) sessionManager.bindPath("main-data-path", projectFilesPath) sessionManager.bindPath("exports-path", defaultExportsPath) @@ -211,5 +215,7 @@ object Properties { sessionManager.bindPath("last-wavefront-sequence-export-path", lastWaveFrontSequenceExportPath) sessionManager.bindPath("last-gif-export-path", lastGIFExportPath) sessionManager.bindPath("last-sprite-export-path", lastSpriteExportPath) + + sessionManager.bindBoolean("show-id-after-label", showIdAfterLabel) } } diff --git a/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt b/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt index 0cce404..c70316c 100644 --- a/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt +++ b/src/main/kotlin/stan/qodat/scene/control/LabeledHBox.kt @@ -4,6 +4,13 @@ import javafx.beans.property.SimpleStringProperty import javafx.geometry.Pos import javafx.scene.control.Label import javafx.scene.layout.HBox +import javafx.scene.paint.Color +import javafx.scene.text.Font +import javafx.scene.text.FontWeight +import stan.qodat.Properties +import stan.qodat.util.LabelMapping +import tornadofx.* +import kotlin.error /** * Represents a [HBox] containing a [Label] bound to the text property. @@ -12,15 +19,72 @@ import javafx.scene.layout.HBox * @since 31/01/2021 */ class LabeledHBox( - textProperty: SimpleStringProperty, + private val originalValueProperty: SimpleStringProperty, spacing: Double = 10.0, - alignment: Pos = Pos.CENTER_LEFT + alignment: Pos = Pos.CENTER_LEFT, + private val labelPrefix: String? = null ) : HBox(spacing) { - val label = Label() + private val actualTextProperty = stringProperty(mappingOrOriginal()) + + val editableValueProperty = stringProperty(mappingOrNull()?:"").apply { + onChange { + if (it != null) { + LabelMapping[key()] = it + actualTextProperty.value = it.ifBlank { originalValueProperty.value } + } + } + } + + private fun mappingOrOriginal() = + mappingOrNull()?: originalValueProperty.value + private fun mappingOrNull() = + labelPrefix?.let { LabelMapping[key()] } + private fun key() = + "${labelPrefix ?: error("Label prefix is required for editable labeled hbox!")}.${originalValueProperty.value}" + + val editableProperty = booleanProperty(false) + init { this.alignment = alignment - label.textProperty().bind(textProperty) - children.add(label) + editableProperty.onChange { updateChildren() } + focusedProperty().onChange { if (!it) editableProperty.set(false) } + updateChildren() + } + + private fun LabeledHBox.updateChildren() { + children.clear() + if (editableProperty.get()) { + textfield(editableValueProperty) { + focusedProperty().onChange { + if (!it) + editableProperty.set(false) + } + setOnKeyPressed { + if (it.code.name == "ENTER") + editableProperty.set(false) + } + } + } else { + if (actualTextProperty.value.isNullOrBlank()) { + actualTextProperty.value = originalValueProperty.value + } + text(actualTextProperty) { + font = Font.font("Menlo", FontWeight.BOLD, 13.0) + fill = Color(214 / 255.0, 214 / 255.0, 214 / 255.0, 1.0) + if (labelPrefix != null) { + onDoubleClick { + editableProperty.set(true) + } + } + } + if (actualTextProperty.value != originalValueProperty.value && Properties.showIdAfterLabel.value) { + text(originalValueProperty) { + font = Font.font("Menlo", FontWeight.EXTRA_LIGHT, 13.0) + fill = Color.web("#A4B8C8") + } + } + } } -} \ No newline at end of file +} + diff --git a/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt b/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt index 8538831..574e213 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/animation/Animation.kt @@ -27,6 +27,9 @@ import stan.qodat.scene.transform.Transformable import stan.qodat.scene.transform.Transformer import stan.qodat.task.BackgroundTasks import stan.qodat.util.Searchable +import tornadofx.contextmenu +import tornadofx.item +import tornadofx.stringBinding /** * Represents a [Transformer] for [Model] objects. @@ -35,7 +38,7 @@ import stan.qodat.util.Searchable * @since 28/01/2021 */ class Animation( - label: String, + private val label: String, private val definition: AnimationDefinition? = null, private val cache: Cache? = null ) : Transformer, Searchable, ViewNodeProvider, Encoder { @@ -49,6 +52,7 @@ class Animation( val treeItemProperty = SimpleObjectProperty() val exportFrameArchiveId = SimpleIntegerProperty() val labelProperty = SimpleStringProperty(label) + val nameProperty = SimpleStringProperty(label) val idProperty = SimpleIntegerProperty() val loopOffsetProperty = SimpleIntegerProperty(definition?.loopOffset ?: -1) val leftHandItemProperty = SimpleIntegerProperty(definition?.leftHandItem ?: -1) @@ -114,12 +118,20 @@ class Animation( } } - override fun getName() = labelProperty.get() + override fun getName() = nameProperty.get() override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { - viewBox = LabeledHBox(labelProperty).apply { - label.contextMenu = ContextMenu().apply { + viewBox = LabeledHBox(labelProperty, labelPrefix = "animation").apply { + nameProperty.bind(editableValueProperty.stringBinding(labelProperty) { + "${editableValueProperty.get()} (${labelProperty.get()})" + }) + contextmenu { + item("Rename") { + setOnAction { + editableProperty.set(true) + } + } menu("export") { menuItem("GIF") { BackgroundTasks.submit( @@ -163,4 +175,4 @@ class Animation( } } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt index 57cdf65..2400412 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/AnimatedEntity.kt @@ -17,8 +17,9 @@ import stan.qodat.scene.transform.Transformable abstract class AnimatedEntity( cache: Cache, definition: D, - private val animationProvider: D.() -> Array -) : Entity(cache, definition), Transformable, GroupableTransformable { + private val animationProvider: D.() -> Array, + labelPrefix: String? = null, +) : Entity(cache, definition, labelPrefix), Transformable, GroupableTransformable { private lateinit var animations: Array // private lateinit var skeletons: Map @@ -58,4 +59,4 @@ abstract class AnimatedEntity( for (model in getModels()) model.animate(frame) } -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt index 123141a..8b87ba2 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/Entity.kt @@ -37,7 +37,8 @@ import stan.qodat.util.getMaterial */ abstract class Entity( protected val cache: Cache, - val definition: D + val definition: D, + val labelPrefix: String? = null ) : Exportable, SceneNodeProvider, ViewNodeProvider, TreeItemProvider { private var modelGroup: Group? = null @@ -142,7 +143,7 @@ abstract class Entity( override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { val optionalInt = definition.getOptionalId() - val box = LabeledHBox(labelProperty) + val box = LabeledHBox(labelProperty, labelPrefix = labelPrefix) viewBox = if (optionalInt.isPresent) HBox().apply { val id = optionalInt.asInt.toString() diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt index 3d693b0..dbfd7de 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/Item.kt @@ -1,9 +1,9 @@ package stan.qodat.scene.runescape.entity -import stan.qodat.Properties import qodat.cache.Cache import qodat.cache.Encoder import qodat.cache.definition.ItemDefinition +import stan.qodat.Properties /** * TODO: add documentation @@ -12,7 +12,7 @@ import qodat.cache.definition.ItemDefinition * @since 28/01/2021 */ class Item(cache: Cache, definition: ItemDefinition) - : Entity(cache, definition), Encoder { + : Entity(cache, definition, "item"), Encoder { override fun property() = Properties.selectedItemName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt index 72117b7..e95138f 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/NPC.kt @@ -1,10 +1,10 @@ package stan.qodat.scene.runescape.entity -import stan.qodat.Properties import qodat.cache.Cache import qodat.cache.EncodeResult import qodat.cache.Encoder import qodat.cache.definition.NPCDefinition +import stan.qodat.Properties import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite import stan.qodat.scene.runescape.animation.Animation import java.io.UnsupportedEncodingException @@ -19,7 +19,7 @@ class NPC( cache: Cache = OldschoolCacheRuneLite, definition: NPCDefinition, animationProvider: NPCDefinition.() -> Array -) : AnimatedEntity(cache, definition, animationProvider), Encoder { +) : AnimatedEntity(cache, definition, animationProvider, "npc"), Encoder { override fun encode(format: Cache) : EncodeResult { throw UnsupportedEncodingException() @@ -28,4 +28,4 @@ class NPC( override fun toString(): String = getName() override fun property() = Properties.selectedNpcName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt index 5c07061..477ca0b 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/Object.kt @@ -1,10 +1,10 @@ package stan.qodat.scene.runescape.entity -import stan.qodat.Properties import qodat.cache.Cache import qodat.cache.EncodeResult import qodat.cache.Encoder import qodat.cache.definition.ObjectDefinition +import stan.qodat.Properties import stan.qodat.cache.impl.oldschool.OldschoolCacheRuneLite import stan.qodat.scene.runescape.animation.Animation import java.io.UnsupportedEncodingException @@ -19,11 +19,11 @@ class Object( cache: Cache = OldschoolCacheRuneLite, definition: ObjectDefinition, animationProvider: ObjectDefinition.() -> Array -) : AnimatedEntity(cache, definition, animationProvider), Encoder { +) : AnimatedEntity(cache, definition, animationProvider, "loc"), Encoder { override fun encode(format: Cache) : EncodeResult { throw UnsupportedEncodingException() } override fun property() = Properties.selectedObjectName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt b/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt index 15f7d24..dc790c1 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/entity/SpotAnimation.kt @@ -11,9 +11,9 @@ class SpotAnimation( cache: Cache = OldschoolCacheRuneLite, definition: SpotAnimationDefinition, animationProvider: SpotAnimationDefinition.() -> Array -) : AnimatedEntity(cache, definition, animationProvider), Encoder { +) : AnimatedEntity(cache, definition, animationProvider, labelPrefix = "spot_anim"), Encoder { override fun toString(): String = getName() override fun property() = Properties.selectedSpotAnimName -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt b/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt index b2bb3d5..9ddc2bc 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/model/Model.kt @@ -186,7 +186,7 @@ class Model(label: String, override fun getViewNode(): Node { if (!this::viewBox.isInitialized) { - viewBox = LabeledHBox(labelProperty) + viewBox = LabeledHBox(labelProperty, labelPrefix = "model") } return viewBox } diff --git a/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt b/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt index a263dcf..2589ec4 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/ui/InterfaceGroup.kt @@ -24,7 +24,7 @@ class InterfaceGroup(val cache: Cache, private val groupId: Int, val definitions val nameProperty = SimpleStringProperty(idProperty.get().toString()) private val viewBox: HBox by lazy { - LabeledHBox(nameProperty) + LabeledHBox(nameProperty, labelPrefix = "widget") } private val sceneGroup: Group by lazy { @@ -48,4 +48,4 @@ class InterfaceGroup(val cache: Cache, private val groupId: Int, val definitions override fun treeItemExpandedProperty(): BooleanProperty = Properties.treeItemInterfaceExpanded -} \ No newline at end of file +} diff --git a/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt b/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt index 8c195a5..8f92928 100644 --- a/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt +++ b/src/main/kotlin/stan/qodat/scene/runescape/ui/Sprite.kt @@ -38,7 +38,7 @@ class Sprite(val definition: SpriteDefinition) : SceneNodeProvider, ViewNodeProv } val viewNode: HBox by lazy { - LabeledHBox(nameProperty) .apply { + LabeledHBox(nameProperty, labelPrefix = "sprite").apply { contextmenu { styleClass += "wave-front-format-export-menu" for (exportFormat in SpriteExportFormat.all) diff --git a/src/main/kotlin/stan/qodat/util/Labels.kt b/src/main/kotlin/stan/qodat/util/Labels.kt index 282e692..b0e8c8a 100644 --- a/src/main/kotlin/stan/qodat/util/Labels.kt +++ b/src/main/kotlin/stan/qodat/util/Labels.kt @@ -1,6 +1,41 @@ package stan.qodat.util -object Labels { +import kotlinx.serialization.decodeFromString +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import stan.qodat.Properties +import kotlin.io.path.createFile +import kotlin.io.path.exists +import kotlin.io.path.readText +import kotlin.io.path.writeText +object LabelMapping { -} \ No newline at end of file + private val labelData by lazy { load() } + + private fun load(): MutableMap { + val labelData = mutableMapOf() + Properties.labelMappingPath.value?.let { path -> + if (path.exists()) + labelData.putAll(Json.decodeFromString(path.readText())) + } + return labelData + } + + private fun save() { + Properties.labelMappingPath.value?.let { path -> + if (!path.exists()) + path.createFile() + path.writeText(Json.encodeToString(labelData)) + } + } + + operator fun get(key: String): String? { + return labelData[key] + } + + operator fun set(key: String, value: String) { + labelData[key] = value + save() + } +}