-
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:
class MyApp : App() {
override val primaryView = MyView::class
init {
importStylesheet(Styles::class)
}
}
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.
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:
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;
}
Next: Async Task Execution