{"id":13427923,"url":"https://github.com/edvin/tornadofx","last_synced_at":"2025-10-31T06:30:43.759Z","repository":{"id":42618299,"uuid":"49143977","full_name":"edvin/tornadofx","owner":"edvin","description":"Lightweight JavaFX Framework for Kotlin","archived":true,"fork":false,"pushed_at":"2023-04-13T05:12:47.000Z","size":4287,"stargazers_count":3676,"open_issues_count":197,"forks_count":271,"subscribers_count":98,"default_branch":"master","last_synced_at":"2025-02-15T13:49:37.820Z","etag":null,"topics":["desktop","javafx","kotlin","rcp","tornadofx","ui-kit"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edvin.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-01-06T15:38:14.000Z","updated_at":"2025-02-13T20:51:21.000Z","dependencies_parsed_at":"2024-01-06T22:42:00.348Z","dependency_job_id":null,"html_url":"https://github.com/edvin/tornadofx","commit_stats":null,"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvin%2Ftornadofx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvin%2Ftornadofx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvin%2Ftornadofx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edvin%2Ftornadofx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edvin","download_url":"https://codeload.github.com/edvin/tornadofx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":239132545,"owners_count":19587106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["desktop","javafx","kotlin","rcp","tornadofx","ui-kit"],"created_at":"2024-07-31T01:00:42.612Z","updated_at":"2025-10-31T06:30:43.375Z","avatar_url":"https://github.com/edvin.png","language":"Kotlin","funding_links":[],"categories":["Kotlin","Libraries","Community","Frameworks"],"sub_categories":["Other Languages"],"readme":"![TornadoFX Logo](graphics/tornado-fx-logo.png?raw=true \"TornadoFX\")\n# TornadoFX\n\nJavaFX Framework for Kotlin\n\n## (This project is no longer maintained)\n\n[![Travis CI](https://travis-ci.org/edvin/tornadofx.svg)](https://travis-ci.org/edvin/tornadofx)\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/no.tornado/tornadofx/badge.svg?cachebust)](https://search.maven.org/#search|ga|1|no.tornado.tornadofx)\n[![Apache License](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0)\n\n**Important: TornadoFX is not yet compatible with Java 9/10**\n\nOracle is intending to decouple JavaFX from the JDK. We will wait\nuntil the decoupled JavaFX is available and stable before upgrading TornadoFX to support\nit. As of now there is little value and significant effort involved in updating to JDK 9/10,\nwhile there will be an enormous value in updating to the decoupled version.\n\n## Features\n\n- Supports both MVC, MVP and their derivatives\n- Dependency injection\n- Type safe GUI builders\n- Type safe CSS builders\n- First class FXML support\n- Async task execution\n- EventBus with thread targeting\n- Hot reload of Views and Stylesheets\n- OSGi support\n- REST client with automatic JSON conversion\n- Zero config, no XML, no annotations\n\n## Important version note\n\nTornadoFX requires Kotlin 1.1.2 and jvmTarget 1.8. Make sure you update your IDE plugins (Kotlin + TornadoFX).\n\nAfter updating IntelliJ IDEA, make sure your Kotlin target version is 1.1 (Project Settings -\u003e Modules -\u003e Kotlin -\u003e Language Version / API Version)\n\nRemember to update your build system to configure the `jvmTarget` as well.\n\nFor Maven, you add the following configuration block to `kotlin-maven-plugin`:\n\n```xml\n\u003cconfiguration\u003e\n    \u003cjvmTarget\u003e1.8\u003c/jvmTarget\u003e\n\u003c/configuration\u003e\n```\n\nFor Gradle, it means configuring the `kotlinOptions` of the Kotlin compilation task:\n\n```gradle\ncompileKotlin {\n    kotlinOptions.jvmTarget= \"1.8\"\n}\n```\n\nFailing to do so will yield errors about the compiler not being able to inline certain calls.\n\nYou also need a full rebuild of your code after a version upgrade. If you run into trouble, try to clean caches and restart IDEA (File -\u003e Invalidate caches / Restart).\n \n## Getting started\n\n- [Screencasts](https://www.youtube.com/user/MrEdvinsyse)\n- [Guide](https://docs.tornadofx.io/) We are gradually migrating all information from the Wiki into the Guide\n- [KDocs](https://tornadofx.io/dokka/tornadofx/tornadofx/index.html)\n- [Wiki](https://github.com/edvin/tornadofx/wiki)\n- [Slack](https://kotlinlang.slack.com/messages/tornadofx/details/)\n- [Stack Overflow](http://stackoverflow.com/questions/ask?tags=tornadofx)\n- [Documentation](https://github.com/edvin/tornadofx/wiki/Documentation) \n- [IntelliJ IDEA Plugin](https://github.com/edvin/tornadofx-idea-plugin) \n- [Example Application](https://github.com/edvin/tornadofx-samples) \n- [Maven QuickStart Archetype](https://github.com/edvin/tornadofx-quickstart-archetype) \n- [Changelog](CHANGELOG.md)\n\n### Generate a quickstart application with Maven\n\n```bash\nmvn archetype:generate -DarchetypeGroupId=no.tornado \\\n  -DarchetypeArtifactId=tornadofx-quickstart-archetype \\\n  -DarchetypeVersion=1.7.20\n```\n\n### Add TornadoFX to your project\n\n#### Maven\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eno.tornado\u003c/groupId\u003e\n    \u003cartifactId\u003etornadofx\u003c/artifactId\u003e\n    \u003cversion\u003e1.7.20\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```groovy\nimplementation 'no.tornado:tornadofx:1.7.20'\n```\n\n### Snapshots are published to Sonatype\n\nConfigure your build environment to use snapshots if you want to try out the latest features:\n\n```xml\n \u003crepositories\u003e\n   \u003crepository\u003e\n     \u003cid\u003esnapshots-repo\u003c/id\u003e\n     \u003curl\u003ehttps://oss.sonatype.org/content/repositories/snapshots\u003c/url\u003e\n     \u003creleases\u003e\u003cenabled\u003efalse\u003c/enabled\u003e\u003c/releases\u003e\n     \u003csnapshots\u003e\u003cenabled\u003etrue\u003c/enabled\u003e\u003c/snapshots\u003e\n   \u003c/repository\u003e\n \u003c/repositories\u003e\n```\n\nSnapshots are published every day at GMT 16:00 if there has been any changes.\n\n### What does it look like? (Code snippets)\n\nCreate a View\n\n```kotlin\nclass HelloWorld : View() {\n    override val root = hbox {\n        label(\"Hello world\")\n    }\n}\n```\n\nStart your application and show the primary `View` and add a type safe stylesheet\n    \n```kotlin\nimport javafx.scene.text.FontWeight\nimport tornadofx.*\n\nclass HelloWorldApp : App(HelloWorld::class, Styles::class)\n\nclass Styles : Stylesheet() {\n    init {\n        label {\n            fontSize = 20.px\n            fontWeight = FontWeight.BOLD\n            backgroundColor += c(\"#cecece\")\n        }    \n    }    \n}\n```\n\u003e Start app and load a type safe stylesheet\n\nUse [Type Safe Builders](https://github.com/edvin/tornadofx/wiki/Type-Safe-Builders) to quickly create complex user interfaces\n\n```kotlin\nclass MyView : View() {\n    private val persons = FXCollections.observableArrayList(\n            Person(1, \"Samantha Stuart\", LocalDate.of(1981,12,4)),\n            Person(2, \"Tom Marks\", LocalDate.of(2001,1,23)),\n            Person(3, \"Stuart Gills\", LocalDate.of(1989,5,23)),\n            Person(3, \"Nicole Williams\", LocalDate.of(1998,8,11))\n    )\n\n    override val root = tableview(persons) {\n        column(\"ID\", Person::id)\n        column(\"Name\", Person::name)\n        column(\"Birthday\", Person::birthday)\n        column(\"Age\", Person::age)\n        columnResizePolicy = SmartResize.POLICY\n    }\n}\n```\n\n**RENDERED UI**\n\n![](https://i.imgur.com/AGMCP8S.png)\n\nCreate a Customer model object that can be converted to and from JSON and exposes both a JavaFX Property and getter/setter pairs:\n\n```kotlin\nimport tornadofx.getValue\nimport tornadofx.setValue\n\nclass Customer : JsonModel {\n    val idProperty = SimpleIntegerProperty()\n    var id by idProperty\n\n    val nameProperty = SimpleStringProperty()\n    var name by nameProperty\n\n    override fun updateModel(json: JsonObject) {\n        with(json) {\n            id = int(\"id\") ?: 0\n            name = string(\"name\")\n        }\n    }\n\n    override fun toJSON(json: JsonBuilder) {\n        with(json) {\n            add(\"id\", id)\n            add(\"name\", name)\n        }\n    }\n}\n```\n    \nCreate a controller which downloads a JSON list of customers with the REST api:\n\n```kotlin\nclass HelloWorldController : Controller() {\n    val api : Rest by inject()\n    \n    fun loadCustomers(): ObservableList\u003cCustomer\u003e = \n        api.get(\"customers\").list().toModel() \n}\n```\n    \nConfigure the REST API with a base URI and Basic Authentication:\n    \n```kotlin\nwith (api) {\n    baseURI = \"http://contoso.com/api\"\n    setBasicAuth(\"user\", \"secret\")\n}\n```\n    \nLoad customers in the background and update a TableView on the UI thread:\n\n```kotlin\nrunAsync {\n    controller.loadCustomers()\n} ui {\n    customerTable.items = it\n}\n```\n\nLoad customers and apply to table declaratively:\n\n```kotlin\ncustomerTable.asyncItems { controller.loadCustomers() }\n```\n\nDefine a type safe CSS stylesheet:\n\n```kotlin\nclass Styles : Stylesheet() {\n    companion object {\n        // Define css classes\n        val heading by cssclass()\n        \n        // Define colors\n        val mainColor = c(\"#bdbd22\")\n    }\n\n    init {\n        heading {\n            textFill = mainColor\n            fontSize = 20.px\n            fontWeight = BOLD\n        }\n        \n        button {\n            padding = box(10.px, 20.px)\n            fontWeight = BOLD\n        }\n\n        val flat = mixin {\n            backgroundInsets += box(0.px)\n            borderColor += box(Color.DARKGRAY)\n        }\n\n        s(button, textInput) {\n            +flat\n        }\n    }\n}\n```\n\nCreate an HBox with a Label and a TextField with type safe builders:\n\n```kotlin\nhbox {\n    label(\"Hello world\") {\n        addClass(heading)\n    }\n    \n    textfield {\n        promptText = \"Enter your name\"\n    }\n}\n```\n    \nGet and set per component configuration settings:\n    \n```kotlin\n// set prefWidth from setting or default to 200.0\nnode.prefWidth(config.double(\"width\", 200.0))\n\n// set username and age, then save\nwith (config) {\n    set(\"username\", \"john\")\n    set(\"age\", 30)\n    save()\n}\n```\n    \nCreate a `Fragment` instead of a `View`. A `Fragment` is not a `Singleton` like `View` is, so you will\ncreate a new instance and you can reuse the Fragment in multiple ui locations simultaneously.\n     \n```kotlin\nclass MyFragment : Fragment() {\n    override val root = hbox {\n    }\n}\n```\n     \nOpen it in a Modal Window:\n                   \n```kotlin\nfind\u003cMyFragment\u003e().openModal()\n``` \n         \nLookup and embed a `View` inside another `Pane` in one go\n           \n```kotlin\nadd\u003cMyFragment\u003e()\n```\n\nInject a `View` and embed inside another `Pane`\n       \n```kotlin\nval myView: MyView by inject()\n \ninit {\n    root.add(myFragment)\n}\n```\n\nSwap a View for another (change Scene root or embedded View)\n\n```kotlin\nbutton(\"Go to next page\") {\n    action {\n        replaceWith\u003cPageTwo\u003e(ViewTransition.Slide(0.3.seconds, Direction.LEFT)\n    }\n}\n```\n\nOpen a View in an internal window over the current scene graph\n\n```kotlin\nbutton(\"Open\") {\n    action {\n        openInternalWindow\u003cMyOtherView\u003e()\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedvin%2Ftornadofx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedvin%2Ftornadofx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedvin%2Ftornadofx/lists"}