Skip to content
This repository has been archived by the owner on Sep 28, 2024. It is now read-only.

Type Safe CSS

Edvin Syse edited this page Apr 23, 2016 · 49 revisions

WikiDocumentationType Safe CSS

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.

Defining a style sheet

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
                }
            }
        }
    }
}

Importing the stylesheet

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()
    }
}

Applying style classes to components

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)
}

Default style classes

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 thecsspseudoclass` delegate.

Defining colors

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").

Dimensions

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)).

Box

Mixins

Modifier selections (+s)

Special support for SingleViewApp

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;
}

Selecting nodes with type safety

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

Clone this wiki locally