Codetown ::: a software developer's community
Michael Levin and I grabbed coffee a few weeks ago discussing what we might do with Orlando JUG (thanks to gmail for the offer for a TornadoFX workshop being shoved in my spam email for weeks). We started up a KotlinTown group in CodeTown for anyone interested in meeting other JUG members of all levels — introductory and experienced Java users alike!
One of the things I wanted to start doing with KotlinTown is Kotlin Tuesdays: we’re still figuring out a format — I’d like to get a chat successfully set up for Kotlin use only, but in the meantime, I’m posting both here and there and if you’re interested in joining the community, comment below or message us!
TornadoFX was created by Edvin Syse, who leveraged Kotlin to create a framework for JavaFX. TornadoFX was my first love with Kotlin, and I highly recommend checking out my other posts or even Github for other really cool projects people have done playing with TornadoFX. As a developer with extensive experience in large-scale web development, I definitely have JS fatigue with the amount of new frameworks that seem to come out every year. I’m not the only one either, Mike Hearn’s post on It’s Time to Kill the Web sounds more plausible by the day.
THAT ASIDES I am not here to argue web v. native development. TornadoFX is really fun and is a small framework (hence, short learning curve), and it’s a great way to start learning Kotlin without being bogged down by servers or mobile or web development. Ya’ll ready? Good. You’re not required to know JavaFX to get started with native application development, and I’ll be providing Kotlin and TornadoFX documentation below. Documentation for TornadoFX also has extensive examples with a convenient search function for you to browse around.
1. On IntelliJ, go to Preferences > Plugins and make sure Kotlin and TornadoFX is installed.
2. General consensus among TorandoFXers is that Maven is a lot easier to use — if you’re not familiar with Gradle v. Maven, don’t worry about it. It is not necessary to understand how to use either for this project, but don’t hesitate to ask for questions on setup if you run into a blocker. Once you have your plugins downloaded, open a new project and select the tornadofx-maven-plugin, which will mostly set up your project immediately.
3. It seems like the latest update for Kotlin didn’t pick up the latest running update for your Kotlin version (if you’re not sure what version you’re running, check your plugins for the latest i.e. 1.2.41). Go into your pom.xml file to edit the version and click “Import Changes” below. If it’s builds successfully, you’re good to go!
4. You’ll need to set up minimal configuration to run an instance of your application: luckily, the TornadoFX Maven plugin already did most of the painful setup for you. Go to Run > Edit Configurations, and add a new TornadoFX Configuration. Just name your config something meaningful and grab the MyApp class address, since that is already set up to launch the app. $MODULE_DIR$ detects the source of your project in your computer so you don’t have to dig for an address. Add the 1.8 JDK to the JRE and the name of your project to the classpath of the module, then hit Apply and OK. If you don’t have 1.8 JDK, you’ll need to download it from Oracle.
5. Press “Run” (or the green arrow) and you’ll see an instance of the skeleton application!Not too bad, right? The setup is always the worst part of a project.
While TornadoFX has a lot of great components made specifically for the framework like Datagrid or Datepicker, already pre-made for your ease of use, I want to create my own neighborhood with specific streets and houses and I want full control of what goes in each block, so I will use the JavaFX GridPane written with TornadoFX DSLs. You’ll notice that you already have an app and view folder in your project — you’ll want to add a controller and model folder as well for easy organization. If you’re not familiar with MVC, the structure is a general architectural pattern developers have come up with to work with large frameworks, all split to handle different aspects of the application.
What I had was house icons I downloaded from a free icon site — you’re welcome to use the ones in my project or find your own. Downloading free icons is great especially since I’m not a graphic designer myself. TornadoFX has all the features JavaFX has, if you want to know how to do something, look up Oracle JavaFX documentation/examples and transfer it to Kotlin! Here’s why I love Kotlin — in JavaFX, a gridpane is typically created like so:
class GridPaneExample {
/**
* @param args the command line arguments
*/
public static void main (String[ ] args) {
Application.launch(args);
}
@Override
public void start (Stage primaryStage) {
int numCol = 8;
GridPane pane = new GridPane( );
pane.setVgap(15);
pane.setPadding(new Insets(15, 15, 15, 15) );
// one row
for (int i = 0; i < numCol; i++) {
pane.add(horizontalStreetPane( ));
}
// show scene
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show( );
}
public StackPane horizontalStreetPane( ) {
Rectangle grass = new Rectangle(100.0, 100.0);
grass.setFill(Color.GREEN);
StackPane stack = new StackPane(grass);
Rectangle street = new Rectangle(100.0, 40.0);
street.setFill(Color.GRAY);
// adding additional elements overlays elements
stack.getChildren( ).add(street);
return stack;
}
A JavaFX rendering of GridPane with a single row
Given that I want to have crossing streets and houses placed in different parts of the grid, this code can grow redundant quickly. With TornadoFX and Kotlin, you can easily use DSLs to create an equivalent layout and easily add properties with apply to the parent. In Kotlin, you’ll notice that the code is a lot easier to read, and there are a couple syntactic things you see right away:
// TornadoFX has View, Controller, and Model components, allowing you to separate your code.
class MyApp: View( ) {
// global variables
val numCol : Int = 8;
// every view has a root component
override val root = gridpane {
vgap = 15.0
padding = insets( 15)
for (var i in 1..numCol) {
add(horitzontalStreetPane( ))
}
fun horizontalStreetPane( ): StackPane {
return stackpane {
rectangle {
fill = c("4E9830")
width = 100.0
height = 100.0
}
rectangle {
fill = c("919191")
width = 100.0
height = 40.0
}
}
}
A TornadoFX rendering of the above example.
That being said, I want to separate my individual components in the controller for function logic and create a quick and dirty version of my GridPane. You can test your results with the run every time. Below, are some more notible features for TornadoFX:
class NeighborhoodView: View( ) {
// dependency injection
val controller: NeighborhoodController by inject( )
// set up neighborhood
override val root = gridpane {
row {
add(controller.grassPane( ))
add(controller.grassPane( ))
add(controller.housePane(Pos.BASELINE_LEFT))
add(controller.verticalStreetPane( ))
add(controller.housePane(Pos.BASELINE_CENTER))
add(controller.verticalStreetPane( ))
add(controller.housePane(Pos.BASELINE_CENTER))
add(controller.grassPane( ))
}
// you can add a lot more rows below as you like.
// use this to check placement:
// isGridLinesVisible = true
}
}
class Styles: Stylesheet( ) {
companion object {
val main by cssclass( )
val neighborhood by cssclass( )
}
init {
main {
backgroundColor += c("222222")
}
neighborhood {
backgroundColor += c("4E9830")
prefWidth = 470.px
prefHeight = 590.px
}
}
}
}
// located in the controller package
class NeighborhoodController: Controller( ) {
fun housePane(position: Pos) : StackPane {
val houseNum = (1..6).random( )
return StackPane.apply {
rectangle {
fill = c("4E9830")
width = 100.0
height = 100.0
}
imageview("house$houseNum.png").apply {
alignment = position
}
}
fun grassPane( ) : StackPane {
return StackPane.apply {
rectangle {
fill = c("4E9830")
width = 100.0
height = 40.0
}
}
}
fun verticalStreetPane( ) : StackPane {
return StackPane.apply {
rectangle {
fill = c("4E9830")
width = 100.0
height = 100.0
}
rectangle {
fill = c("919191")
width = 100.0
height = 100.0
}
}
}
private fun ClosedRange.random( ) =Random( ).nextInt(endInclusive - start) + start
}
A TornadoFX rendering of the above example.
Build your neighborhood however you like:
One of the neat things about TornadoFX streamlines complex JavaFX layouts, including tabpane and tableview. The following is seriously crash course on some of these concepts, so I’m attaching documentation for the model creation and binding for the following result:
class MainView: View("Neighborhood Cat Scheduler" ) {
override val root = stackpane {
borderpane {
prefHeight = 700.0
prefWidth = 800.0
top(NeighborhoodView::class)
left(BottomView::class)
}
addClass(Styles.main)
}
}
// Kotlin data classes simplifies the way we hold data
data class CatSchedule(val ownerName: String? = null, val catName: String? = null,
val address: String? = null, val time: String? = null,val catImage: String? = null)
// allows for easy data binding for display and changes
class CatScheduleModel: ItemViewModel/span>CatSchedule>( ) {
val ownerName = bind { item?.ownerName?.toProperty( ) }
val catName = bind { item?.catName?.toProperty( ) }
val address = bind { item?.address?.toProperty( ) }
val time = bind { item?.time?.toProperty( ) }
val catImage = bind { item?.catImage?.toProperty( ) }
}
// modelview in displayed view
class BottomView: View( ) {
private val model: CatScheduleModel by inject( )
private val mondays = listOf(
CatSchedule("Tom Mariano", "Meowsers", "1110 6th Street", "3:00PM", "/kitty/kitty1.png"),
CatSchedule("John Hill", "Tom", "1115 6th Street", "4:00PM", "/kitty/kitty2.png"),
CatSchedule("Louise Vargas", "Mr. Whiskers", "1120 6th Street", "4:30PM", "/kitty/kitty3.png"),
CatSchedule("Dale Benton", "Pepper", "2111 8th Street", "7:00PM", "/kitty/kitty4.png"),
CatSchedule("Tucker Harrison", "Princess", "2267 8th Street", "8:00PM", "/kitty/kitty5.png")
).observable()
override val root = hbox {
tabpane {
tab("Monday") {
tableview(mondays) {
column("Owner", CatSchedule:ownerName)
column("Cat", CatSchedule:catName)
column("Address", CatSchedule:address).remainingWidth( )
column("Time", CatSchedule:time)
bindSelected(model)
smartResize( )
}
}
addClass(Styles.schedule)
}
stackpane {
rectangle {
width = 200.0
height = 200.0
fill = Color.TRANSPARENT
}
imageview(cat, true)
}
}
End result:
We have a lot of neat things going, but there’s a lot more we could do from here: for example, the cats avis might change per selection, and maybe we want to be able to edit our schedule. A friend of my recommended I make this canine-compatible as well: I’m inclined to agree since I’m more of a dog person myself. I’d like to be really ambitious and connect those actions with the houses as well — what are your ideas with the project?
I intend on setting up a chat on here - tried earlier last week but I'm hoping the endeavor will be successful later this week. Stay tuned!
Tags:
Sorry it took me a while - I promise I'll deliver the next part ON TIME y'all. Any questions?
Codetown is a social network. It's got blogs, forums, groups, personal pages and more! You might think of Codetown as a funky camper van with lots of compartments for your stuff and a great multimedia system, too! Best of all, Codetown has room for all of your friends.
Created by Michael Levin Dec 18, 2008 at 6:56pm. Last updated by Michael Levin May 4, 2018.
Check out the Codetown Jobs group.
Android XR is Google's new operating system aimed at powering devices like headsets and glasses and making possible new experiences, a.k.a. apps, running on them. Android XR will integrate Gemini, Google's AI assistant, to enable understanding user intent, defining a plan, guiding through tasks, and more.
By Sergio De SimoneOpenAI has launched the O3 and O3 Mini models, setting a new standard in AI with enhanced reasoning capabilities. Notable achievements include 71.7% accuracy on SWE-Bench and 96.7% on the AIME benchmark. While these models excel in coding and mathematics, challenges remain. O3 Mini offers scalable options for developers, prioritizing safety and adaptability.
By Andrew HoblitzellAlex Shopov discusses how CarGurus decided which framework would best support the needs of their front-end development teams and evolving application ecosystem.
By Alex ShopovAt Ignite 2024, Microsoft unveiled the Azure Boost DPU, its first in-house solution for low-power, data-centric workloads. This innovative chip optimizes cloud performance and security, offering triple the efficiency of CPUs. With a robust hardware-software design, Microsoft’s advancements position it to redefine AI and cloud infrastructure.
By Steef-Jan WiggersIn a recent article on its security blog, AWS detailed its plan for migrating to post-quantum cryptography (PQC). The article addresses the challenges posed by PQC, outlines AWS's current progress in the migration process, and explains the impact on customers within the traditional shared responsibility model.
By Renato Losio© 2024 Created by Michael Levin. Powered by