[Editor] Implement possibility to preview graphic assets
This commit is contained in:
@@ -0,0 +1,14 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.asset.component
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.main.component.EditorFragment
|
||||||
|
import javafx.scene.input.KeyEvent
|
||||||
|
|
||||||
|
class GraphicAssetPreviewFragment : EditorFragment() {
|
||||||
|
private val preview = find<GraphicAssetPreviewView>()
|
||||||
|
|
||||||
|
override val root = preview.root
|
||||||
|
|
||||||
|
override fun handleShortcut(event: KeyEvent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.asset.component
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.asset.viewmodel.GraphicAssetVM
|
||||||
|
import javafx.beans.binding.Bindings
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
|
import javafx.scene.image.Image
|
||||||
|
import javafx.scene.image.WritableImage
|
||||||
|
import javafx.scene.input.MouseEvent
|
||||||
|
import javafx.scene.paint.Color
|
||||||
|
import javafx.scene.text.TextAlignment
|
||||||
|
import tornadofx.*
|
||||||
|
import kotlin.math.max
|
||||||
|
|
||||||
|
class GraphicAssetPreviewView : View() {
|
||||||
|
private val vm = find<GraphicAssetVM>()
|
||||||
|
private val image = Image(vm.fileProperty.value.inputStream())
|
||||||
|
private val rows = vm.rowsProperty.value
|
||||||
|
private val columns = vm.columnsProperty.value
|
||||||
|
private val chunkWidth = (image.width / columns).toInt()
|
||||||
|
private val chunkHeight = (image.height / rows).toInt()
|
||||||
|
private val mouseColumn = SimpleIntegerProperty(0)
|
||||||
|
private val mouseRow = SimpleIntegerProperty(0)
|
||||||
|
private val singleChunk = rows == 1 && columns == 1
|
||||||
|
|
||||||
|
override val root = borderpane {
|
||||||
|
center = scrollpane {
|
||||||
|
canvas(image.width + OFFSET_X, image.height + OFFSET_Y) {
|
||||||
|
addEventHandler(MouseEvent.ANY) {
|
||||||
|
mouseColumn.value = max(0, (it.x / chunkWidth).toInt() - 1)
|
||||||
|
mouseRow.value = max(0, (it.y / chunkHeight).toInt() - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
graphicsContext2D.drawImage(createBackgroundImage(), OFFSET_X, OFFSET_Y)
|
||||||
|
graphicsContext2D.drawImage(image, OFFSET_X, OFFSET_Y)
|
||||||
|
|
||||||
|
if(!singleChunk) {
|
||||||
|
graphicsContext2D.textAlign = TextAlignment.LEFT
|
||||||
|
for (i in 0 until columns) {
|
||||||
|
graphicsContext2D.fillText(i.toString(), OFFSET_X + chunkWidth * i + INDEX_OFFSET_X, OFFSET_Y - 5.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
graphicsContext2D.textAlign = TextAlignment.RIGHT
|
||||||
|
for (i in 0 until rows) {
|
||||||
|
graphicsContext2D.fillText(i.toString(), OFFSET_X - 5.0, OFFSET_Y + chunkHeight * i + INDEX_OFFSET_Y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!singleChunk) graphicsContext2D.drawImage(createGrid(), OFFSET_X, OFFSET_Y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bottom = label(
|
||||||
|
if (singleChunk) Bindings.format(
|
||||||
|
"Width: %d, Height: %d",
|
||||||
|
image.width.toInt(),
|
||||||
|
image.height.toInt()
|
||||||
|
)
|
||||||
|
else Bindings.format(
|
||||||
|
"Rows: %d, Columns: %d, Chunk width: %d, Chunk height: %d, Cursor: %d, %d",
|
||||||
|
rows,
|
||||||
|
columns,
|
||||||
|
chunkWidth,
|
||||||
|
chunkHeight,
|
||||||
|
mouseRow,
|
||||||
|
mouseColumn
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createBackgroundImage(): WritableImage {
|
||||||
|
val background = WritableImage(image.width.toInt(), image.height.toInt())
|
||||||
|
|
||||||
|
val writer = background.pixelWriter
|
||||||
|
for (x in 0 until background.width.toInt()) {
|
||||||
|
for (y in 0 until background.height.toInt()) {
|
||||||
|
val color = when (((x / BACKGROUND_TILE_WIDTH) + (y / BACKGROUND_TILE_HEIGHT)) % 2) {
|
||||||
|
0 -> BACKGROUND_COLOR1
|
||||||
|
else -> BACKGROUND_COLOR2
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.setColor(x, y, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return background
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createGrid(): WritableImage {
|
||||||
|
val grid = WritableImage(image.width.toInt(), image.height.toInt())
|
||||||
|
|
||||||
|
val writer = grid.pixelWriter
|
||||||
|
val color = Color.BLACK
|
||||||
|
for (x in 0 until grid.width.toInt()) {
|
||||||
|
for (y in 0 until grid.height.toInt()) {
|
||||||
|
if (x % chunkWidth == 0) {
|
||||||
|
writer.setColor(x, y, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y % chunkHeight == 0) {
|
||||||
|
writer.setColor(x, y, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastX = grid.width.toInt() - 1
|
||||||
|
val lastY = grid.height.toInt() - 1
|
||||||
|
|
||||||
|
for (x in 0 until grid.width.toInt()) {
|
||||||
|
writer.setColor(x, 0, color)
|
||||||
|
writer.setColor(x, lastY, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (y in 0 until grid.height.toInt()) {
|
||||||
|
writer.setColor(0, y, color)
|
||||||
|
writer.setColor(lastX, y, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val BACKGROUND_TILE_WIDTH = 5
|
||||||
|
private const val BACKGROUND_TILE_HEIGHT = 5
|
||||||
|
private val BACKGROUND_COLOR1 = Color.color(1.0, 1.0, 1.0, 1.0)
|
||||||
|
private val BACKGROUND_COLOR2 = Color.color(0.95, 0.95, 0.95, 1.0)
|
||||||
|
private const val OFFSET_X = 30.0
|
||||||
|
private const val OFFSET_Y = 30.0
|
||||||
|
private const val INDEX_OFFSET_X = 0.0
|
||||||
|
private const val INDEX_OFFSET_Y = 10.0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.asset.model
|
||||||
|
|
||||||
|
import javafx.beans.property.ObjectProperty
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class GraphicAsset(
|
||||||
|
directory: ObjectProperty<File>,
|
||||||
|
uid: String,
|
||||||
|
source: String,
|
||||||
|
name: String,
|
||||||
|
val rows: Int,
|
||||||
|
val columns: Int
|
||||||
|
) : Asset(directory, uid, source, name) {
|
||||||
|
val rowsProperty = SimpleIntegerProperty(rows)
|
||||||
|
val columnsProperty = SimpleIntegerProperty(columns)
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package com.bartlomiejpluta.base.editor.asset.view.list
|
|||||||
import com.bartlomiejpluta.base.editor.asset.component.AssetTreeCell
|
import com.bartlomiejpluta.base.editor.asset.component.AssetTreeCell
|
||||||
import com.bartlomiejpluta.base.editor.asset.model.Asset
|
import com.bartlomiejpluta.base.editor.asset.model.Asset
|
||||||
import com.bartlomiejpluta.base.editor.asset.model.AssetCategory
|
import com.bartlomiejpluta.base.editor.asset.model.AssetCategory
|
||||||
|
import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset
|
||||||
import com.bartlomiejpluta.base.editor.file.model.ScriptAssetFileNode
|
import com.bartlomiejpluta.base.editor.file.model.ScriptAssetFileNode
|
||||||
import com.bartlomiejpluta.base.editor.main.controller.MainController
|
import com.bartlomiejpluta.base.editor.main.controller.MainController
|
||||||
import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset
|
import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset
|
||||||
@@ -106,6 +107,7 @@ class AssetsListView : View() {
|
|||||||
when (val item = selectionModel?.selectedItem?.value) {
|
when (val item = selectionModel?.selectedItem?.value) {
|
||||||
is GameMapAsset -> mainController.openMap(item.uid)
|
is GameMapAsset -> mainController.openMap(item.uid)
|
||||||
is ScriptAssetFileNode -> mainController.openScript(item)
|
is ScriptAssetFileNode -> mainController.openScript(item)
|
||||||
|
is GraphicAsset -> mainController.openGraphicAsset(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.bartlomiejpluta.base.editor.asset.viewmodel
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset
|
||||||
|
import javafx.beans.property.SimpleIntegerProperty
|
||||||
|
import tornadofx.ItemViewModel
|
||||||
|
|
||||||
|
class GraphicAssetVM(asset: GraphicAsset) : ItemViewModel<GraphicAsset>(asset) {
|
||||||
|
val nameProperty = bind(GraphicAsset::nameProperty)
|
||||||
|
val fileProperty = bind(GraphicAsset::file)
|
||||||
|
val rowsProperty = bind(GraphicAsset::rowsProperty)
|
||||||
|
val columnsProperty = bind(GraphicAsset::columnsProperty)
|
||||||
|
}
|
||||||
@@ -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.view.importing.ImportAnimationFragment
|
||||||
import com.bartlomiejpluta.base.editor.animation.viewmodel.AnimationAssetDataVM
|
import com.bartlomiejpluta.base.editor.animation.viewmodel.AnimationAssetDataVM
|
||||||
import com.bartlomiejpluta.base.editor.asset.model.Asset
|
import com.bartlomiejpluta.base.editor.asset.model.Asset
|
||||||
|
import com.bartlomiejpluta.base.editor.asset.model.GraphicAsset
|
||||||
|
import com.bartlomiejpluta.base.editor.asset.viewmodel.GraphicAssetVM
|
||||||
import com.bartlomiejpluta.base.editor.audio.view.importing.ImportSoundFragment
|
import com.bartlomiejpluta.base.editor.audio.view.importing.ImportSoundFragment
|
||||||
import com.bartlomiejpluta.base.editor.audio.viewmodel.SoundAssetDataVM
|
import com.bartlomiejpluta.base.editor.audio.viewmodel.SoundAssetDataVM
|
||||||
import com.bartlomiejpluta.base.editor.code.model.CodeScope
|
import com.bartlomiejpluta.base.editor.code.model.CodeScope
|
||||||
@@ -151,6 +153,17 @@ class MainController : Controller() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun openGraphicAsset(asset: GraphicAsset) {
|
||||||
|
val findItem = { a: GraphicAssetVM -> a.fileProperty.value.absolutePath == asset.file.absolutePath }
|
||||||
|
openItem<GraphicAsset, GraphicAssetVM, Scope>(findItem) {
|
||||||
|
val vm = GraphicAssetVM(asset)
|
||||||
|
val scope = Scope()
|
||||||
|
setInScope(vm, scope)
|
||||||
|
|
||||||
|
scope to vm
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inline fun <reified T, reified VM : ItemViewModel<T>, S : Scope> openItem(
|
private inline fun <reified T, reified VM : ItemViewModel<T>, S : Scope> openItem(
|
||||||
findItem: (item: VM) -> Boolean,
|
findItem: (item: VM) -> Boolean,
|
||||||
updateExistingScope: (S) -> Unit = {},
|
updateExistingScope: (S) -> Unit = {},
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.bartlomiejpluta.base.editor.main.view
|
package com.bartlomiejpluta.base.editor.main.view
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.asset.component.GraphicAssetPreviewFragment
|
||||||
import com.bartlomiejpluta.base.editor.asset.view.list.AssetsListView
|
import com.bartlomiejpluta.base.editor.asset.view.list.AssetsListView
|
||||||
|
import com.bartlomiejpluta.base.editor.asset.viewmodel.GraphicAssetVM
|
||||||
import com.bartlomiejpluta.base.editor.code.view.build.BuildLogsView
|
import com.bartlomiejpluta.base.editor.code.view.build.BuildLogsView
|
||||||
import com.bartlomiejpluta.base.editor.code.view.editor.CodeEditorFragment
|
import com.bartlomiejpluta.base.editor.code.view.editor.CodeEditorFragment
|
||||||
import com.bartlomiejpluta.base.editor.code.view.list.ScriptFilesView
|
import com.bartlomiejpluta.base.editor.code.view.list.ScriptFilesView
|
||||||
@@ -173,6 +175,18 @@ class MainView : View("BASE Game Editor") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is GraphicAssetVM -> {
|
||||||
|
setInScope(vm, scope)
|
||||||
|
|
||||||
|
EditorTab(find<GraphicAssetPreviewFragment>(scope), FontIcon("fa-file-image-o")).apply {
|
||||||
|
textProperty().bind(vm.nameProperty)
|
||||||
|
|
||||||
|
setOnClosed {
|
||||||
|
mainController.openItems.remove(scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> throw IllegalStateException("Unsupported tab item")
|
else -> throw IllegalStateException("Unsupported tab item")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user