edvin / tornadofx

Lightweight JavaFX Framework for Kotlin
Apache License 2.0
3.67k stars 269 forks source link

How to call EventBus fire from custom view #993

Closed balage1551 closed 5 years ago

balage1551 commented 5 years ago

I have a custom class derived from AnchorPane:

class ViewerPane : AnchorPane() { ... }

I add several controls to the pane and also register on mouse events. In case a specific mouse event occurs, I would like to fire an EventBus event:

 subScene = SubScene(root, 100.0, 100.0, true, SceneAntialiasing.BALANCED)
 add(subScene)
 subScene.onMouseDragged = EventHandler<MouseEvent> { me ->
     [lots of code]
     // Here I would like to fire an event:
     fire(RotateEvent(mouseDeltaX, mouseDeltaY, speed))
}

However, fire is not a top-level function, so I am lost how to fire event from this point of code. What is the right way to call it? (Also, my custom ViewerPane needs to register for some events, so this code-scope problem would raise there too.) Or this is not what tornadoFX event bus was for? Should I use a general purpose EventBus implementation?

edvin commented 5 years ago

You can fire events using FX.eventbus.fire(). I normally create custom ui elements as Fragments instead of Node subclasses. This gives me the ability to reach all of the life cycle callbacks of the framework more easily, and it normally has no drawbacks, so you might consider that approach.

balage1551 commented 5 years ago

I'll try that approach.

balage1551 commented 5 years ago

Fire works fine when I switched to Fragment:

class ViewerPane : Fragment() {

    override val root = AnchorPane()
    ...
}

But when I tried to add my fragment to the parent view:

class MainView : View("Viewer") {

    private lateinit var viewer: ViewerPane

    override val root = borderpane {
        prefWidth = 1400.0
        prefHeight = 1000.0

        center {
            viewer = find<ViewerPane>().apply {
                axesVisible = true
            }
            add(viewer)
        }

    }
}

I got a StackOverflow:

java.lang.RuntimeException: Exception in Application start method
    at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:917)
    at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$154(LauncherImpl.java:182)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.StackOverflowError
    at javafx.scene.Parent.impl_getAllParentStylesheets(Parent.java:1215)
    at javafx.scene.Parent.impl_getAllParentStylesheets(Parent.java:1215)
    at com.sun.javafx.css.StyleManager.gatherParentStylesheets(StyleManager.java:1587)
    at com.sun.javafx.css.StyleManager.findMatchingStyles(StyleManager.java:1659)
    at javafx.scene.CssStyleHelper.createStyleHelper(CssStyleHelper.java:111)
    at javafx.scene.Node.reapplyCss(Node.java:8985)
    at javafx.scene.Node.reapplyCss(Node.java:8985)
    at javafx.scene.Node.reapplyCss(Node.java:8985)
    at javafx.scene.Node.reapplyCss(Node.java:8985)
    ...

What did I do wrong? I have no experience with Fragments.

edvin commented 5 years ago

The right way to add the ViewerPane would be like this:

center {
    add<ViewerPane> {
        axesVisible = true
    }
}

Without seeing a runnable code example it's hard to tell exactly what is going wrong here, so if this doesn't solve your problem, please post a complete example.

balage1551 commented 5 years ago

I've found what causes the infinite recursion. I don't know why.

I add a SubScene component to the root (for 3D):


class ViewerPane : Fragment() {

    private lateinit var subScene: SubScene
    override val root = anchorpane {
        subScene = SubScene(this, 100.0, 100.0, true, SceneAntialiasing.BALANCED)
        add(subScene)
    }

As long as I don't add this control to the fragment, it works fine. SubScene is a very special control. Could it be a bug between with TornadoFX fragment and SubScene?

edvin commented 5 years ago

Quite possibly. Can you create a minimal code samle that throws this exception?

balage1551 commented 5 years ago

Sure. I'll open a new issue for this.