-
Notifications
You must be signed in to change notification settings - Fork 271
Type Safe CSS
Wiki ▸ Documentation ▸ Type Safe CSS
TornadoFX has a type safe DSL for generating CSS, including type safe selector declarations. Using it is optional, you can always write your stylesheets manually, but you'll find there are many advantages to using the DSL. A couple of them are discoverability, powerful refactoring capabilities and getting the syntax right the first time. Others are support for mixins
and the ability to generate CSS with code, even based on for example state or configuration in your app.
class Styles : Stylesheet() {
companion object {
// Define our styles
val wrapper by cssclass()
val bob by cssclass()
val alice by cssclass()
// Define our colors
val dangerColor = c("#a94442")
val hoverColor = c("#d49942")
}
init {
s(wrapper) {
padding = box(10.px)
spacing = 10.px
}
s(label) {
fontSize = 56.px
padding = box(5.px, 10.px)
maxWidth = infinity
+s(bob, alice) {
borderColor = box(dangerColor)
borderStyle = BorderStrokeStyle(StrokeType.INSIDE, StrokeLineJoin.MITER, StrokeLineCap.BUTT, 10.0, 0.0, listOf(25.0, 5.0))
borderWidth = box(5.px)
+s(hover) {
backgroundColor = hoverColor
}
}
}
}
}
To use a particular stylesheet, import it in the init
block of your app class. Optionally, tell TornadoFX to reload your stylesheet whenever the app gets focus, so you can make hot changes and recompile without restarting your app.
class MyApp : App() {
override val primaryView = MyView::class
init {
importStylesheet(Styles::class)
reloadStylesheetsOnFocus()
}
}
You can choose to use the type safe selectors shown above, or use strings, both for defining selectors and adding classes to nodes. It is a best pratice to use type safe selectors everywhere, so that you can track where/if your css is defined and applied.
Describe addClass
, hasClass
, toggleClass
both with strings and CSSClasses.
It is also possible to manipulate classes of multiple components at once. Any List can be manipulated by addClass
, removeClass
and toggleClass
. Example:
hbox {
// Build a very compicated UI here
// Apply the class 'wrapper' to all children of type HBox
children.filter { it is HBox }.addClass(wrapper)
}
The Stylesheet
class defines constants for all pseudo classes and node classes used in all the default JavaFX components, to there is no need to define classes like hover
, label
, button
and listView
.
You can also define #id
with the cssid
delegate and ':pseudoclasseswith the
csspseudoclass` delegate.
Colors are defined in the companion object of your stylesheet. All colors are of type Paint
but there are convenience functions to create colors from strings eg. c("#a94442")
.
Some sort of explanation that all measurements are type safe as well using units. (There is support for linear units (px, %, mm, pt, em, infinity, etc.), angular units (deg, rad, grad, and turn), and temporal units (s, ms)).
For small/demo applications, you can define and apply a stylesheet directly inside the app class. Note that the automatic reload function is not compatible with inline stylesheets.
class CssDemo : SingleViewApp("CSS Demo") {
override val root = VBox()
companion object {
val wrapper by cssclass()
val alice by cssclass()
val bob by cssclass()
}
init {
with(root) {
addClass(wrapper)
label("Alice") {
addClass(alice)
}
label("Bob") {
addClass(bob)
}
}
css {
s(wrapper) {
padding = box(10.px)
spacing = 10.px
}
s(label) {
fontSize = 56.px
padding = box(5.px, 10.px)
maxWidth = infinity
+s(bob, alice) {
borderColor = box(Color.ORANGE)
borderStyle = BorderStrokeStyle(StrokeType.INSIDE, StrokeLineJoin.MITER, StrokeLineCap.BUTT, 10.0, 0.0, listOf(25.0, 5.0))
borderWidth = box(5.px)
}
}
}
}
}
The application will look like this:
The CSS can be printed using println(stylesheet)
and looks like this:
.box {
-fx-padding: 10px 10px 10px 10px;
-fx-spacing: 10px;
}
.label {
-fx-font-size: 56px;
-fx-padding: 5px 10px 5px 10px;
-fx-max-width: infinity;
}
.label.bob, .label.alice {
-fx-border-color: rgba(255, 165, 0, 1) rgba(255, 165, 0, 1) rgba(255, 165, 0, 1) rgba(255, 165, 0, 1);
-fx-border-style: segments(25.0 5.0) inside line-join miter 10.0 line-cap butt;
-fx-border-width: 5px 5px 5px 5px;
}
When you have applied your style classes to your nodes, you can use select
and selectAll
to retrieve the nodes based on their classes:
val wrapper = root.select(wrapper)
val hboxes = root.selectAll(hbox)
Remember that the hbox
class is not added to HBoxes by default, so you would have to add it manually.
Next: Async Task Execution