[Editor] Enable code editor shutdown when code editor tab is closed as well as application is being shutdown

This commit is contained in:
2021-02-24 21:20:06 +01:00
parent 6f89c5f6fb
commit 569daa7ebd
6 changed files with 64 additions and 20 deletions

View File

@@ -1,6 +1,8 @@
package com.bartlomiejpluta.base.editor
import com.bartlomiejpluta.base.editor.main.controller.MainController
import com.bartlomiejpluta.base.editor.main.view.MainView
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication
import org.springframework.context.ConfigurableApplicationContext
@@ -14,6 +16,9 @@ import kotlin.reflect.KClass
open class EditorApp : App(MainView::class) {
private lateinit var context: ConfigurableApplicationContext
@Autowired
private lateinit var mainController: MainController
override fun init() {
this.context = SpringApplication.run(this.javaClass)
context.autowireCapableBeanFactory.autowireBean(this)
@@ -27,6 +32,7 @@ open class EditorApp : App(MainView::class) {
override fun stop() {
super.stop()
context.close()
mainController.clearResources()
}
}

View File

@@ -20,7 +20,18 @@ class CodeEditor(private val highlighter: ObservableValue<out SyntaxHighlighter>
StackPane() {
private val editor = CodeArea()
private val executor = Executors.newSingleThreadExecutor()
private val cleanupWhenDone = editor.multiPlainChanges()
private val highlightingSubscription = editor.multiPlainChanges()
.successionEnds(Duration.ofMillis(500))
.supplyTask(this::computeHighlightingAsync)
.awaitLatest(editor.multiPlainChanges())
.filterMap {
when {
it.isSuccess -> Optional.of(it.get())
else -> Optional.empty()
}
}
.subscribe(this::applyHighlighting)
init {
editor.replaceText(0, 0, codeProperty.value)
@@ -28,20 +39,6 @@ class CodeEditor(private val highlighter: ObservableValue<out SyntaxHighlighter>
editor.paragraphGraphicFactory = LineNumberFactory.get(editor)
applyHighlighting(highlighter.value.highlight(editor.text))
cleanupWhenDone
.successionEnds(Duration.ofMillis(500))
.supplyTask(this::computeHighlightingAsync)
.awaitLatest(editor.multiPlainChanges())
.filterMap {
when {
it.isSuccess -> Optional.of(it.get())
else -> Optional.empty()
}
}
.subscribe(this::applyHighlighting)
initAutoIndents()
children += VirtualizedScrollPane(editor)
@@ -55,6 +52,11 @@ class CodeEditor(private val highlighter: ObservableValue<out SyntaxHighlighter>
editor.redo()
}
fun shutdownHighlighterThread() {
highlightingSubscription.unsubscribe()
executor.shutdownNow()
}
private fun initAutoIndents() {
editor.addEventHandler(KeyEvent.KEY_PRESSED) { event ->
if (event.code === KeyCode.ENTER) {

View File

@@ -5,5 +5,9 @@ import tornadofx.Fragment
class CodeEditorFragment : Fragment() {
private val editorView = find<CodeEditorView>()
fun shutdown() {
editorView.shutdown()
}
override val root = editorView.root
}

View File

@@ -27,6 +27,10 @@ class CodeEditorView : View() {
private val editor = CodeEditor(highlighter, codeVM.codeProperty)
fun shutdown() {
editor.shutdownHighlighterThread()
}
override val root = borderpane {
top = toolbar {
button(graphic = FontIcon("fa-floppy-o")) {

View File

@@ -69,7 +69,7 @@ class MainController : Controller() {
title = "Load Project",
filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")),
).getOrNull(0)?.let {
openItems.clear()
clearResources()
projectContext.open(it)
}
}
@@ -143,4 +143,8 @@ class MainController : Controller() {
}
}
}
fun clearResources() {
openItems.clear()
}
}

View File

@@ -11,6 +11,7 @@ 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.collections.MapChangeListener
import javafx.event.Event
import javafx.scene.control.Tab
import org.kordamp.ikonli.javafx.FontIcon
import tornadofx.*
@@ -50,8 +51,26 @@ class MainView : View("BASE Game Editor") {
it.wasRemoved() -> {
val tab = openTabs[it.key]
tabs -= tab
openTabs.remove(it.key)
// FIXME
// This ugly hack prevents from double-firing the CLOSED EVENT
// when user tries to close tab from the UI (cross sign).
// When user clicks the close button, the event is fired automatically,
// which removes the item from mainController.openItems.
// Right here, this listener would fire the event once again,
// so essentially we need to check if the tab exists in TabPane.tabs
// (which means the tab wasn't be closed from UI). If so, we need to fire
// the event manually since it won't be fired by TabPane.
// Otherwise, if the tab does not exist in tabs anymore, the tab
// was closed from UI and likely the event has been fired automatically.
// The same goes for the TAB_CLOSE_REQUEST event.
if (tab in tabs) {
Event.fireEvent(tab, Event(Tab.CLOSED_EVENT))
Event.fireEvent(tab, Event(Tab.TAB_CLOSE_REQUEST_EVENT))
}
tabs -= tab
}
}
})
@@ -82,10 +101,15 @@ class MainView : View("BASE Game Editor") {
is Code -> Tab().apply {
val vm = CodeVM(item)
setInScope(vm, scope)
content = find<CodeEditorFragment>(scope).root
textProperty().bindBidirectional(item.fileProperty.select { it.name.toProperty() })
val editor = find<CodeEditorFragment>(scope)
content = editor.root
graphic = FontIcon("fa-code")
setOnClosed { mainController.openItems.remove(scope) }
textProperty().bindBidirectional(item.fileProperty.select { it.name.toProperty() })
setOnClosed {
editor.shutdown()
mainController.openItems.remove(scope)
}
}
else -> throw IllegalStateException("Unsupported tab item")