edvin / tornadofx

Lightweight JavaFX Framework for Kotlin

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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.

Sure. Probably best to raise support for compile-time DI frameworks like Dagger in another issue at some point.