edvin / tornadofx

Lightweight JavaFX Framework for Kotlin
Apache License 2.0
3.68k stars 272 forks source link

custom controls with dsl #1230

Open kovbe11 opened 4 years ago

kovbe11 commented 4 years ago

I'm new to TornadoFX and to Kotlin too, familiar with JFX mostly. Sorry if I missed the answer in your documentation or if this is a rather weird use case of TornadoFX. I'm developing my first TornadoFX app, and it's my first Kotlin project too, and I'd like to do it right.

I have this bit of code here:

var draggingTab: Tab? = null

class DraggableTabPane : TabPane() {
    init {
        onDragDropped =
                EventHandler { event ->
                    val dragboard = event.dragboard
                    if (dragboard?.string == "tab") {
                        if (draggingTab?.tabPane != this) {
                            draggingTab!!.tabPane!!.tabs!!.remove(draggingTab)
                            tabs.add(draggingTab)
                            selectionModel.select(draggingTab)
                            event.isDropCompleted = true
                            draggingTab = null
                            event.consume()
                        }
                    }
                }

        onDragOver =
                EventHandler { event ->
                    val dragboard = event.dragboard
                    if (dragboard?.string == "tab") {
                        if (draggingTab?.tabPane != this) {
                            event.acceptTransferModes(TransferMode.MOVE)
                            event.consume()
                        }
                    }

                }
    }

}

class DraggableTab : Tab() {
    init {
        val label = Label(text)
        text = null
        label.onDragDetected =
                EventHandler { event ->
                    val dragboard = label.startDragAndDrop(TransferMode.MOVE)
                    val clipboardContent = ClipboardContent()
                    clipboardContent.putString("tab")
                    dragboard.setContent(clipboardContent)
                    draggingTab = this
                    event.consume()
                }
        graphic = label
    }
}

As you could guess, this is supposed implement a Tab class that is draggable between other TabPanes.

I'd like to use this something like this:

        splitpane{
            setDividerPosition(0, 0.5)
            draggabletabpane{
                draggabletab{
                    //and other stuff
                }
                draggabletab{
                    //and other stuff
                }
            }
            draggabletabpane{
                draggabletab{
                    //and other stuff
                }
                draggabletab{
                    //and other stuff
                }
            }
        }

In short, I'd like to reimplement intellij's "split vertically" feature in TornadoFX. Obviously I could copy paste the code needed to make a tab and a tabpane draggable into the dsl's panes but that seems like the worst solution ever.

How could I make this happen? If you have any suggestions like you already have an implementation for this problem that I haven't found or I could do this better without extending the dsl I'd be happy as well.

Also, I'm not sure my draggable implementation is a good implementation, that's basically a copy-pasted java code from stackoverflow rewritten in kotlin with the minimal code it managed to work with. If you could do that much better please let me know.

Thanks!

kovbe11 commented 4 years ago

@edvin can you point me to the right direction please?

I added these two functions, is this the right way? It works, but I'm not sure it won't cause any issues.

fun EventTarget.draggabletabpane(op: DraggableTabPane.() -> Unit = {}) =  DraggableTabPane().attachTo(this, op)

fun DraggableTabPane.draggabletab(text: String? = null, tag: Any? = null, op: Tab.() -> Unit = {}): DraggableTab {
    val tab = DraggableTab(text)
    tab.tag = tag
    tabs.add(tab)
    return tab.also(op)
}
edvin commented 4 years ago

You can have a look at how we create these in the framework itself, look at the hbox() builder for a simple example :)