New Zircon Release: 2021.1.0-RELEASEE

  Written by:

Zircon is an extensible, multiplatform and user-friendly tile engine.

If you are not yet familiar with the project take a look at our Project Page.

The Javadoc / Kdoc can be found here.

You can grab this release from Maven Central here. More on how to add Zircon as a dependency to your project can be found here.

Also if you have any questions about this release, feel free to come up to our Discord Server and ask them!

TL;DR For the Impatient:

  • New fragments are added (Table, HorizontalTabBar, VerticalTabBar, DropDownMenu)
  • A full-blown Kotlin DSL is added for components and fragments
  • Getters received an upgrade (getOrNull, getOrElse)
  • Component builders now auto-calculate size
  • You can create custom tileset loaders now
  • No global state: now you can use Zircon in renderer-only or tile grid only mode

Highlights of this release

The main focus of this release was usability and extensibility:

On the usability front, we’ve deprecated Maybe, and started adding two accessor variants instead:

  • getOrNull: this will either return an item you’re looking for or a null if it is not present
  • getOrElse: Just like getOrNull but you can provide a supplier for the “else” object (this will never return null)
  • We’re also going to add a simple get that will throw an exception instead of returning null if the value you’re looking for is not present

More on usability: we added a Kotlin dsl for all components and fragments, so now it is easier than ever to create declarative UIs with Zircon:

SwingApplications.startTileGrid().toScreen().apply {
    theme = ColorThemes.adriftInDreams()
}.useComponentBuilder {
    val screenSize = size
    vbox {
        preferredSize = screenSize
        decoration = box(title = "DSL Example")
        header { +"This is a simple example" }
        hbox {
            vbox {
                decoration = box()
                label { +"Just a label" }
            vbox {
                decoration = box()
                paragraph {
                    preferredContentSize = Size.create(20, 5)
                    +"This will take a lot more space, but we constrain its size with the preferred content size"
            vbox {
                decoration = box()
                paragraph {
                    preferredContentSize = Size.create(10, 20)
                    +"As you can see sizes are automatically calculated when you don't supply parameters explicitly. Text is also wrapped by default."

And it looks like this:


One important change that was a blocker for some other things was the reliance on the global Zircon object that also held some state. With #404 completed now there is no global state, and it is also possible to have TileGrid and / or Renderer objects without having an Application that continuously renders content.

What this means is that now it is possible to embed Zircon into other applications that don’t use tile grids at all or to bridge Zircon with different component systems. All you’ll need is to pass the relevant input (keyboard/mouse) events!

Another use case that was requested for a long time is to create tile / ASCII art with Zircon without having to deal with an Application object (eg: using just a Renderer). Now it is finally possible!

There is another improvement that helps with extensibility: now it is possible to create custom tileset loaders. There is already a WIP loader that uses Tiled’s format.

There was some confusion previously on how Zircon objects should be created. Now this is made explicit by this issue. From now on the appropriate builders should be used and all builders have their constructors locked down, so usage is like this for all the things: Thing.newBuilder() ... .build().

Some notable Fragments:

The Table fragment is now released, usage is very simple:

data class Person(
    val name: String,
    val age: Int,
    val occupation: String

val persons = listOf(
        name = "Joe",
        age = 28,
        occupation = "programmer"
    ), Person(
        name = "Jane",
        age = 30,
        occupation = "UX Expert"
    ), Person(
        name = "Tanya",
        age = 26,
        occupation = "Commando"

SwingApplications.startTileGrid().toScreen().apply {
}.useComponentBuilder {
    table<Person> {
        data = persons
        textColumn {
            name = "Name"
            width = 10
            valueProvider = Person::name
        numberColumn {
            name = "Age"
            width = 4
            valueProvider = Person::age
        textColumn {
            name = "Occupation"
            width = 15
            valueProvider = Person::occupation

And it looks like this:

Now it is also possible to create Dropdown Menus:

and Tab bars:


You can now also set up listeners right in the component builders and now the component builders are also more smart about sizing: they will calculate the size that’s necessary to display the component, but you can now also set either the peferred content size or the preferred size. The same stands for containers! This means that you can create a HBox with all of its children, and the occupied space for the HBox will be calculated for you.

Here is a full list of issues we finished for this release:

New Features

  • #340: Make Tilesets and Modifiers configurable from the AppConfig class
  • #404: Add the ability to create a TileGrid and a Renderer without an Application
  • #397: ScrollableList for Zircon
  • #185: Table control for Zircon
  • #135: Introduction of menus in Zircon
  • #395: Re-add clear to Container to enable removing all children at once without having to use the AttachedComponent reference
  • #394: Finalize Tab bars
  • #393: Add the option to set properties and listeners to ComponentBuilders
  • #104: Add function to BaseComponentBuilder for defining a content-based size
  • #364: Add Zircon Builder Dsl
  • #391: Implement component autosizing
  • #386: Custom Tileset Loader Support
  • #388: Add orNull alternatives to operations that return a Maybe
  • #380: AppConfig extensible custom properties API


  • #412: Add orElse variants of functions to objects with accessors
  • #326: Move the contents of the Zircon object into Application
  • #382: All classes that have builders should lock down their constructors
  • #19: Implement box connecting in Box
  • #18: Box connecting should be handled when the area surrounding the box changes
  • #399: Add remainingSpace property ty HBox and VBox
  • #396: Restore the original position on attachment failure or component reset
  • #309: borderless window / simplified fullscreen
  • #331: Box Decoration Title Alignment

Bug fixes

  • #409: Cursor is not displayed properly
  • #410: Removing a non-last item from a HBox / VBox throws an exception
  • #407: Some of the GameArea examples are misaligned
  • #406: Transforming a Layer to contain transparent tiles will make it black instead
  • #405: The Delayed TileString example is not working
  • #398: Data binding loop when updating UNKNOWN tilesets
  • #365: ComponentBuilder constructor parameter
  • #367: Headers don’t wrap on word
  • #376: BaseGameArea sticks in memory after undocking

Road Map

We’ve covered a lot of ground in this release, but there are still things to do:


Thank you for all of you out there who helped with this release! Special thanks to Baret, Abbi @lesbiangunshow, and Max @nanodeath for their contributions!


If you think that you can contribute or just have an idea feel free to join the discussion on our Discord server.

Do you have questions? Ask us on our Discord Server.

If you like what we do and want to support us consider becoming a Patron.