[출처 - 이 글은 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 |