[Editor] Add support for importing SoundAssets

This commit is contained in:
2021-03-23 18:20:14 +01:00
parent cf4bcc8cbd
commit e3d7ce2b73
13 changed files with 182 additions and 21 deletions

View File

@@ -3,6 +3,7 @@ package com.bartlomiejpluta.base.editor.asset.component
import com.bartlomiejpluta.base.editor.animation.asset.AnimationAsset
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.asset.model.AssetCategory
import com.bartlomiejpluta.base.editor.audio.asset.SoundAsset
import com.bartlomiejpluta.base.editor.entityset.asset.EntitySet
import com.bartlomiejpluta.base.editor.gui.font.asset.FontAsset
import com.bartlomiejpluta.base.editor.gui.widget.asset.WidgetAsset
@@ -76,6 +77,7 @@ class AssetTreeCell(renameAsset: (asset: Asset, name: String) -> Asset, deleteAs
is AnimationAsset -> FontIcon("fa-film")
is FontAsset -> FontIcon("fa-font")
is WidgetAsset -> FontIcon("fa-tachometer")
is SoundAsset -> FontIcon("fa-music")
else -> null
}
}

View File

@@ -7,7 +7,7 @@ import com.bartlomiejpluta.base.editor.file.model.ScriptAssetFileNode
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 javafx.beans.binding.Bindings
import javafx.beans.binding.Bindings.bindContent
import javafx.scene.control.TreeItem
import javafx.scene.control.TreeView
import tornadofx.*
@@ -44,6 +44,10 @@ class AssetsListView : View() {
menuitem("New Widget...") { mainController.createEmptyWidget() }
}
private val audio = AssetCategory("Audio").apply {
menuitem("Import Sound...") { mainController.importSound() }
}
private val rootItem = AssetCategory(
name = "Project", items = observableListOf(
maps,
@@ -52,7 +56,8 @@ class AssetsListView : View() {
entitySet,
animations,
fonts,
widgets
widgets,
audio
)
)
@@ -60,13 +65,14 @@ class AssetsListView : View() {
projectContext.projectProperty.addListener { _, _, project ->
project?.let {
rootItem.nameProperty.bind(it.nameProperty)
Bindings.bindContent(maps.items, it.maps)
Bindings.bindContent(tileSets.items, it.tileSets)
Bindings.bindContent(images.items, it.images)
Bindings.bindContent(entitySet.items, it.entitySets)
Bindings.bindContent(animations.items, it.animations)
Bindings.bindContent(fonts.items, it.fonts)
Bindings.bindContent(widgets.items, it.widgets)
bindContent(maps.items, it.maps)
bindContent(tileSets.items, it.tileSets)
bindContent(images.items, it.images)
bindContent(entitySet.items, it.entitySets)
bindContent(animations.items, it.animations)
bindContent(fonts.items, it.fonts)
bindContent(widgets.items, it.widgets)
bindContent(audio.items, it.sounds)
root.root.expandAll()
}
}

View File

@@ -0,0 +1,7 @@
package com.bartlomiejpluta.base.editor.audio.asset
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.project.model.Project
class SoundAsset(project: Project, uid: String, source: String, name: String) :
Asset(project.audioDirectoryProperty, uid, source, name)

View File

@@ -0,0 +1,15 @@
package com.bartlomiejpluta.base.editor.audio.asset
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import tornadofx.getValue
import tornadofx.setValue
import java.io.File
class SoundAssetData {
val nameProperty = SimpleStringProperty()
var name by nameProperty
val fileProperty = SimpleObjectProperty<File>()
var file by fileProperty
}

View File

@@ -0,0 +1,55 @@
package com.bartlomiejpluta.base.editor.audio.view.importing
import com.bartlomiejpluta.base.editor.audio.asset.SoundAssetData
import com.bartlomiejpluta.base.editor.audio.viewmodel.SoundAssetDataVM
import com.bartlomiejpluta.base.editor.common.component.SingleFileChooserField
import javafx.stage.FileChooser
import tornadofx.*
class ImportSoundFragment : Fragment("Import Sound") {
private val dataVM = find<SoundAssetDataVM>()
private var onCompleteConsumer: ((SoundAssetData) -> Unit)? = null
fun onComplete(consumer: (SoundAssetData) -> Unit) {
this.onCompleteConsumer = consumer
}
override val root = form {
fieldset("Import Sound") {
field("Sound Name") {
textfield(dataVM.nameProperty) {
requestFocus()
required()
trimWhitespace()
}
}
field("Sound File") {
this += SingleFileChooserField(dataVM.fileProperty) {
type = SingleFileChooserField.Type.FILE
validationContext = dataVM.validationContext
dialogFormat {
chooseFile("Sound File Location", arrayOf(FileChooser.ExtensionFilter("OGG Vorbis File", "*.ogg")))
}
}
}
}
buttonbar {
button("Ok") {
action {
if (dataVM.commit()) {
onCompleteConsumer?.let { it(dataVM.item) }
close()
}
}
}
button("Cancel") {
action { close() }
}
}
}
}

View File

@@ -0,0 +1,14 @@
package com.bartlomiejpluta.base.editor.audio.viewmodel
import com.bartlomiejpluta.base.editor.audio.asset.SoundAssetData
import tornadofx.ItemViewModel
import tornadofx.getValue
import tornadofx.setValue
class SoundAssetDataVM : ItemViewModel<SoundAssetData>(SoundAssetData()) {
val nameProperty = bind(SoundAssetData::nameProperty)
var name by nameProperty
val fileProperty = bind(SoundAssetData::fileProperty)
var file by fileProperty
}

View File

@@ -30,6 +30,7 @@ class DefaultProjectAssembler : ProjectAssembler {
packager.pack(project.animationsDirectory, targetJar, "BOOT-INF/classes/project/animations")
packager.pack(project.fontsDirectory, targetJar, "BOOT-INF/classes/project/fonts")
packager.pack(project.widgetsDirectory, targetJar, "BOOT-INF/classes/project/widgets")
packager.pack(project.audioDirectory, targetJar, "BOOT-INF/classes/project/audio")
packager.copy(project.projectFile, targetJar, "BOOT-INF/classes/project")
}

View File

@@ -3,6 +3,8 @@ package com.bartlomiejpluta.base.editor.main.controller
import com.bartlomiejpluta.base.editor.animation.view.importing.ImportAnimationFragment
import com.bartlomiejpluta.base.editor.animation.viewmodel.AnimationAssetDataVM
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.audio.view.importing.ImportSoundFragment
import com.bartlomiejpluta.base.editor.audio.viewmodel.SoundAssetDataVM
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.code.model.CodeScope
import com.bartlomiejpluta.base.editor.code.viewmodel.CodeVM
@@ -214,6 +216,20 @@ class MainController : Controller() {
.ifPresent(this::openScript)
}
fun importSound() {
val vm = SoundAssetDataVM()
val scope = Scope()
setInScope(vm, scope)
find<ImportSoundFragment>(scope).apply {
onComplete {
projectContext.importSound(it)
}
openModal(block = true, resizable = true)
}
}
fun closeAsset(asset: Asset) {
when (asset) {
is GameMapAsset -> openItems.entries.firstOrNull { (_, item) -> item is GameMap && item.uid == asset.uid }?.key?.let {
@@ -236,6 +252,7 @@ class MainController : Controller() {
}
}
fun clearResources() {
openItems.clear()
}

View File

@@ -3,6 +3,8 @@ package com.bartlomiejpluta.base.editor.project.context
import com.bartlomiejpluta.base.editor.animation.asset.AnimationAsset
import com.bartlomiejpluta.base.editor.animation.asset.AnimationAssetData
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAsset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAssetData
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.code.model.CodeType
import com.bartlomiejpluta.base.editor.code.service.JavaClassService
@@ -222,6 +224,19 @@ class DefaultProjectContext : ProjectContext {
}
} ?: throw IllegalStateException("There is no open project in the context")
override fun importSound(data: SoundAssetData) {
project?.let {
UID.next(it.sounds.map(Asset::uid)).let { uid ->
val source = "$uid.${data.file.extension}"
val targetFile = File(it.audioDirectory, source)
data.file.copyTo(targetFile)
it.sounds += SoundAsset(it, uid, source, data.name)
save()
}
}
}
override fun deleteAsset(asset: Asset) {
project?.let {
it.assetLists.firstOrNull { assets -> assets.remove(asset) }

View File

@@ -2,6 +2,7 @@ package com.bartlomiejpluta.base.editor.project.context
import com.bartlomiejpluta.base.editor.animation.asset.AnimationAssetData
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAssetData
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.entityset.asset.EntitySetAssetData
import com.bartlomiejpluta.base.editor.file.model.FileNode
@@ -45,6 +46,8 @@ interface ProjectContext {
fun createWidget(data: WidgetAssetData): WidgetAsset
fun importSound(data: SoundAssetData)
fun deleteAsset(asset: Asset)
fun loadScript(fileNode: FileNode): Code
fun saveScript(code: Code)

View File

@@ -1,6 +1,7 @@
package com.bartlomiejpluta.base.editor.project.model
import com.bartlomiejpluta.base.editor.animation.asset.AnimationAsset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAsset
import com.bartlomiejpluta.base.editor.entityset.asset.EntitySet
import com.bartlomiejpluta.base.editor.file.model.FileSystemNode
import com.bartlomiejpluta.base.editor.gui.font.asset.FontAsset
@@ -36,8 +37,9 @@ class Project {
val animations = observableListOf<AnimationAsset>()
val fonts = observableListOf<FontAsset>()
val widgets = observableListOf<WidgetAsset>()
val sounds = observableListOf<SoundAsset>()
val assetLists = listOf(maps, tileSets, images, entitySets, animations, fonts, widgets)
val assetLists = listOf(maps, tileSets, images, entitySets, animations, fonts, widgets, sounds)
val mapsDirectoryProperty = SimpleObjectProperty<File>()
var mapsDirectory by mapsDirectoryProperty
@@ -67,6 +69,10 @@ class Project {
var widgetsDirectory by widgetsDirectoryProperty
private set
val audioDirectoryProperty = SimpleObjectProperty<File>()
var audioDirectory by audioDirectoryProperty
private set
val codeDirectoryProperty = SimpleObjectProperty<File>()
var codeDirectory by codeDirectoryProperty
private set
@@ -104,6 +110,7 @@ class Project {
animationsDirectory = File(it, ANIMATIONS_DIR)
fontsDirectory = File(it, FONTS_DIR)
widgetsDirectory = File(it, WIDGETS_DIR)
audioDirectory = File(it, AUDIO_DIR)
codeDirectory = File(it, CODE_DIR)
buildDirectory = File(it, BUILD_DIR)
buildClassesDirectory = File(it, BUILD_CLASSES_DIR)
@@ -122,6 +129,7 @@ class Project {
animationsDirectory?.mkdirs()
fontsDirectory?.mkdirs()
widgetsDirectory?.mkdirs()
audioDirectory?.mkdirs()
codeDirectory?.mkdirs()
}
@@ -136,6 +144,7 @@ class Project {
const val ANIMATIONS_DIR = "animations"
const val FONTS_DIR = "fonts"
const val WIDGETS_DIR = "widgets"
const val AUDIO_DIR = "audio"
const val CODE_DIR = "code"
const val BUILD_DIR = "build"
const val BUILD_CLASSES_DIR = "$BUILD_DIR/classes"

View File

@@ -1,6 +1,7 @@
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.animation.asset.AnimationAsset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAsset
import com.bartlomiejpluta.base.editor.entityset.asset.EntitySet
import com.bartlomiejpluta.base.editor.gui.font.asset.FontAsset
import com.bartlomiejpluta.base.editor.gui.widget.asset.WidgetAsset
@@ -17,18 +18,19 @@ class ProtobufProjectDeserializer : ProjectDeserializer {
override fun deserialize(input: InputStream): Project {
val proto = ProjectProto.Project.parseFrom(input)
val project = Project()
project.name = proto.name
project.runner = proto.runner
project.maps.addAll(proto.mapsList.map { deserializeMap(project, it) })
project.tileSets.addAll(proto.tileSetsList.map { deserializeTileSet(project, it) })
project.images.addAll(proto.imagesList.map { deserializeImage(project, it) })
project.entitySets.addAll(proto.entitySetsList.map { deserializeEntitySet(project, it) })
project.animations.addAll(proto.animationsList.map { deserializeAnimation(project, it) })
project.fonts.addAll(proto.fontsList.map { deserializeFont(project, it) })
project.widgets.addAll(proto.widgetsList.map { deserializeWidget(project, it) })
return project
return Project().apply {
name = proto.name
runner = proto.runner
maps.addAll(proto.mapsList.map { deserializeMap(this, it) })
tileSets.addAll(proto.tileSetsList.map { deserializeTileSet(this, it) })
images.addAll(proto.imagesList.map { deserializeImage(this, it) })
entitySets.addAll(proto.entitySetsList.map { deserializeEntitySet(this, it) })
animations.addAll(proto.animationsList.map { deserializeAnimation(this, it) })
fonts.addAll(proto.fontsList.map { deserializeFont(this, it) })
widgets.addAll(proto.widgetsList.map { deserializeWidget(this, it) })
sounds.addAll(proto.soundsList.map { deserializeSound(this, it) })
}
}
private fun deserializeMap(project: Project, map: ProjectProto.GameMapAsset) = GameMapAsset(
@@ -83,4 +85,11 @@ class ProtobufProjectDeserializer : ProjectDeserializer {
uid = widget.uid,
name = widget.name
)
private fun deserializeSound(project: Project, sound: ProjectProto.SoundAsset) = SoundAsset(
project = project,
uid = sound.uid,
source = sound.source,
name = sound.name
)
}

View File

@@ -1,6 +1,7 @@
package com.bartlomiejpluta.base.editor.project.serial
import com.bartlomiejpluta.base.editor.animation.asset.AnimationAsset
import com.bartlomiejpluta.base.editor.audio.asset.SoundAsset
import com.bartlomiejpluta.base.editor.entityset.asset.EntitySet
import com.bartlomiejpluta.base.editor.gui.font.asset.FontAsset
import com.bartlomiejpluta.base.editor.gui.widget.asset.WidgetAsset
@@ -26,6 +27,7 @@ class ProtobufProjectSerializer : ProjectSerializer {
proto.addAllAnimations(item.animations.map(this::serializeAnimation))
proto.addAllFonts(item.fonts.map(this::serializeFont))
proto.addAllWidgets(item.widgets.map(this::serializeWidget))
proto.addAllSounds(item.sounds.map(this::serializeSound))
proto.build().writeTo(output)
}
@@ -76,4 +78,10 @@ class ProtobufProjectSerializer : ProjectSerializer {
.setSource(widget.source)
.setName(widget.name)
.build()
private fun serializeSound(sound: SoundAsset) = ProjectProto.SoundAsset.newBuilder()
.setUid(sound.uid)
.setSource(sound.source)
.setName(sound.name)
.build()
}