[Editor] Enable code editor shutdown when code editor tab is closed as well as application is being shutdown
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package com.bartlomiejpluta.base.editor
|
package com.bartlomiejpluta.base.editor
|
||||||
|
|
||||||
|
import com.bartlomiejpluta.base.editor.main.controller.MainController
|
||||||
import com.bartlomiejpluta.base.editor.main.view.MainView
|
import com.bartlomiejpluta.base.editor.main.view.MainView
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.boot.SpringApplication
|
import org.springframework.boot.SpringApplication
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication
|
import org.springframework.boot.autoconfigure.SpringBootApplication
|
||||||
import org.springframework.context.ConfigurableApplicationContext
|
import org.springframework.context.ConfigurableApplicationContext
|
||||||
@@ -14,6 +16,9 @@ import kotlin.reflect.KClass
|
|||||||
open class EditorApp : App(MainView::class) {
|
open class EditorApp : App(MainView::class) {
|
||||||
private lateinit var context: ConfigurableApplicationContext
|
private lateinit var context: ConfigurableApplicationContext
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private lateinit var mainController: MainController
|
||||||
|
|
||||||
override fun init() {
|
override fun init() {
|
||||||
this.context = SpringApplication.run(this.javaClass)
|
this.context = SpringApplication.run(this.javaClass)
|
||||||
context.autowireCapableBeanFactory.autowireBean(this)
|
context.autowireCapableBeanFactory.autowireBean(this)
|
||||||
@@ -27,6 +32,7 @@ open class EditorApp : App(MainView::class) {
|
|||||||
override fun stop() {
|
override fun stop() {
|
||||||
super.stop()
|
super.stop()
|
||||||
context.close()
|
context.close()
|
||||||
|
mainController.clearResources()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,18 @@ class CodeEditor(private val highlighter: ObservableValue<out SyntaxHighlighter>
|
|||||||
StackPane() {
|
StackPane() {
|
||||||
private val editor = CodeArea()
|
private val editor = CodeArea()
|
||||||
private val executor = Executors.newSingleThreadExecutor()
|
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 {
|
init {
|
||||||
editor.replaceText(0, 0, codeProperty.value)
|
editor.replaceText(0, 0, codeProperty.value)
|
||||||
@@ -28,20 +39,6 @@ class CodeEditor(private val highlighter: ObservableValue<out SyntaxHighlighter>
|
|||||||
editor.paragraphGraphicFactory = LineNumberFactory.get(editor)
|
editor.paragraphGraphicFactory = LineNumberFactory.get(editor)
|
||||||
applyHighlighting(highlighter.value.highlight(editor.text))
|
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()
|
initAutoIndents()
|
||||||
|
|
||||||
children += VirtualizedScrollPane(editor)
|
children += VirtualizedScrollPane(editor)
|
||||||
@@ -55,6 +52,11 @@ class CodeEditor(private val highlighter: ObservableValue<out SyntaxHighlighter>
|
|||||||
editor.redo()
|
editor.redo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun shutdownHighlighterThread() {
|
||||||
|
highlightingSubscription.unsubscribe()
|
||||||
|
executor.shutdownNow()
|
||||||
|
}
|
||||||
|
|
||||||
private fun initAutoIndents() {
|
private fun initAutoIndents() {
|
||||||
editor.addEventHandler(KeyEvent.KEY_PRESSED) { event ->
|
editor.addEventHandler(KeyEvent.KEY_PRESSED) { event ->
|
||||||
if (event.code === KeyCode.ENTER) {
|
if (event.code === KeyCode.ENTER) {
|
||||||
|
|||||||
@@ -5,5 +5,9 @@ import tornadofx.Fragment
|
|||||||
class CodeEditorFragment : Fragment() {
|
class CodeEditorFragment : Fragment() {
|
||||||
private val editorView = find<CodeEditorView>()
|
private val editorView = find<CodeEditorView>()
|
||||||
|
|
||||||
|
fun shutdown() {
|
||||||
|
editorView.shutdown()
|
||||||
|
}
|
||||||
|
|
||||||
override val root = editorView.root
|
override val root = editorView.root
|
||||||
}
|
}
|
||||||
@@ -27,6 +27,10 @@ class CodeEditorView : View() {
|
|||||||
|
|
||||||
private val editor = CodeEditor(highlighter, codeVM.codeProperty)
|
private val editor = CodeEditor(highlighter, codeVM.codeProperty)
|
||||||
|
|
||||||
|
fun shutdown() {
|
||||||
|
editor.shutdownHighlighterThread()
|
||||||
|
}
|
||||||
|
|
||||||
override val root = borderpane {
|
override val root = borderpane {
|
||||||
top = toolbar {
|
top = toolbar {
|
||||||
button(graphic = FontIcon("fa-floppy-o")) {
|
button(graphic = FontIcon("fa-floppy-o")) {
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ class MainController : Controller() {
|
|||||||
title = "Load Project",
|
title = "Load Project",
|
||||||
filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")),
|
filters = arrayOf(FileChooser.ExtensionFilter("BASE Editor Project (*.bep)", "*.bep")),
|
||||||
).getOrNull(0)?.let {
|
).getOrNull(0)?.let {
|
||||||
openItems.clear()
|
clearResources()
|
||||||
projectContext.open(it)
|
projectContext.open(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -143,4 +143,8 @@ class MainController : Controller() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun clearResources() {
|
||||||
|
openItems.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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.map.viewmodel.GameMapVM
|
||||||
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
|
import com.bartlomiejpluta.base.editor.project.context.ProjectContext
|
||||||
import javafx.collections.MapChangeListener
|
import javafx.collections.MapChangeListener
|
||||||
|
import javafx.event.Event
|
||||||
import javafx.scene.control.Tab
|
import javafx.scene.control.Tab
|
||||||
import org.kordamp.ikonli.javafx.FontIcon
|
import org.kordamp.ikonli.javafx.FontIcon
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
@@ -50,8 +51,26 @@ class MainView : View("BASE Game Editor") {
|
|||||||
|
|
||||||
it.wasRemoved() -> {
|
it.wasRemoved() -> {
|
||||||
val tab = openTabs[it.key]
|
val tab = openTabs[it.key]
|
||||||
tabs -= tab
|
|
||||||
openTabs.remove(it.key)
|
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 {
|
is Code -> Tab().apply {
|
||||||
val vm = CodeVM(item)
|
val vm = CodeVM(item)
|
||||||
setInScope(vm, scope)
|
setInScope(vm, scope)
|
||||||
content = find<CodeEditorFragment>(scope).root
|
val editor = find<CodeEditorFragment>(scope)
|
||||||
textProperty().bindBidirectional(item.fileProperty.select { it.name.toProperty() })
|
content = editor.root
|
||||||
graphic = FontIcon("fa-code")
|
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")
|
else -> throw IllegalStateException("Unsupported tab item")
|
||||||
|
|||||||
Reference in New Issue
Block a user