如何在Kotlin中围绕JavaFX组件构建构建器模式

时间:2017-06-22 06:13:15

标签: javafx kotlin builder

我试图在Kotlin中为javafx组件提出一个构建器模式。该模式将如下所示

fun main(args: Array<String>) {
    val vb = vbox {
        child {
            hbox {
                child {
                    label {
                        prefWidth = 20.0
                    }
                    button {
                        text = "Click"
                    }
                }
            }
            label {
                prefHeight = 80.0
            }
        }
    }
}

到目前为止我所做的工作如下,但child未公开label类中声明的buttonChild方法。有什么指针吗?

fun Pane.child(init: (Pane.() -> Unit)? = null): Child {
    val ch = Child()
    init?.invoke(this)
    ch.parent = this
    return ch
}

class Child {
    var parent: Pane? = null

    fun <T : Node> initChildNode(styleClass: String? = null, tag: T, init: (T.() -> Unit)? = null): T {
        init?.invoke(tag)
        tag.styleClass.add(styleClass)
        parent?.children?.add(tag)
        return tag
    }

    fun region(styleClass: String? = null, init: (Region.() -> Unit)? = null) = initChildNode(styleClass, Region(), init)
    fun vbox(styleClass: String? = null, init: (VBox.() -> Unit)? = null) = initChildNode(styleClass, VBox(), init)
    fun hbox(styleClass: String? = null, init: (HBox.() -> Unit)? = null) = initChildNode(styleClass, HBox(), init)
    fun label(styleClass: String? = null, init: (Label.() -> Unit)? = null) = initChildNode(styleClass, Label(), init)
    fun button(styleClass: String? = null, init: (Button.() -> Unit)? = null) = initChildNode(styleClass, Button(), init)

}

fun vbox(styleClass: String? = null, init: (VBox.() -> Unit)? = null) = initNode(styleClass, VBox(), init)
fun hbox(styleClass: String? = null, init: (HBox.() -> Unit)? = null) = initNode(styleClass, HBox(), init)

fun <T : Node> initNode(styleClass: String? = null, tag: T, init: (T.() -> Unit)? = null): T {
    init?.invoke(tag)
    tag.styleClass.add(styleClass)
    return tag
}

注意:我已经查看了TornadoFX库,但我想提出一个我自己的解决方案,主要是出于学习目的。

1 个答案:

答案 0 :(得分:0)

我终于使用@ zsmb13的建议提出了解决方案。

这是其他人的参考

fun <T : Pane> T.children(init: (Children.() -> Unit)? = null): Children {
    val ch = Children()
    ch.parent = this
    init?.invoke(ch)
    return ch
}

class Children : Layout() {
    var parent: Pane? = null

    override fun <T : Node> initNode(styleClass: String?, tag: T, init: (T.() -> Unit)?): T {
        val node: T = super.initNode(styleClass, tag, init)
        parent?.children?.add(node)
        return node
    }
}

open class Layout {
    var pane: Pane? = null

    open fun <T : Node> initNode(styleClass: String? = null, tag: T, init: (T.() -> Unit)? = null): T {
        init?.invoke(tag)
        tag.styleClass.add(styleClass)
        return tag
    }

    fun region(styleClass: String? = null, init: (Region.() -> Unit)? = null) = initNode(styleClass, Region(), init)
    fun vbox(styleClass: String? = null, init: (VBox.() -> Unit)? = null) = initNode(styleClass, VBox(), init)
    fun hbox(styleClass: String? = null, init: (HBox.() -> Unit)? = null) = initNode(styleClass, HBox(), init)
    fun label(styleClass: String? = null, init: (Label.() -> Unit)? = null) = initNode(styleClass, Label(), init)
    fun button(styleClass: String? = null, init: (Button.() -> Unit)? = null) = initNode(styleClass, Button(), init)
    fun progressBar(styleClass: String? = null, init: (ProgressBar.() -> Unit)? = null) = initNode(styleClass, ProgressBar(), init)
}

fun <T : Pane> layout(styleSheet: String = "", init: Layout.() -> T): T {
    val layout = Layout()
    val pane = layout.init()
    layout.pane = pane
    pane.stylesheets.add(layout.javaClass.classLoader.getResource(styleSheet).toExternalForm())
    return pane
}

用法如下:

private var loadProgress: ProgressBar? = null
private var progressText: Label? = null

val layout = layout("xyz.css") {
            vbox("screen") {
                prefWidth = 500
                prefHeight = 450
                children {
                    region { minHeight = 250.0 }
                    hbox {
                        children {
                            region { minWidth = 30.0 }
                            label("large-label") {
                                text = "Foo"
                            }
                            label("large-label") {
                                text = "Bar"
                            }
                            region { minWidth = 20.0 }
                            label("small-label") {
                                text = "xyz"
                            }
                        }
                    }
                    region { minHeight = 10.0 }
                    loadProgress = progressBar("progress") { minWidth = this@vbox.prefWidth }
                    hbox {
                        children {
                            region { minWidth = 30.0 }
                            progressText = label("progress-label")
                        }
                    }
                }
            }
        }