[Editor] Make FileSystemNode implements root FileNode interface

This commit is contained in:
2021-02-28 23:33:01 +01:00
parent 3b43b87b8e
commit 965775774c
18 changed files with 189 additions and 129 deletions

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.code.build.compiler
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileSystemNode
import java.io.File
interface Compiler {

View File

@@ -2,9 +2,9 @@ package com.bartlomiejpluta.base.editor.code.build.compiler
import com.bartlomiejpluta.base.editor.code.build.exception.BuildException
import com.bartlomiejpluta.base.editor.code.build.model.ClasspathResource
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.common.logs.enumeration.Severity
import com.bartlomiejpluta.base.editor.event.AppendBuildLogsEvent
import com.bartlomiejpluta.base.editor.file.model.FileSystemNode
import org.codehaus.commons.compiler.CompileException
import org.codehaus.commons.compiler.util.resource.FileResource
import org.codehaus.commons.compiler.util.resource.Resource

View File

@@ -1,15 +1,15 @@
package com.bartlomiejpluta.base.editor.code.component
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileNode
import javafx.scene.control.TreeCell
import javafx.util.StringConverter
class ScriptFileStringConverter(
private val cell: TreeCell<FileSystemNode>,
private val onUpdate: (item: FileSystemNode, name: String) -> FileSystemNode
) : StringConverter<FileSystemNode>() {
override fun toString(item: FileSystemNode?): String = when (item) {
is FileSystemNode -> item.file.name
private val cell: TreeCell<FileNode>,
private val onUpdate: (item: FileNode, name: String) -> FileNode
) : StringConverter<FileNode>() {
override fun toString(item: FileNode?): String = when (item) {
is FileNode -> item.name
else -> ""
}

View File

@@ -1,6 +1,7 @@
package com.bartlomiejpluta.base.editor.code.component
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileNode
import com.bartlomiejpluta.base.editor.file.model.FileType
import javafx.scene.control.ContextMenu
import javafx.scene.control.cell.TextFieldTreeCell
import org.kordamp.ikonli.javafx.FontIcon
@@ -9,8 +10,8 @@ import tornadofx.enableWhen
import tornadofx.item
import tornadofx.toProperty
class ScriptFileTreeCell(onCreate: (FileSystemNode) -> Unit, onDelete: (FileSystemNode) -> Unit) :
TextFieldTreeCell<FileSystemNode>() {
class ScriptFileTreeCell(onCreate: (FileNode) -> Unit, onDelete: (FileNode) -> Unit) :
TextFieldTreeCell<FileNode>() {
private val isRoot = true.toProperty()
private val isNotRoot = isRoot.not()
@@ -68,11 +69,11 @@ class ScriptFileTreeCell(onCreate: (FileSystemNode) -> Unit, onDelete: (FileSyst
converter = ScriptFileStringConverter(this, this::renameFile)
}
private fun renameFile(file: FileSystemNode, name: String) = file.apply {
private fun renameFile(file: FileNode, name: String) = file.apply {
file.rename(name)
}
override fun updateItem(item: FileSystemNode?, empty: Boolean) {
override fun updateItem(item: FileNode?, empty: Boolean) {
super.updateItem(item, empty)
if (empty || item == null) {
@@ -83,13 +84,13 @@ class ScriptFileTreeCell(onCreate: (FileSystemNode) -> Unit, onDelete: (FileSyst
return
}
text = item.file.name
graphic = FontIcon(getFileSystemNodeIcon(item))
text = item.name
graphic = FontIcon(getFileNodeIcon(item))
contextMenu = when {
isEditing -> null
item.isFile -> fileMenu
item.isDirectory -> directoryMenu
item.type == FileType.FILE -> fileMenu
item.type == FileType.DIRECTORY -> directoryMenu
else -> null
}
@@ -97,12 +98,12 @@ class ScriptFileTreeCell(onCreate: (FileSystemNode) -> Unit, onDelete: (FileSyst
}
companion object {
private fun getFileSystemNodeIcon(file: FileSystemNode): String {
if (file.isDirectory) {
private fun getFileNodeIcon(file: FileNode): String {
if (file.type == FileType.DIRECTORY) {
return "fa-folder"
}
return when (file.file.extension.toLowerCase()) {
return when (file.extension.toLowerCase()) {
"java" -> "fa-code"
else -> "fa-file"
}

View File

@@ -1,14 +1,12 @@
package com.bartlomiejpluta.base.editor.code.model
import com.bartlomiejpluta.base.editor.file.model.FileNode
import javafx.beans.property.Property
import tornadofx.getValue
import tornadofx.setValue
import tornadofx.toProperty
import java.io.File
class Code(val fileProperty: Property<File>, val typeProperty: Property<CodeType>, code: String) {
val file by fileProperty
class Code(val fileNode: FileNode, val typeProperty: Property<CodeType>, code: String) {
val type by typeProperty
val codeProperty = code.toProperty()

View File

@@ -1,78 +0,0 @@
package com.bartlomiejpluta.base.editor.code.model
import tornadofx.getValue
import tornadofx.observableListOf
import tornadofx.setValue
import tornadofx.toProperty
import java.io.File
import java.nio.file.Path
class FileSystemNode(file: File, val parent: FileSystemNode? = null) {
val fileProperty = file.toProperty()
var file by fileProperty
private set
val isFile = file.isFile
val isDirectory = file.isDirectory
val children = observableListOf<FileSystemNode>()
val allChildren: List<FileSystemNode>
get() = children + children.flatMap { it.allChildren }
init {
refreshChildren()
}
private fun refreshChildren() {
if (isDirectory) {
children.clear()
file.listFiles()?.map { FileSystemNode(it, this) }?.let { children.addAll(it) }
}
}
fun rename(name: String) {
val newFile = File(file.parent, name)
file.renameTo(newFile)
file = newFile
}
fun delete() {
val deleted = when {
isFile -> file.delete()
isDirectory -> file.deleteRecursively()
else -> false
}
if (deleted) {
parent?.children?.remove(this)
}
}
fun refresh() {
refreshChildren()
}
fun createNode(path: String): FileSystemNode {
val segments = Path.of(path.replace("..", "."))
return segments.foldIndexed(this) { index, parent, segment ->
val file = File(parent.file, segment.toString())
when {
index < segments.count() - 1 -> file.mkdirs()
else -> file.createNewFile()
}
when (val child = findChild(file)) {
null -> FileSystemNode(file, parent).also { parent.children += it }
else -> child
}
}
}
private fun findChild(file: File): FileSystemNode? {
return children.firstOrNull { it.file.name == file.name }
}
fun findByFile(file: File) = allChildren.firstOrNull { it.file.equals(file) }
}

View File

@@ -1,7 +1,8 @@
package com.bartlomiejpluta.base.editor.code.view.list
import com.bartlomiejpluta.base.editor.code.component.ScriptFileTreeCell
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileNode
import com.bartlomiejpluta.base.editor.file.model.FileType
import com.bartlomiejpluta.base.editor.main.controller.MainController
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
import javafx.scene.control.TextInputDialog
@@ -28,7 +29,7 @@ class ScriptFilesView : View() {
}
}
private val treeView: TreeView<FileSystemNode> = treeview {
private val treeView: TreeView<FileNode> = treeview {
setCellFactory {
ScriptFileTreeCell(this@ScriptFilesView::onCreate, mainController::closeScript)
}
@@ -36,7 +37,7 @@ class ScriptFilesView : View() {
setOnMouseClicked { event ->
if (event.button == MouseButton.PRIMARY && event.clickCount == 2) {
selectionModel?.selectedItem?.value
.takeIf { it?.isFile ?: false }
.takeIf { it?.type == FileType.FILE }
?.let { mainController.openScript(it) }
event.consume()
@@ -46,7 +47,7 @@ class ScriptFilesView : View() {
override val root = treeView
private fun onCreate(fsNode: FileSystemNode) {
private fun onCreate(fsNode: FileNode) {
TextInputDialog().apply {
width = 300.0
contentText = "Class name"

View File

@@ -1,6 +1,7 @@
package com.bartlomiejpluta.base.editor.code.view.select
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileType
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
import javafx.beans.binding.Bindings.createBooleanBinding
import javafx.beans.property.SimpleObjectProperty
@@ -17,7 +18,7 @@ class SelectJavaClassFragment : Fragment("Select Java Class") {
SelectJavaClassView::selection to selection
)
private val isFile = createBooleanBinding({ selection.value?.isFile ?: false }, selection)
private val isFile = createBooleanBinding({ selection.value?.type == FileType.FILE }, selection)
private var onCompleteConsumer: ((String) -> Unit)? = null

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.code.view.select
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileSystemNode
import javafx.beans.property.Property
import javafx.scene.control.TreeItem
import org.kordamp.ikonli.javafx.FontIcon

View File

@@ -6,9 +6,6 @@ import tornadofx.getValue
import tornadofx.setValue
class CodeVM(code: Code) : ItemViewModel<Code>(code) {
val fileProperty = bind(Code::fileProperty)
val file by fileProperty
val typeProperty = bind(Code::typeProperty)
val type by typeProperty

View File

@@ -0,0 +1,35 @@
package com.bartlomiejpluta.base.editor.file.model
import javafx.beans.value.ObservableValue
import java.io.InputStream
import java.io.OutputStream
import java.nio.charset.Charset
interface FileNode {
val nameProperty: ObservableValue<String>
val name: String
val extensionProperty: ObservableValue<String>
val extension: String
val absolutePathProperty: ObservableValue<String>
val absolutePath: String
val type: FileType
val parent: FileNode?
val children: Iterable<FileNode>
val allChildren: Iterable<FileNode>
fun delete()
fun rename(name: String)
fun refresh()
fun createNode(path: String): FileNode
fun inputStream(): InputStream
fun outputStream(): OutputStream
fun writeBytes(array: ByteArray)
fun writeText(text: String, charset: Charset = Charsets.UTF_8) = writeBytes(text.toByteArray(charset))
fun readText(charset: Charset = Charsets.UTF_8) = inputStream().reader(charset).readText()
}

View File

@@ -0,0 +1,99 @@
package com.bartlomiejpluta.base.editor.file.model
import javafx.beans.binding.Bindings.createStringBinding
import tornadofx.getValue
import tornadofx.observableListOf
import tornadofx.setValue
import tornadofx.toProperty
import java.io.File
import java.io.FileOutputStream
import java.nio.file.Path
class FileSystemNode(file: File, override val parent: FileSystemNode? = null) : FileNode {
private val fileProperty = file.toProperty()
var file by fileProperty
private set
override val nameProperty = createStringBinding({ fileProperty.value.name }, fileProperty)
override val name by nameProperty
override val extensionProperty = createStringBinding({ fileProperty.value.extension }, fileProperty)
override val extension by extensionProperty
override val absolutePathProperty = createStringBinding({ fileProperty.value.absolutePath }, fileProperty)
override val absolutePath by absolutePathProperty
override val type = when {
file.isFile -> FileType.FILE
file.isDirectory -> FileType.DIRECTORY
else -> throw IllegalStateException("Unsupported file type")
}
override val children = observableListOf<FileSystemNode>()
override val allChildren: List<FileSystemNode>
get() = children + children.flatMap { it.allChildren }
init {
refreshChildren()
fileProperty.addListener { _, _, f -> println(f) }
nameProperty.addListener { _, _, f -> println(f) }
}
private fun refreshChildren() {
if (type == FileType.DIRECTORY) {
children.clear()
file.listFiles()?.map { FileSystemNode(it, this) }?.let { children.addAll(it) }
}
}
override fun rename(name: String) {
val newFile = File(file.parent, name)
file.renameTo(newFile)
file = newFile
}
override fun delete() {
val deleted = when (type) {
FileType.FILE -> file.delete()
FileType.DIRECTORY -> file.deleteRecursively()
}
if (deleted) {
parent?.children?.remove(this)
}
}
override fun refresh() {
refreshChildren()
}
override fun createNode(path: String): FileSystemNode {
val segments = Path.of(path.replace("..", "."))
return segments.foldIndexed(this) { index, parent, segment ->
val file = File(parent.file, segment.toString())
when {
index < segments.count() - 1 -> file.mkdirs()
else -> file.createNewFile()
}
when (val child = findChild(file)) {
null -> FileSystemNode(file, parent).also { parent.children += it }
else -> child
}
}
}
override fun inputStream() = file.inputStream()
override fun outputStream() = file.outputStream()
override fun writeBytes(array: ByteArray): Unit = FileOutputStream(file).use { it.write(array) }
private fun findChild(file: File): FileSystemNode? {
return children.firstOrNull { it.file.name == file.name }
}
fun findByFile(file: File) = allChildren.firstOrNull { it.file.equals(file) }
}

View File

@@ -0,0 +1,6 @@
package com.bartlomiejpluta.base.editor.file.model
enum class FileType {
FILE,
DIRECTORY
}

View File

@@ -3,10 +3,10 @@ package com.bartlomiejpluta.base.editor.main.controller
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.code.model.CodeScope
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.code.viewmodel.CodeVM
import com.bartlomiejpluta.base.editor.command.context.UndoableScope
import com.bartlomiejpluta.base.editor.event.SelectMainViewTabEvent
import com.bartlomiejpluta.base.editor.file.model.FileNode
import com.bartlomiejpluta.base.editor.image.view.importing.ImportImageFragment
import com.bartlomiejpluta.base.editor.image.viewmodel.ImageAssetDataVM
import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset
@@ -87,12 +87,12 @@ class MainController : Controller() {
}
}
fun openScript(fsNode: FileSystemNode, line: Int = 1, column: Int = 1) {
val findScript = { script: Code -> script.file.absolutePath == fsNode.file.absolutePath }
fun openScript(fsNode: FileNode, line: Int = 1, column: Int = 1) {
val findScript = { script: Code -> script.fileNode.absolutePath == fsNode.absolutePath }
val updateExistingScope = { scope: CodeScope -> scope.setCaretPosition(line, column) }
openItem(findScript, updateExistingScope) {
val code = projectContext.loadScript(fsNode.fileProperty)
val code = projectContext.loadScript(fsNode)
val vm = CodeVM(code)
val scope = CodeScope(line, column)
setInScope(vm, scope)
@@ -151,13 +151,13 @@ class MainController : Controller() {
}
}
fun closeScript(fsNode: FileSystemNode) {
openItems.entries.firstOrNull { (_, item) -> item is Code && item.file.absolutePath == fsNode.file.absolutePath }?.key?.let {
fun closeScript(fsNode: FileNode) {
openItems.entries.firstOrNull { (_, item) -> item is Code && item.fileNode.absolutePath == fsNode.absolutePath }?.key?.let {
openItems.remove(it)
}
fsNode.allChildren.forEach { child ->
openItems.entries.firstOrNull { (_, item) -> item is Code && item.file.absolutePath == child.file.absolutePath }?.key?.let {
openItems.entries.firstOrNull { (_, item) -> item is Code && item.fileNode.absolutePath == child.absolutePath }?.key?.let {
openItems.remove(it)
}
}

View File

@@ -153,7 +153,7 @@ class MainView : View("BASE Game Editor") {
val editor = find<CodeEditorFragment>(scope)
content = editor.root
graphic = FontIcon("fa-code")
textProperty().bindBidirectional(item.fileProperty.select { it.name.toProperty() })
textProperty().bind(item.fileNode.nameProperty)
setOnClosed {
editor.shutdown()

View File

@@ -3,6 +3,7 @@ package com.bartlomiejpluta.base.editor.project.context
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.code.model.CodeType
import com.bartlomiejpluta.base.editor.file.model.FileNode
import com.bartlomiejpluta.base.editor.image.asset.ImageAsset
import com.bartlomiejpluta.base.editor.image.asset.ImageAssetData
import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset
@@ -18,7 +19,6 @@ import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import com.bartlomiejpluta.base.editor.util.uid.UID
import javafx.beans.binding.Bindings.createObjectBinding
import javafx.beans.property.ObjectProperty
import javafx.beans.property.Property
import javafx.beans.property.SimpleObjectProperty
import javafx.scene.image.Image
import org.springframework.beans.factory.annotation.Autowired
@@ -159,10 +159,10 @@ class DefaultProjectContext : ProjectContext {
} ?: throw IllegalStateException("There is no open project in the context")
}
override fun loadScript(fileProperty: Property<File>): Code {
override fun loadScript(fileNode: FileNode): Code {
val typeProperty = SimpleObjectProperty<CodeType>().apply {
bind(createObjectBinding({
when (fileProperty.value.extension.toLowerCase()) {
when (fileNode.extension.toLowerCase()) {
"java" -> CodeType.JAVA
else -> throw IllegalStateException("Unsupported script type")
}
@@ -170,12 +170,12 @@ class DefaultProjectContext : ProjectContext {
}
val code = fileProperty.value.readText()
val code = fileNode.readText()
return Code(fileProperty, typeProperty, code)
return Code(fileNode, typeProperty, code)
}
override fun saveScript(code: Code) {
code.file.writeText(code.code)
code.fileNode.writeText(code.code)
}
}

View File

@@ -2,6 +2,7 @@ package com.bartlomiejpluta.base.editor.project.context
import com.bartlomiejpluta.base.editor.asset.model.Asset
import com.bartlomiejpluta.base.editor.code.model.Code
import com.bartlomiejpluta.base.editor.file.model.FileNode
import com.bartlomiejpluta.base.editor.image.asset.ImageAsset
import com.bartlomiejpluta.base.editor.image.asset.ImageAssetData
import com.bartlomiejpluta.base.editor.map.model.map.GameMap
@@ -9,7 +10,6 @@ import com.bartlomiejpluta.base.editor.project.model.Project
import com.bartlomiejpluta.base.editor.tileset.asset.TileSetAssetData
import com.bartlomiejpluta.base.editor.tileset.model.TileSet
import javafx.beans.property.ObjectProperty
import javafx.beans.property.Property
import javafx.scene.image.Image
import java.io.File
@@ -33,6 +33,6 @@ interface ProjectContext {
fun deleteAsset(asset: Asset)
fun loadScript(fileProperty: Property<File>): Code
fun loadScript(fileNode: FileNode): Code
fun saveScript(code: Code)
}

View File

@@ -1,6 +1,6 @@
package com.bartlomiejpluta.base.editor.project.model
import com.bartlomiejpluta.base.editor.code.model.FileSystemNode
import com.bartlomiejpluta.base.editor.file.model.FileSystemNode
import com.bartlomiejpluta.base.editor.image.asset.ImageAsset
import com.bartlomiejpluta.base.editor.map.asset.GameMapAsset
import com.bartlomiejpluta.base.editor.tileset.asset.TileSetAsset