v17 Introduction Developer at SYSE Norwegian hosting company Project lead for TornadoFX and FXLauncher Why TornadoFX JavaFX is a UI toolkit not a framework JavaFX is powerful but verbose ID: 683197
Download Presentation The PPT/PDF document "Super charged JavaFX with Kotlin and To..." is the property of its rightful owner. Permission is granted to download and print the materials on this web site for personal, non-commercial use only, and to display it on your personal computer provided you do not modify the materials and that you retain all copyright notices contained in the materials. By downloading content from our website, you accept the terms of this agreement.
Slide1
Super charged JavaFX
with Kotlin and TornadoFX
v1.7Slide2IntroductionDeveloper at SYSE – Norwegian hosting company
Project lead for TornadoFX and FXLauncherSlide3Why TornadoFX?
JavaFX is a UI toolkit, not a frameworkJavaFX is powerful, but verboseKotlin presents some unique opportunities for APIsSlide4Statically typed JVM language from JetBrainsMore concise syntax, less noise from types/declarations
Specific DSL language constructsSlide5GoalsReduce boilerplate, but never hide JavaFX APIs
Add commonly needed componentsWizard, Forms, DataGrid, ViewModel, Validation, ListMenu, Drawer, Workspace ++Improve existing componentsData Driven TreeView, TableView
Row Expansion, Auto Complete ComboBoxBeautiful, understandable, refactorable code!Slide6Key Features
Type Safe BuildersType Safe CSSWorkspace Business Framework (RCP)MVC / MVP / MVVM and similar UI patternsDependency InjectionEventBusLive reloading of Views and CSSREST Client and JSONSlide7
Code comparisonJavaFX: Create a TableView
with two columnsTableView<Customer> table = new TableView<>(customers);TableColumn<Customer, String> firstNameColumn = new TableColumn<>(
"First Name"
);firstNameColumn.setCellValueFactory(param -> param.getValue().firstNameProperty());TableColumn<Customer, String> lastNameColumn = new TableColumn<>("Last Name");lastNameColumn.setCellValueFactory(param -> param.getValue().lastNameProperty());table.getColumns
().addAll(
firstNameColumn, lastNameColumn
);Slide8
tableview
(customers) { column("First Name", Customer::firstNameProperty) column("Last Name", Customer::lastNameProperty)
}
Code comparisonTornadoFX: Create a TableView with two columnsSlide9Code comparison
TableColumn
<Customer, Number> ageColumn = new TableColumn<>("Age");ageColumn.setCellValueFactory(param -> param.getValue().ageProperty());ageColumn.setCellFactory(col -> new TableCell<Customer, Number>() { public void updateItem(Number age,
boolean empty) {
super.updateItem(age, empty); setText(null); setGraphic(null); if (!empty && age != null) { setText(age.toString()); setStyle("-fx-text-fill: " + (age.intValue() < 18 ? "red" : "black")); } }});JavaFX: Custom
TableCellSlide10Code comparison
column
("Age", Customer::ageProperty).cellFormat { -> age text = "$age" style { textFill = if (age.toInt() < 18) RED else BLACK }}
TornadoFX: Custom
TableCellSlide11
TornadoFX Building BlocksSlide12Type Safe Builders
Construct hierarchy of NodesCommon properties as paramsFunction block operates on the created Node
borderpane { top { toolbar { button("Refresh").action { onRefresh
()
} } }}Slide13View
Container for the root Node
Contains UI logicOptionally delegates business logic to Controller or ViewModelViews are Singletons within a Scope - Fragments are notCan be embedded in other components or Views, or be the scene rootHot reloading during developmentclass MyView : View() {
override val
root = vbox { button("Click me").action { information("You clicked me!") } }}Slide14Controller
Contains shared state and business logicInject into any other ComponentSingletons within the current Scope
class CustomerController : Controller() { val api: Rest by inject() fun listCustomers(): ObservableList<Customer> = api.get("customers"
).list().toModel()
}Slide15Model
class Person { val
firstNameProperty = SimpleStringProperty() var firstName by firstNameProperty val lastNameProperty = SimpleStringProperty() var lastName by lastNameProperty}Slide16ViewModel
Mediator between View and ModelRollback / CommitValidation / DecorationCan contain business logicItemViewModel
class PersonModel : ItemViewModel<Person>() { val firstName = bind(Person::firstNameProperty) val lastName = bind(Person::lastNameProperty)}Slide17Scopes
Enables separation for JPro appsPerfect for MDIComponents are unique only within the scopeEasy grouping of UI and stateEnables testing Slide18
Code ExamplesSlide19Forms
class
LoginForm : View("Login") { val user = UserModel() override val root = form { fieldset(title, labelPosition = VERTICAL) { field("Username"
) {
textfield(user.username).required() } field("Password") { passwordfield(user.password).required() } button("Log in") {
enableWhen(user
.valid)
action(
user
::
login)
}
}
}
}Slide20FXML
Views can load the root Node from FXMLThe View becomes the Controller
class MyView : View() { val person: PersonModel by inject() override val root: ToolBar by fxml() val name: TextField
by fxid
() init { name.bind(person.name) } fun save() = person.save()}<ToolBar xmlns
="http://javafx.com/javafx"
xmlns:
fx
="http://javafx.com/
fxml
"
>
<
Label
text
="Name:"
/>
<
TextField
fx
:id
="name"
/>
<
Button
text
="Save"
onAction
="#save"
/>
</
ToolBar
>
class
MyView
: View() {
val
person
:
PersonModel
by
inject()
override val
root
=
toolbar
{
label
(
"Name:"
)
textfield
(
person
.
name
)
button
(
“Save"
).
action(
person
::
save)
}
}Slide21Type Safe CSS
Inspired by type safe UI buildersExtremely refactorableCovers 95% of the JavaFX CSS supportVariables and even functionsColor.derive/ladderCSS hot reload
val mainColor = c("#bdbd22") val heading by cssclass() heading { textFill = mainColor
fontSize
= 20.px fontWeight = BOLD} button { padding = box(10.px, 20.px) borderColor += box(Color.DARKGRAY) }
label
(
"Hello TornadoFX"
).
addClass
(
heading
)Slide22Charts
piechart
("Imported Fruits") { data("Grapefruit", 12.0) data("Oranges", 25.0) data("Plums", 10.0)
data(
"Pears", 22.0) data("Apples", 30.0)} barchart("Stock Monitoring, 2010", CategoryAxis(), NumberAxis()) { series("Portfolio 1") { data
("Jan",
23)
data
(
"Feb"
,
14
)
data
(
"Mar"
,
15
)
}
series(
"Portfolio 2"
) {
data
(
"Jan"
,
11
)
data
(
"Feb"
,
19
)
data
(
"Mar"
,
27
)
}
}
Slide23OSGiSlide24
Turns TornadoFX into an RCP platform
Browser like navigation history (optional tabbed navigation)
Common action buttons connected to currently docked View
Optional drawers on all sides
Views can provide dynamic or static additions to the Workspace
WorkspaceSlide25IntelliJ IDEA PluginGenerate code
Run configurationsRun Views directlyHot reload of Views and/or CSSIntentionsCustom editors (Ex Type Safe CSS color picker)Slide26TornadoFX Community and Resources
GitHub tornadofx.io/githubSlack tornadofx.io/slackGuide tornadofx.io/guide
YouTube tornadofx.io/screencasts