diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/App.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/App.kt index 94a24af2..12366346 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/App.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/App.kt @@ -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() } } diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/component/CodeEditor.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/component/CodeEditor.kt index d1e100ae..c24cd095 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/component/CodeEditor.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/component/CodeEditor.kt @@ -20,7 +20,18 @@ class CodeEditor(private val highlighter: ObservableValue 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 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 editor.redo() } + fun shutdownHighlighterThread() { + highlightingSubscription.unsubscribe() + executor.shutdownNow() + } + private fun initAutoIndents() { editor.addEventHandler(KeyEvent.KEY_PRESSED) { event -> if (event.code === KeyCode.ENTER) { diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorFragment.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorFragment.kt index 5a3a8385..833715eb 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorFragment.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorFragment.kt @@ -5,5 +5,9 @@ import tornadofx.Fragment class CodeEditorFragment : Fragment() { private val editorView = find() + fun shutdown() { + editorView.shutdown() + } + override val root = editorView.root } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorView.kt index 2a05c526..a8776d69 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/code/view/CodeEditorView.kt @@ -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")) { diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt index 5dbf22d6..e4b32c8d 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/controller/MainController.kt @@ -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() + } } \ No newline at end of file diff --git a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt index 630bc4e1..7b39aeec 100644 --- a/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt +++ b/editor/src/main/kotlin/com/bartlomiejpluta/base/editor/main/view/MainView.kt @@ -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(scope).root - textProperty().bindBidirectional(item.fileProperty.select { it.name.toProperty() }) + val editor = find(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")