본문 바로가기
2023년 이전/kotlin-TornadoFx

TornadoFx - Layouts and Menus (1)

by JeongUPark 2019. 12. 9.
반응형

[출처 - 이 글은 tornadofx-guide르 통해 공부한 내용을 정리한 글입니다. 더 정확한 내용은 여기 에서 확인 하실 수 있습니다.]

 

오랜만에 TornadoFx관련 공부를 하는 것 같습니다.  사용법을 다 까먹어서 적었던 내용들을 다시 한번 흝어 보고 이번 절을 작성하고 있습니다. (공부만 하고 먼가 만들어보질 않아서 더 기억을 못하는것 같아 문서의 Part1 부분을 다 보고나면 한번 프로젝트성으로 먼가를 만들어봐야 할 것 같습니다.)

 

이번 절에서는 TornadoFx에서 제공하는 Layout들과 Menu들에 대해서 공부해봤습니다.

 

TornadoFx는 JavaFx와 함께 제공하는 기능들을 더 간결하게 제공하면 자체 form Layout도 제공하고 있습니다.

vbox

vBox 스택은 블록 내에 선언 된 순서대로 컨트롤을 수직으로 제어합니다

import tornadofx.*

class My_TEST : View() {
    override val root = vbox {
        button("Press Me").setOnAction {
            println("Button 1 Pressed")
        }
        button("Press Me 2").setOnAction {
            println("Button 2 Pressed")
        }
    }
}
class My_App : App(My_TEST::class){
}

fun main(){
    launch<My_App>()
}

이렇게 Press Me를 누르면 Button 1 Pressed가 Press Me 2를 누르면 Button 2 Pressed가 나타납니다. 즉 button.setOnAction을 하면 button을 눌렸을 때 동작이 수행됩니다.

그리고 하위 블록 내에서 vboxConstraints ()를 호출하여 VBox의 여백 및 수직 증가 동작을 변경할 수도 있습니다.

import javafx.scene.layout.Priority
import tornadofx.*

class My_TEST : View() {
    override val root = vbox {
        button("Press Me") {
            vboxConstraints {
                marginBottom = 20.0
                vGrow = Priority.ALWAYS
            }
        }
        button("Press Me 2")
    }
}
class My_App : App(My_TEST::class){
}
fun main(){
    launch<My_App>()
}

이렇게 button 1의 bottom margin을 20으로 설정하여 button 2개 사이의 간격을 줄 수 있습니다. 그리고 여기서 사용된 vGrow는 수직 빈 공간 채우기 설정으로 더 자세한 내용은 여기서 확인하시면 좋을 것 같습니다.

그리고 vboxConstrains 없이 vGrow에 대한 단축 확장 속성을 설정할 수 있습니다.

import javafx.scene.layout.Priority
import tornadofx.*

class My_TEST : View() {
    override val root = vbox {
        button("Press Me") {
            vgrow = Priority.ALWAYS
        }
        button("Press Me 2")
    }
}
class My_App : App(My_TEST::class){
}
fun main(){
    launch<My_App>()
}

사실 지금 code에서는 vGrow의 효과를 확인 할 수 없습니다.

 HBox

HBox는 vBox와 거의 동일하게 동작하지만 블록에 선언 된 순서대로 모든 컨트롤을 왼쪽에서 오른쪽으로 가로로 쌓습니다.

import javafx.scene.layout.Priority
import tornadofx.*

class My_hbox_TEST : View() {
    override val root = hbox {
        button("Button 1").setOnAction {
            println("Button 1 Pressed")
        }
        button("Button 2").setOnAction {
            println("Button 2 Pressed")
        }
    }
}
class My_hbox_App : App(My_hbox_TEST::class){
}
fun main(){
    launch<My_hbox_App>()
}

hbox 역시 hboxconstraints ()를 호출하여 HBox의 여백 및 수평 증가 동작을 변경할 수 있습니다.

import javafx.scene.layout.Priority
import tornadofx.*

class My_hbox_TEST : View() {
    override val root = hbox {
        button("Button 1") {
            hboxConstraints {
                marginRight = 20.0
                hGrow = Priority.ALWAYS
            }
        }
        button("Button 2")
    }
}


class My_hbox_App : App(My_hbox_TEST::class){
}

fun main(){
    launch<My_hbox_App>()
}

그리고 hbox 역시  hboxconstraints 없이 hGrow에 대한 단축 확장 속성을 설정할 수 있습니다.

import javafx.scene.layout.Priority
import tornadofx.*

class My_hbox_TEST : View() {
    override val root = hbox {
        button("Button 1") {
            hgrow = Priority.ALWAYS
        }
        button("Button 2")
    }
}


class My_hbox_App : App(My_hbox_TEST::class){
}

fun main(){
    launch<My_hbox_App>()
}

FlowPane

FlowPane은 컨트롤을 왼쪽에서 오른쪽으로 배치하고 경계의 다음 줄로 줄 바꿈합니다. 즉 왼쪽에서 오른쪽으로 배치를 하다 공간이 모자르면 다음줄로 변경됩니다. 예제를 보면

import javafx.scene.layout.Priority
import tornadofx.*

class flow_pane_TEST : View() {
    override val root = flowpane {
        for (i in 1..100) {
            button(i.toString()) {
                setOnAction { println("You pressed button $i") }
            }
        }
    }
}
class flow_pane_App : App(flow_pane_TEST::class){
}

fun main(){
    launch<flow_pane_App>()
}

총 100개의 버튼을 만드는데 공간에 맞추어 버튼을 왼쪽에서 오른쪽으로 나열하다 공간이 부족하면 다음줄에서 버튼을 계속 나열 합니다. 창 크기를 조절하면 그 사이즈에 맞게 버튼이 나열 됩니다.

Flwopane은 많은 수의 컨트롤을 처리하기에는 단순하기 때문에 자주 씌이지는 않지만 그래도 특정 상황에서 편리하게 쓰일 수 있습니다.

BorderPane

BorderPane은 이미 한번 간단하게 다뤄본 적이있습니다. BorderPane은 컨트롤을 top, left, bottom, right 및 center의 5 개 영역으로 나누는 매우 유용한 레이아웃입니다.

import javafx.scene.layout.Priority
import javafx.scene.paint.Color
import tornadofx.*

class border_pane_TEST : View() {
    override val root = borderpane {
        top = label("TOP") {
            useMaxWidth = true
            style {
                backgroundColor += Color.RED
            }
        }
        bottom = label("BOTTOM") {
            useMaxWidth = true
            style {
                backgroundColor += Color.BLUE
            }
        }
        left = label("LEFT") {
            useMaxWidth = true
            style {
                backgroundColor += Color.GREEN
            }
        }
        right = label("RIGHT") {
            useMaxWidth = true
            style {
                backgroundColor += Color.PURPLE
            }
        }
        center = label("CENTER") {
            useMaxWidth = true
            style {
                backgroundColor += Color.YELLOW
            }
        }
    }
}
class border_pane_App : App(border_pane_TEST::class){
}
fun main(){
    launch<border_pane_App>()
}

위의 code 처럼 top/bottom/left/right/center로 나누면 다음과 같은 결과를 만들 수 있습니다.

top및 bottom영역은 전체 수평 공간을 차지하지만 left, center및 right은 사용 가능한 수평 공간을 공유해야합니다. 그러나 center는 사용 가능한 추가 공간 (수직 및 수평)을 사용할 수 있으므로 TableView와 같은 대규모 컨트롤을 보유하는 것이 이상적입니다.예를 들어 다음과 같이 할 수 있습니다.

import tornadofx.*

class border_pane_TEST : View() {
    override val root = borderpane {
        left = vbox {
            button("REFRESH")
            button("COMMIT")
        }

        center  = tableview<Person> {
            items = listOf(
                Person("Joe Thompson", 33),
                Person("Sam Smith", 29),
                Person("Nancy Reams", 41)
            ).observable()

            column("NAME",Person::name)
            column("AGE",Person::age)
        }
    }
}
class border_pane_App : App(border_pane_TEST::class){
}
fun main(){
    launch<border_pane_App>()
}
data class Person(var name: String, var age:Int)

위의 code와 결과를 보시면 center에 tabeleview를 사용했고, left에 button을 나열 했습니다. 이 처럼 left와 center가 수평공간을 공유하여 채워진 것을 알 수 있습니다.

BorderPane은 복잡한 UI를 단순화하기 때문에 자주 사용하는 레이아웃입니다. 위쪽 영역은 일반적으로 MenuBar를 유지하는 데 사용되며 bottom 영역에는 종종 일종의 상태 표시 줄이 있습니다. 

Form Builder

TornadoFX에는 많은 수의 사용자 입력을 처리하는 데 유용한 Form 컨트롤이 있습니다. 사용자 정보를 얻기 위해 여러 입력 필드를 갖는 것이 일반적이며 JavaFX에는 이를 간소화 할 수있는 기본 제공 솔루션이 없습니다. 이를 해결하기 위해 TornadoFX에는 여러 필드를 가진 Form을 선언하는 빌더가 있습니다

import tornadofx.*

class form_TEST : View() {
    override val root =form {
        fieldset("Personal Info") {
            field("First Name") {
                textfield()
            }
            field("Last Name") {
                textfield()
            }
            field("Birthday") {
                datepicker()
            }
        }
        fieldset("Contact") {
            field("Phone") {
                textfield()
            }
            field("Email") {
                textfield()
            }
        }
        button("Commit") {
            action { println("Wrote to database!")}
        }
    }
}
class form_App : App(form_TEST::class){
}
fun main(){
    launch<form_App>()
}

각 필드에 대해 하나 이상의 컨트롤을 지정할 수 있으며 Form이 그룹화 및 레이블을 렌더링합니다. 입력 위에 레이블을 배치하도록 선택할 수도 있습니다.

//...
fieldset("Personal Info", labelPosition = Orientation.VERTICAL)
//....
fieldset("Contact", labelPosition = Orientation.VERTICAL)
//...

처리를 하면 

위의 결과 처럼 입력 위에 레이블이 배치 됩니다.

각 filed는 레이블이있는 컨테이너와 그 안에 추가 한 입력 필드에 대한 다른 컨테이너를 나타냅니다. 입력 컨테이너는 기본적으로 HBox이므로 단일 필드 내의 여러 입력이 나란히 배치됩니다. 여러 개의 입력을 서로 아래에 배치하도록 필드에 orientation 매개 변수를 지정할 수 있습니다. 세로 방향의 또 다른 사용 사례는 양식이 세로로 확장 될 때 입력이 커지도록하는 것입니다. 이것은 양식에 TextArea를 표시하는 데 편리합니다.

import javafx.geometry.Orientation
import javafx.scene.layout.Priority
import tornadofx.*

class form_TEST : View() {
    override val root = form {
        fieldset("Feedback Form", labelPosition = Orientation.VERTICAL) {
            field("Comment", Orientation.VERTICAL) {
                textarea {
                    prefRowCount = 5
                    vgrow = Priority.ALWAYS
                }
            }
            buttonbar {
                button("Send")
            }
        }
    }
}
class form_App : App(form_TEST::class){
}
fun main(){
    launch<form_App>()
}

위의 예제는 또한 buttonbar builder를 사용하여 레이블을 들여 쓰지 않고 레이블이없는 특수 필드를 작성하여 buttom이 입력 아래에 정렬되도록합니다. 각 입력을 모델에 바인딩하고 컨트롤 레이아웃의 렌더링을 양식에 남겨 둘 수 있습니다. 

반응형

'2023년 이전 > kotlin-TornadoFx' 카테고리의 다른 글

TornadoFx - Layouts and Menus (3)  (0) 2019.12.10
TornadoFx - Layouts and Menus (2)  (0) 2019.12.09
TornadoFx - Type-Safe CSS(1)  (0) 2019.10.31
TornadoFX - Exception 문제  (0) 2019.10.11
TornadoFx - Data Controls(2)  (0) 2019.10.11