onDock() not called when a View instance rather than class is passed to replaceWith
kiwiandroiddev opened this issue · comments
onDock()
is called on LoginView
when it becomes visible in this example:
class SplashView : View() {
override val root = vbox {
label("Splash screen")
}
override fun onDock() {
replaceWith(LoginView::class)
}
}
...but not this one:
class SplashView : View() {
override val root = vbox {
label("Splash screen")
}
override fun onDock() {
replaceWith(LoginView())
}
}
I understand the first example is more conventional. However it seems as though onDock
should still be called in the second case, given it shows correctly and is interactive?
Doing some investigation with the debugger shows that LoginView
's rootParentChangeListener
is not being fired at all in the second example.
My understanding is in TornadoFX you should never instantiate your views / fragments directly. You should always use the find/add
methods.
Going into the code, you can see when you have replaceWith(LoginView::class)
the following method is called:
fun <T : UIComponent> replaceWith(
component: KClass<T>,
transition: ViewTransition? = null,
sizeToScene: Boolean = false,
centerOnScreen: Boolean = false
) = replaceWith(find(component, scope), transition, sizeToScene, centerOnScreen)
Behind the scenes TornadoFX is creating it via find
for you. If we following this we see in the find function in FX.kt that TornadoFX will look to see if the UIComponent already exists in the scope it is being looked up in and if not it will create it. You can see it runs an init()
function on the created component. Let's have a look at that in Component.kt:
internal fun init() {
if (isInitialized) return
root.properties[UI_COMPONENT_PROPERTY] = this
root.parentProperty().addListener(rootParentChangeListener)
root.sceneProperty().addListener(rootSceneChangeListener)
isInitialized = true
}
And theres those listeners finally being properly attached which you correctly identified as missing in your latter case.
I don't think the find function that is called in your second example should be accessible by the user. Maybe there's a reason it is..
I see. That makes sense, thanks.
To step back a bit, I came across this by attempting to use the Dagger DI framework to constuct TornadoFX Views as per the example here: https://github.com/otruffer/caffeinated_blade_storm. It works by (mostly) reimplementing App
's start()
method to use Dagger to provide the primary View.
However it looks like TornadoFX really isn't designed to support this kind of extension. Some important methods called from start
are internal, so you can't really reimplement start
properly.
I'm wondering if it's possible to make use of Dagger in a way that doesn't go against the grain of the framework. It looks like the current DI hook points are set up more for runtime DI frameworks like Guice and Koin, but perhaps it's possible.
@kiwiandroiddev close ?
Sure. Probably best to raise support for compile-time DI frameworks like Dagger in another issue at some point.