From 989d7e94ed8a77909e535b562dc7de9df713d3b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Przemys=C5=82aw=20Pluta?= Date: Thu, 11 Feb 2021 21:00:59 +0100 Subject: [PATCH] [Editor] Enable opening map on double-click on project structure panel --- .../editor/main/controller/MainController.kt | 20 +++++++++++-- .../base/editor/main/view/MainMenuView.kt | 2 +- .../base/editor/main/view/MainView.kt | 12 ++++++++ .../editor/main/view/ProjectStructureView.kt | 13 +++++++- .../project/context/DefaultProjectContext.kt | 30 ++++++++----------- .../editor/project/context/ProjectContext.kt | 1 + .../base/editor/project/model/Project.kt | 20 +++++++++++++ 7 files changed, 76 insertions(+), 22 deletions(-) diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt index 6c3846c8..0e3e4abc 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt @@ -4,6 +4,7 @@ import com.bartlomiejpluta.base.editor.command.context.UndoableScope import com.bartlomiejpluta.base.editor.map.model.map.GameMap import com.bartlomiejpluta.base.editor.map.view.wizard.MapCreationWizard import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapBuilderVM +import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM import com.bartlomiejpluta.base.editor.project.context.ProjectContext import com.bartlomiejpluta.base.editor.project.model.Project import com.bartlomiejpluta.base.editor.project.view.ProjectSettingsFragment @@ -27,6 +28,7 @@ class MainController : Controller() { val modal = find().apply { openModal(block = true, resizable = false) } if (modal.result) { + openMaps.clear() projectContext.project = project projectContext.save() } @@ -49,10 +51,24 @@ class MainController : Controller() { } } - fun loadProject() { + fun openProject() { chooseFile( title = "Load Project", filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")), - ).getOrNull(0)?.let { projectContext.open(it) } + ).getOrNull(0)?.let { + openMaps.clear() + projectContext.open(it) + } + } + + fun openMap(uid: String) { + if (openMaps.count { (_, map) -> map.uid == uid } == 0) { + val map = projectContext.loadMap(uid) + val vm = GameMapVM(map) + val scope = UndoableScope() + setInScope(vm, scope) + + openMaps[scope] = map + } } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainMenuView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainMenuView.kt index 38281b42..ee87d029 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainMenuView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainMenuView.kt @@ -28,7 +28,7 @@ class MainMenuView : View() { menu("Open") { item("Project...") { action { - mainController.loadProject() + mainController.openProject() } } } diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt index e202a130..b4b3637f 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt @@ -4,6 +4,9 @@ import com.bartlomiejpluta.base.editor.main.controller.MainController import com.bartlomiejpluta.base.editor.map.view.editor.MapFragment import com.bartlomiejpluta.base.editor.map.viewmodel.GameMapVM import com.bartlomiejpluta.base.editor.project.context.ProjectContext +import javafx.beans.InvalidationListener +import javafx.beans.Observable +import javafx.collections.MapChangeListener import javafx.scene.control.Tab import tornadofx.* @@ -35,6 +38,15 @@ class MainView : View("BASE Game Editor") { setOnClosed { mainController.openMaps.remove(scope) } } } + + // FIXME + // For some reason cleaning mainController.openMaps just takes off the tabs content keeping open themselves. + // The workaround is to detect if map is cleaned and the clean the tabs by hand. + mainController.openMaps.addListener(MapChangeListener { + if(it.map.isEmpty()) { + tabs.clear() + } + }) } left = projectStructureView.root diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/ProjectStructureView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/ProjectStructureView.kt index 4b716223..ad0081bf 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/ProjectStructureView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/ProjectStructureView.kt @@ -1,18 +1,20 @@ package com.bartlomiejpluta.base.editor.main.view +import com.bartlomiejpluta.base.editor.main.controller.MainController import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset import com.bartlomiejpluta.base.editor.project.context.ProjectContext -import com.bartlomiejpluta.base.editor.util.fx.BindingUtil import javafx.beans.binding.Bindings import javafx.beans.property.SimpleStringProperty import javafx.collections.ObservableList import javafx.scene.control.TreeItem +import javafx.scene.input.MouseEvent import org.kordamp.ikonli.javafx.FontIcon import tornadofx.* class ProjectStructureView : View() { private val projectContext: ProjectContext by di() + private val mainController: MainController by di() private val structureMaps = StructureCategory("Maps") @@ -23,6 +25,7 @@ class ProjectStructureView : View() { project?.let { structureRoot.nameProperty.bind(it.nameProperty) Bindings.bindContent(structureMaps.items, project.maps) + root.root.expandAll() root.refresh() } } @@ -52,6 +55,14 @@ class ProjectStructureView : View() { else -> null } } + + setOnMouseClicked { event -> + if(event.clickCount == 2) { + when(val item = selectionModel?.selectedItem?.value) { + is GameMapAsset -> mainController.openMap(item.uid) + } + } + } } private class StructureCategory(name: String = "", var items: ObservableList = observableListOf()) { diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt index 625b2af6..c5f41ccb 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/DefaultProjectContext.kt @@ -3,18 +3,17 @@ package com.bartlomiejpluta.base.editor.project.context import com.bartlomiejpluta.base.editor.asset.model.Asset import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset import com.bartlomiejpluta.base.editor.map.model.map.GameMap +import com.bartlomiejpluta.base.editor.map.serial.MapDeserializer import com.bartlomiejpluta.base.editor.map.serial.MapSerializer import com.bartlomiejpluta.base.editor.project.model.Project import com.bartlomiejpluta.base.editor.project.serial.ProjectDeserializer import com.bartlomiejpluta.base.editor.project.serial.ProjectSerializer import com.bartlomiejpluta.base.editor.util.uid.UID import javafx.beans.property.ObjectProperty -import javafx.beans.property.ReadOnlyObjectWrapper import javafx.beans.property.SimpleObjectProperty import org.springframework.beans.factory.annotation.Autowired import org.springframework.stereotype.Component import tornadofx.getValue -import tornadofx.select import tornadofx.setValue import java.io.File import java.io.FileInputStream @@ -32,23 +31,15 @@ class DefaultProjectContext : ProjectContext { @Autowired private lateinit var mapSerializer: MapSerializer + @Autowired + private lateinit var mapDeserializer: MapDeserializer + override val projectProperty = SimpleObjectProperty() as ObjectProperty override var project by projectProperty - private val mapsDirectoryProperty = SimpleObjectProperty() - private val mapsDirectory by mapsDirectoryProperty - init { projectProperty.addListener { _, _, newProject -> - when(newProject) { - null -> { - mapsDirectoryProperty.value = null - } - - else -> { - mapsDirectoryProperty.value = File(newProject.sourceDirectory, MAPS_DIR).apply(File::mkdirs) - } - } + newProject?.mkdirs() } } @@ -77,12 +68,15 @@ class DefaultProjectContext : ProjectContext { it.maps += asset save() - File(mapsDirectory, asset.source).outputStream().use { fos -> mapSerializer.serialize(map, fos) } + File(it.mapsDirectory, asset.source).outputStream().use { fos -> mapSerializer.serialize(map, fos) } } } } - companion object { - const val MAPS_DIR = "maps" - } + override fun loadMap(uid: String) = project?.let { + val asset = it.maps.first { map -> map.uid == uid } + ?: throw IllegalStateException("The map with uid [$uid] does not exist ") + + File(it.mapsDirectory, asset.source).inputStream().use { fis -> mapDeserializer.deserialize(fis) } + } ?: throw IllegalStateException("There is no open project in the context") } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt index 8a439b89..b54e879e 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/context/ProjectContext.kt @@ -13,4 +13,5 @@ interface ProjectContext { fun open(file: File) fun importMap(name: String, map: GameMap) + fun loadMap(uid: String): GameMap } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/model/Project.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/model/Project.kt index 4dc4d485..f2ab18b7 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/model/Project.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/project/model/Project.kt @@ -1,6 +1,7 @@ package com.bartlomiejpluta.base.editor.project.model import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset +import com.bartlomiejpluta.base.editor.project.context.DefaultProjectContext import javafx.beans.property.SimpleObjectProperty import javafx.beans.property.SimpleStringProperty import tornadofx.getValue @@ -16,4 +17,23 @@ class Project { val sourceDirectory by sourceDirectoryProperty val maps = observableListOf() + + val mapsDirectoryProperty = SimpleObjectProperty() + var mapsDirectory by mapsDirectoryProperty + private set + + init { + sourceDirectoryProperty.addListener { _, _, dir -> + dir?.let { mapsDirectory = File(it, MAPS_DIR) } + } + } + + fun mkdirs() { + sourceDirectory?.mkdirs() + mapsDirectory?.mkdirs() + } + + companion object { + const val MAPS_DIR = "maps" + } }