2017-06-22 87 views
1

我想在javatx組件中爲Kotlin提供構建器模式。該模式將類似於下面如何構建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不暴露在Child類中聲明的labelbutton方法。任何指針?

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

如果你想這些功能在範圍上,你應該讓你的'init'參數擴展'兒童'而不是'窗格'。 – zsmb13

+0

你應該在這裏有你需要的一切https://kotlinlang.org/docs/reference/type-safe-builders.html – Calin

回答

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 = [email protected] } 
        hbox { 
         children { 
          region { minWidth = 30.0 } 
          progressText = label("progress-label") 
         } 
        } 
       } 
      } 
     }