Skip to content
This repository has been archived by the owner on Sep 28, 2024. It is now read-only.

Commit

Permalink
Hot View Reload (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
Edvin Syse committed Apr 26, 2016
1 parent 035ff35 commit 58b196f
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ All notable changes to this project will be documented in this file.

### Added

- Hot View Reload (/~https://github.com/edvin/tornadofx/issues/96)
- `children(nodeList)` builder helper to redirect built children to a specific node list (/~https://github.com/edvin/tornadofx/issues/95)
- `buttonbar` builder (/~https://github.com/edvin/tornadofx/issues/95)
- `ButtonBar.button` builder (/~https://github.com/edvin/tornadofx/issues/95)
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/tornadofx/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package tornadofx

import javafx.application.Application
import javafx.beans.property.SimpleStringProperty
import javafx.collections.FXCollections
import javafx.collections.ObservableMap
import javafx.scene.Scene
import javafx.stage.Stage
import kotlin.properties.ReadOnlyProperty
Expand All @@ -19,6 +21,7 @@ abstract class App : Application() {

stage.apply {
scene = Scene(view.root)
view.properties["tornadofx.scene"] = scene
scene.stylesheets.addAll(FX.stylesheets)
titleProperty().bind(view.titleProperty)
show()
Expand All @@ -40,6 +43,9 @@ abstract class SingleViewApp(title: String? = null) : App(), ViewContainer {
var stylesheet: Stylesheet? = null
override val primaryView = javaClass.kotlin
override val titleProperty = SimpleStringProperty(title)
override val properties: ObservableMap<Any, Any>
get() = _properties.value
private val _properties = lazy { FXCollections.observableHashMap<Any, Any>() }

init {
FX.components[javaClass.kotlin] = this
Expand Down
18 changes: 18 additions & 0 deletions src/main/java/tornadofx/Component.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package tornadofx

import javafx.application.Platform
import javafx.beans.property.Property
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
Expand All @@ -16,6 +17,7 @@ import javafx.scene.input.KeyEvent
import javafx.stage.Modality
import javafx.stage.Stage
import javafx.stage.StageStyle
import java.io.Serializable
import java.net.URL
import java.nio.file.Files
import java.nio.file.Paths
Expand Down Expand Up @@ -94,21 +96,34 @@ abstract class Component {

@Deprecated("Clashes with Region.background, so runAsync is a better name", ReplaceWith("runAsync"), DeprecationLevel.WARNING)
fun <T> background(func: () -> T) = task(func)

fun <T> runAsync(func: () -> T) = task(func)
infix fun <T> Task<T>.ui(func: (T) -> Unit) = success(func)
}

abstract class Controller : Component(), Injectable

interface ViewContainer : Injectable {
val properties: ObservableMap<Any, Any>
val root: Parent
val titleProperty: Property<String>
fun pack(): Serializable? = null
fun unpack(state: Serializable?) {
}
}

abstract class UIComponent : Component(), ViewContainer {
var fxmlLoader: FXMLLoader? = null
var modalStage: Stage? = null

private fun tagRoot() {
root.properties["tornadofx.uicomponent"] = this@UIComponent
}

init {
Platform.runLater { tagRoot() }
}

fun openModal(stageStyle: StageStyle = StageStyle.DECORATED, modality: Modality = Modality.APPLICATION_MODAL, escapeClosesWindow: Boolean = true) {
if (modalStage == null) {
if (root !is Parent) {
Expand All @@ -129,10 +144,13 @@ abstract class UIComponent : Component(), ViewContainer {
stylesheets.addAll(FX.stylesheets)
icons += FX.primaryStage.icons
scene = this
properties["tornadofx.scene"] = this
}

show()

if (FX.reloadStylesheetsOnFocus) reloadStylesheetsOnFocus()
if (FX.reloadViewsOnFocus) reloadViewsOnFocus()
}
}
}
Expand Down
29 changes: 29 additions & 0 deletions src/main/java/tornadofx/FX.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ import javafx.application.Platform
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
import javafx.scene.Scene
import javafx.scene.image.Image
import javafx.scene.layout.Pane
import javafx.stage.Stage
import java.util.*
import java.util.concurrent.CountDownLatch
Expand All @@ -25,6 +27,8 @@ class FX {
var dicontainer: DIContainer? = null
@JvmStatic
var reloadStylesheetsOnFocus = false
@JvmStatic
var reloadViewsOnFocus = false

private val _locale: SimpleObjectProperty<Locale> = object : SimpleObjectProperty<Locale>() {
override fun invalidated() = loadMessages()
Expand Down Expand Up @@ -87,11 +91,32 @@ class FX {
FX.primaryStage = primaryStage
FX.application = application
if (reloadStylesheetsOnFocus) primaryStage.reloadStylesheetsOnFocus()
if (reloadViewsOnFocus) primaryStage.reloadViewsOnFocus()
}

@JvmStatic
fun <T: Injectable> find(componentType: Class<T>) =
find(componentType.kotlin)

fun replaceComponent(obsolete: UIComponent) {
if (obsolete is View) components.remove(obsolete.javaClass.kotlin)
val replacement = find(obsolete.javaClass.kotlin)

val state = obsolete.pack()
replacement.unpack(state)

if (obsolete.root.parent is Pane) {
(obsolete.root.parent as Pane).children.apply {
val index = indexOf(obsolete.root)
remove(obsolete.root)
add(index, replacement.root)
}
} else {
val scene = obsolete.properties["tornadofx.scene"] as Scene
replacement.properties["tornadofx.scene"] = scene
scene.root = replacement.root
}
}
}
}

Expand All @@ -104,6 +129,10 @@ fun reloadStylesheetsOnFocus() {
FX.reloadStylesheetsOnFocus = true
}

fun reloadViewsOnFocus() {
FX.reloadViewsOnFocus = true
}

fun importStylesheet(stylesheet: String) {
val css = FX::class.java.getResource(stylesheet)
FX.stylesheets.add(css.toExternalForm())
Expand Down
26 changes: 25 additions & 1 deletion src/main/java/tornadofx/Nodes.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import javafx.geometry.Insets
import javafx.geometry.Pos
import javafx.geometry.VPos
import javafx.scene.Node
import javafx.scene.Parent
import javafx.scene.Scene
import javafx.scene.control.*
import javafx.scene.control.cell.CheckBoxTableCell
Expand All @@ -25,6 +26,7 @@ import javafx.util.converter.*
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.LocalTime
import java.util.*
import kotlin.reflect.KClass

fun TableColumnBase<*, *>.hasClass(className: String) = styleClass.contains(className)
Expand Down Expand Up @@ -64,9 +66,31 @@ fun Scene.reloadStylesheets() {
stylesheets.addAll(styles)
}

fun Scene.reloadViews() {
findUIComponents().forEach { FX.replaceComponent(it) }
}

fun Scene.findUIComponents(): List<UIComponent> {
val list = ArrayList<UIComponent>()
root.findUIComponents(list)
return list
}

private fun Parent.findUIComponents(list: MutableList<UIComponent>) {
val uicmp = properties["tornadofx.uicomponent"]
if (uicmp is UIComponent) list += uicmp
childrenUnmodifiable.filtered { it is Parent }.forEach { (it as Parent).findUIComponents(list) }
}

fun Stage.reloadStylesheetsOnFocus() {
focusedProperty().addListener { obs, old, focused ->
if (focused) scene.reloadStylesheets()
if (focused && FX.initialized.value) scene.reloadStylesheets()
}
}

fun Stage.reloadViewsOnFocus() {
focusedProperty().addListener { obs, old, focused ->
if (focused && FX.initialized.value) scene.reloadViews()
}
}

Expand Down

0 comments on commit 58b196f

Please sign in to comment.