[{"content":" What is Snapcast? # I cannot do better than the description from the Snapcast GitHub page:\nSnapcast is a multiroom client-server audio player, where all clients are time synchronized with the server to play perfectly synced audio. It\u0026rsquo;s not a standalone player, but an extension that turns your existing audio player into a Sonos-like multiroom solution.\nAudio is captured by the server and routed to the connected clients. Several players can feed audio to the server in parallel and clients can be grouped to play the same audio stream.\nThere\u0026rsquo;s also a diagram which shows how Snapcast works:\nTo sum it up: You set up a server with some sort of music playing software, then install the Snapcast server software and configure it such that it takes its input from the output of the music playing software. Then you set up Snapcast clients, and configure them to connect to the Snapcast server. After that, you can stream the music from the music player software on the server to all of the Snapclast clients and they will be synchronized.\nIn terms of audio quality, I find it to be excellent. I think the concept of \u0026ldquo;whole-home audio\u0026rdquo;, by definition, isn\u0026rsquo;t expected to be audiophile quality. It\u0026rsquo;s designed to be background noise. But I\u0026rsquo;ve found this setup to go far beyond that basic requirement.\nSnapcast Server # As this article is part of a series about setting up a Homelab using Proxmox, this is the approach I will be describing.\nThe requirements for the Snapcast server are fairly light, so a standard LXC container running Debian is more than sufficient. I\u0026rsquo;m running it with 1 CPU and 512MB of RAM and it\u0026rsquo;s usage is averaging about 1.5% of CPU and about 128MB of RAM. That\u0026rsquo;s on a Lenovo M910Q with an i5-6500T processor, which is not particularly high powered.\nIt\u0026rsquo;s perfectly possible that you could run this a Raspberry Pi of some sort, or anything else you have lying around. The instructions would be largely the same, as long as you\u0026rsquo;re running Linux.\nSnapcast Clients # The Snapcast client software will run on Linux, Windows, Android, IOS, and MacOS - pretty much any computing device you might have.\nThe big caveat is that to be of any use, the device needs to be able to play sound in a way that you can hear it. This is also going to mean that the device will need to have some way to convert the digital stream into audio output. This conversion is handled by something called a \u0026ldquo;DAC\u0026rdquo;, or \u0026ldquo;Digital to Analog Converter\u0026rdquo;. Some devices have them built in, but may also benefit from using a dedicated DAC device of some sort. The output from a DAC will be in a form that is compatible with headphones or an amplifier of some sort.\nSince it runs Linux, a RaspberryPi is an excellent, low-cost, Snapcast client, and this is what I\u0026rsquo;m going to talk about in this article. Specifically, I\u0026rsquo;m going to use RaspberryPi Zeros.\nYou can also re-purpose an old android phone or an old iPhone to be a SnapCast client. It should probably have a headphone jack, though, as using Bluetooth to communicate with the speakers will have its own lag. It does seem possible, however, to compensate for that in the setup of the SnapCast client. I\u0026rsquo;m not sure how consistent the lag from Bluetooth is, though, so you\u0026rsquo;d need to experiment to see if it works.\nRaspberryPi Zero # The RaspberryPi Zero (Pi0) is tiny, super cheap (around $20), has built-in WiFi and is easy to get. They don\u0026rsquo;t, however, have any built-in audio output. In order to use them for this application, you\u0026rsquo;ll need to use a external DAC. I recommend getting one that works with the header pins on the Pi0.\nThey look like this:\nApparently HiFiBerry makes a great DAC designed to work with the Pi0. Unfortunately, I wasn\u0026rsquo;t able to find any place to buy them, my usual sources have had them listed as \u0026ldquo;Out of Stock\u0026rdquo; for months, and even the HiFiBerry website doesn\u0026rsquo;t list them as available. However, there was another brand \u0026ldquo;Pimoroni\u0026rdquo; that also make a Pi0 DAC. So I went with that.\nYou can see the listing for the Pimoroni Audio DAC SHIM here on PiShop.ca. When I checked to add the link, I see that it is currently sold out. Even on the Pimoroni site, it\u0026rsquo;s listed as sold out. I\u0026rsquo;m not sure if this is going to be a long-term issue.\nThis is what it looks like:\nYou just shove it down on the GPIO header and it works. No soldering. It\u0026rsquo;s a great design.\nIf you cannot get one of these, then you\u0026rsquo;ll have to source out another DAC board. There are others, but they are generally a bit more expensive, and a bit bigger, too. HiFiBerry makes a variety of them that also do not require soldering. They seem to be in stock, too. My impression is that if you use a HiFiBerry DAC, the setup instructions will be the same as for the Pimoroni SHIM.\nUpdate: I received notification from PiShop.ca that the HiFiBerry DAC is now in stock. Too late for me, but it\u0026rsquo;s a option you might want to consider.\nMopidy # You can use any music playing software that makes sense to you. Snapcast is capable of taking its input from a variety of pipelines, so as long as your music playing software is capable of feeding data into one of those, you\u0026rsquo;re good to go.\nThe concensus for homelabs seems to be \u0026ldquo;Modpidy\u0026rdquo;. Mopidy can take its source from a number of different places, and there are a variety of plugins that you can install to expand this list. This includes services like Spotify, Tidal, JellyFin, TuneIn and YouTube. It also has an extension for SomaFM, which is what I was looking for. Additionally, it can play local files natively.\nThe terminology in situations like this gets a little bit confusing. We are going to talk about Mopidy installed as a service on the server, but it acts as a client to the external streaming services.\nAdditionally, Mopidy has a number of API\u0026rsquo;s available, including a web services API. This means that create home-brewed control scripts or programs to automate aspects of Mopidy. This also means that a number of web clients have been built and are available to install. You\u0026rsquo;re going to need one of these if you want to control your Mopidy server in an ad-hoc fashion.\nInstallation and Configuration # I spun up a Debian 13 LXC in Proxmox to be both my SnapCast and Mopidy host. I didn\u0026rsquo;t see any reason to split these out into two containers because I view them as two sides of the same coin, and having them run in the same container makes things a bit easier.\nAs my Proxmox cluster is sitting in the basement and none of the hosts have any audio equipment attached, I couldn\u0026rsquo;t see any point in installing Mopidy first, as it would be difficult to test it. So I chose to install SnapServer first and test it with some white noise.\nA Quick Note About IP Addresses and Hostnames # In my homelab I have OPNSense providing the DHCP service for the whole network, and I have a Technitium DNS server. My usual process is to create any new servers or devices as DHCP clients. Then I check the \u0026ldquo;leases\u0026rdquo; page in OPNSense, find the device and set it as a static address. Then I go into the DNS server and add an entry for it with that address. This gives me centralized control over the addressing and naming without having to reconfigure the individual servers and devices.\nI\u0026rsquo;m not going to mention this at all in the rest of these instructions. You\u0026rsquo;ll have to take some steps to ensure that you have all of the devices configured the way that you want them in this respect using whatever process you have adopted.\nSnapCast Server # I just created a new LXC with Debian 13 and logged into it on the console. If you use apt to install snapserver, you\u0026rsquo;ll get version 0.31, which is a bit old.\nAt the time of writing this article, the next version of SnapCast has been released, version 0.35.0. My instructions are going to continue to use version 0.34.0, which is about 6 months older. Once again, you should be able to use the newer version without any other changes to the process. I\u0026rsquo;d be careful to make sure that both the SnapCast server and the clients are all using the same version.\nDownload the package from the project on GitHub:\nwget https://github.com/snapcast/snapcast/releases/download/v0.34.0/snapserver_0.34.0-1_amd64_trixie.deb and then use dkpg to install it:\n# dpkg -i snapserver_0.34.0-1_amd64_trixie.deb Selecting previously unselected package snapserver. (Reading database ... 20235 files and directories currently installed.) Preparing to unpack snapserver_0.34.0-1_amd64_trixie.deb ... Unpacking snapserver (0.34.0-1) ... Setting up snapserver (0.34.0-1) ... Installing new version of config file /etc/default/snapserver ... Installing new version of config file /etc/init.d/snapserver ... Installing new version of config file /etc/snapserver.conf ... Processing triggers for man-db (2.13.1-1) ... This handles the setup as a service. You can check it right away:\n# systemctl status snapserver -n40 * snapserver.service - Snapcast server Loaded: loaded (/usr/lib/systemd/system/snapserver.service; enabled; preset: enabled) Active: active (running) since Mon 2026-04-06 17:28:09 UTC; 26s ago Invocation: 6c949040b6dc401485092210bfce4ce5 Docs: man:snapserver(1) Main PID: 1386 (snapserver) Tasks: 3 (limit: 18886) Memory: 2.2M (peak: 2.5M) CPU: 83ms CGroup: /system.slice/snapserver.service `-1386 /usr/bin/snapserver --logging.sink=system --server.datadir=/var/lib/snapserver Apr 06 17:28:09 testsnap systemd[1]: Started snapserver.service - Snapcast server. Apr 06 17:28:09 testsnap snapserver[1386]: (Snapserver) Version 0.34.0, revision 0ac55084 Apr 06 17:28:09 testsnap snapserver[1386]: (Snapserver) Stream plugin directory: \u0026#39;\u0026#34;/usr/share/snapserver/plug-ins\u0026#34;\u0026#39;, sandbox directory: \u0026#39;\u0026#34;/usr/share/snapserver/\u0026gt; Apr 06 17:28:09 testsnap snapserver[1386]: (Snapserver) Adding source: pipe:///tmp/snapfifo?name=default Apr 06 17:28:09 testsnap snapserver[1386]: (Avahi) Adding service \u0026#39;Snapcast\u0026#39; Apr 06 17:28:09 testsnap snapserver[1386]: (Snapserver) Using HTTP host name: testsnap Apr 06 17:28:09 testsnap snapserver[1386]: (PcmStream) PcmStream: default, sampleFormat: 48000:16:2 Apr 06 17:28:09 testsnap snapserver[1386]: (PipeStream) PipeStream mode: create Apr 06 17:28:09 testsnap snapserver[1386]: (Server) Stream: {\u0026#34;fragment\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;host\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;path\u0026#34;:\u0026#34;/tmp/snapfifo\u0026#34;,\u0026#34;query\u0026#34;:{\u0026#34;chunk_ms\u0026#34;:\u0026#34;20\u0026#34;,\u0026#34;codec\u0026#34;:\u0026#34;flac\u0026#34;,\u0026#34;name\u0026#34;:\u0026#34;defa\u0026gt; Apr 06 17:28:09 testsnap snapserver[1386]: (FlacEnc) Init - compression level: 2 Apr 06 17:28:09 testsnap snapserver[1386]: (ControlServer) Creating TCP control acceptor for address: ::, port: 1705 Apr 06 17:28:09 testsnap snapserver[1386]: (ControlServer) Creating HTTP acceptor for address: ::, port: 1780 Apr 06 17:28:09 testsnap snapserver[1386]: (StreamServer) Creating TCP stream acceptor for address: ::, port: 1704 Apr 06 17:28:09 testsnap snapserver[1386]: (Snapserver) Number of threads: 2, hw threads: 1 Apr 06 17:28:09 testsnap snapserver[1386]: (AsioStream) Error reading message: End of file, length: 0, ec: asio.misc:2 Apr 06 17:28:10 testsnap snapserver[1386]: (Avahi) Service \u0026#39;Snapcast\u0026#39; successfully established. It\u0026rsquo;s even enabled! However, it has failed to start. Let\u0026rsquo;s see why. Look at this line of the output:\nApr 06 17:28:09 testsnap snapserver[1386]: (Snapserver) Adding source: pipe:///tmp/snapfifo?name=default See how it says \u0026ldquo;\u0026hellip;/tmp/snapfifo\u0026hellip;\u0026rdquo;? That\u0026rsquo;s going to cause problems.\nThere\u0026rsquo;s a new security feature in Debian that locks down fifo files in \u0026ldquo;sticky\u0026rdquo; directories like /tmp in order to make spoofing attempts harder. You can disable this by turning off the fs.protected_fifos kernel parameter, but this is probably not a good idea. Instead, move the Snapcast input fifo to somewhere else.\nTo do this, you\u0026rsquo;ll need to edit the /etc/snapserver.conf file. It\u0026rsquo;s big, and has lots of stuff in it, but the section you want to look at is called \u0026ldquo;[stream]\u0026rdquo;:\n# Stream settings ############################################################# # [stream] # address to listen on, can be specified multiple times # use \u0026#34;0.0.0.0\u0026#34; to bind to any IPv4 address or :: to bind to any IPv6 address # or \u0026#34;127.0.0.1\u0026#34; or \u0026#34;::1\u0026#34; to bind to localhost IPv4 or IPv6, respectively # use the address of a specific network interface to just listen to and accept # connections from that interface #bind_to_address = :: # which port the server should listen to #port = 1704 # source URI of the PCM input stream, can be configured multiple times # The following notation is used in this paragraph: # \u0026lt;angle brackets\u0026gt;: the whole expression must be replaced with your specific setting # [square brackets]: the whole expression is optional and can be left out # [key=value]: if you leave this option out, \u0026#34;value\u0026#34; will be the default for \u0026#34;key\u0026#34; # # Format: TYPE://host/path?name=\u0026lt;name\u0026gt;[\u0026amp;codec=\u0026lt;codec\u0026gt;][\u0026amp;sampleformat=\u0026lt;sampleformat\u0026gt;][\u0026amp;chunk_ms=\u0026lt;chunk ms\u0026gt;][\u0026amp;controlscript=\u0026lt;control script filename\u0026gt;[\u0026amp;controlscriptparams=\u0026lt;control script command line arguments\u0026gt;]] # parameters have the form \u0026#34;key=value\u0026#34;, they are concatenated with an \u0026#34;\u0026amp;\u0026#34; character # parameter \u0026#34;name\u0026#34; is mandatory for all sources, while codec, sampleformat and chunk_ms are optional # and will override the default codec, sampleformat or chunk_ms settings # Available types are: # pipe: pipe:///\u0026lt;path/to/pipe\u0026gt;?name=\u0026lt;name\u0026gt;[\u0026amp;mode=create], mode can be \u0026#34;create\u0026#34; or \u0026#34;read\u0026#34; # librespot: librespot:///\u0026lt;path/to/librespot\u0026gt;?name=\u0026lt;name\u0026gt;[\u0026amp;username=\u0026lt;my username\u0026gt;\u0026amp;password=\u0026lt;my password\u0026gt;][\u0026amp;devicename=Snapcast][\u0026amp;bitrate=320][\u0026amp;wd_timeout=7800][\u0026amp;volume=100][\u0026amp;onevent=\u0026#34;\u0026#34;][\u0026amp;nomalize=false][\u0026amp;autoplay=false][\u0026amp;params=\u0026lt;generic librepsot process arguments\u0026gt;] # note that you need to have the librespot binary on your machine # sampleformat will be set to \u0026#34;44100:16:2\u0026#34; # file: file:///\u0026lt;path/to/PCM/file\u0026gt;?name=\u0026lt;name\u0026gt; # process: process:///\u0026lt;path/to/process\u0026gt;?name=\u0026lt;name\u0026gt;[\u0026amp;wd_timeout=0][\u0026amp;log_stderr=false][\u0026amp;params=\u0026lt;process arguments\u0026gt;] # airplay: airplay:///\u0026lt;path/to/airplay\u0026gt;?name=\u0026lt;name\u0026gt;[\u0026amp;port=5000] # note that you need to have the airplay binary on your machine # sampleformat will be set to \u0026#34;44100:16:2\u0026#34; # tcp server: tcp://\u0026lt;listen IP, e.g. 127.0.0.1\u0026gt;:\u0026lt;port\u0026gt;?name=\u0026lt;name\u0026gt;[\u0026amp;mode=server] # tcp client: tcp://\u0026lt;server IP, e.g. 127.0.0.1\u0026gt;:\u0026lt;port\u0026gt;?name=\u0026lt;name\u0026gt;\u0026amp;mode=client # alsa: alsa:///?name=\u0026lt;name\u0026gt;\u0026amp;device=\u0026lt;alsa device\u0026gt;[\u0026amp;send_silence=false][\u0026amp;idle_threshold=100][\u0026amp;silence_threshold_percent=0.0] # meta: meta:///\u0026lt;name of source#1\u0026gt;/\u0026lt;name of source#2\u0026gt;/.../\u0026lt;name of source#N\u0026gt;?name=\u0026lt;name\u0026gt; source = pipe:///tmp/snapfifo?name=default There\u0026rsquo;s more in the \u0026ldquo;[stream]\u0026rdquo; section, but I stopped at the line we need to change, \u0026ldquo;source\u0026rdquo;.\nChange it to this:\nsource = pipe:///var/local/snapserver/snapfifo?name=default You\u0026rsquo;ll have to create /var/local/snapserver directory, and set the ownership:\n# mkdir /var/local/snapserver # chown snapserver /var/local/snapserver # chgrp snapserver /var/local/snapserver # systemctl start snapserver # systemctl status snapserver -n40 * snapserver.service - Snapcast server Loaded: loaded (/usr/lib/systemd/system/snapserver.service; enabled; preset: enabled) Active: active (running) since Mon 2026-04-06 17:36:01 UTC; 40s ago Invocation: 022347aa459a41d0b8edb82e4c5eeaf5 Docs: man:snapserver(1) Main PID: 1446 (snapserver) Tasks: 3 (limit: 18886) Memory: 2.4M (peak: 2.4M) CPU: 115ms CGroup: /system.slice/snapserver.service `-1446 /usr/bin/snapserver --logging.sink=system --server.datadir=/var/lib/snapserver Apr 06 17:36:01 testsnap systemd[1]: Started snapserver.service - Snapcast server. Apr 06 17:36:01 testsnap snapserver[1446]: (Snapserver) Version 0.34.0, revision 0ac55084 Apr 06 17:36:01 testsnap snapserver[1446]: (Snapserver) Stream plugin directory: \u0026#39;\u0026#34;/usr/share/snapserver/plug-ins\u0026#34;\u0026#39;, sandbox directory: \u0026#39;\u0026#34;/usr/share/snapserver/\u0026gt; Apr 06 17:36:01 testsnap snapserver[1446]: (Snapserver) Adding source: pipe:///var/local/snapserver/snapfifo?name=default Apr 06 17:36:01 testsnap snapserver[1446]: (Avahi) Adding service \u0026#39;Snapcast\u0026#39; Apr 06 17:36:01 testsnap snapserver[1446]: (Snapserver) Using HTTP host name: testsnap Apr 06 17:36:01 testsnap snapserver[1446]: (PcmStream) PcmStream: default, sampleFormat: 48000:16:2 Apr 06 17:36:01 testsnap snapserver[1446]: (PipeStream) PipeStream mode: create Apr 06 17:36:01 testsnap snapserver[1446]: (Server) Stream: {\u0026#34;fragment\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;host\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;path\u0026#34;:\u0026#34;/var/local/snapserver/snapfifo\u0026#34;,\u0026#34;query\u0026#34;:{\u0026#34;chunk_ms\u0026#34;:\u0026#34;20\u0026#34;,\u0026#34;codec\u0026#34;:\u0026#34;f\u0026gt; Apr 06 17:36:01 testsnap snapserver[1446]: (FlacEnc) Init - compression level: 2 Apr 06 17:36:01 testsnap snapserver[1446]: (ControlServer) Creating TCP control acceptor for address: ::, port: 1705 Apr 06 17:36:01 testsnap snapserver[1446]: (ControlServer) Creating HTTP acceptor for address: ::, port: 1780 Apr 06 17:36:01 testsnap snapserver[1446]: (StreamServer) Creating TCP stream acceptor for address: ::, port: 1704 Apr 06 17:36:01 testsnap snapserver[1446]: (Snapserver) Number of threads: 2, hw threads: 1 Apr 06 17:36:01 testsnap snapserver[1446]: (AsioStream) Error reading message: End of file, length: 0, ec: asio.misc:2 Apr 06 17:36:02 testsnap snapserver[1446]: (Avahi) Service \u0026#39;Snapcast\u0026#39; successfully established. Some online discussions about this have suggested /var/run/snapserver, but I have found that /var/run is cleaned up automatically during system boot and /var/run/snapserver gets deleted. You can automate the creation in the service startup, but /var/local/snapserver seems just fine to me.\nTesting # In order to test your SnapCast server, you\u0026rsquo;ll need some clients. The easiest one comes with the server, and it\u0026rsquo;s available in a web page.\nSnapweb # Open up a browser and go to port 1780 on whatever the address or hostname of your SnapCast server is. You should get a mostly black page with \u0026ldquo;Snapcast\u0026rdquo; and a hamburger menu at the top left, and a \u0026ldquo;Play\u0026rdquo; button on the top right. Go ahead and click on that \u0026ldquo;Play\u0026rdquo; button and you should get this:\nAt this point, you would be hearing something if there was anything streaming. Let\u0026rsquo;s pump some white noise into that fifo file back on the server:\ncat /dev/urandom \u0026gt; /var/local/snapserver/snapfifo Now you should hear something. If you do, then you have now confirmed that the server setup is correct, and if you have problems going forward, it\u0026rsquo;s probably not going to be the server, but your client setup.\nConclusion # This wraps up Part I.\nAt this point, you should have a basic understanding about how SnapCast works, and, if you\u0026rsquo;ve followed along, you\u0026rsquo;ll have a working SnapCast server. It\u0026rsquo;s not a lot of use yet, but we\u0026rsquo;ll get closer to that in Part II of this series.\nPart II continues with the Raspberry Pi SnapCast clients.\n","date":"20 May 2026","externalUrl":null,"permalink":"/homelab/snapcast/part0/","section":"Homelabs","summary":"A way to stream audio around your entire house using inexpensive RaspberryPi computers and SnapCast","title":"Whole Home Audio With SnapCast - Part I","type":"homelab"},{"content":" The \u0026ldquo;Observer Pattern\u0026rdquo; # The key element in how JavaFX achieves Reactivity is via a set of objects which implement the \u0026ldquo;Observer Pattern\u0026rdquo;. Fundamentally, this is a hierarchy of classes which allow you to create an observable wrapper around virtually any data type. Along with that comes all of the structure that you need to connect them to each other so that a change in one observable object triggers changes in other observable objects.\nWhen you are working with Observables, you tend to be concerned with establishing the connections between them. This means that the code that you write is configuration code that happens when the Observables are created. And since almost all of the uses that you\u0026rsquo;ll have for them in the GUI are expressed via Observables baked into the Nodes, this means that you connect them to the Node Properties and then forget about them.\nThe Observable library is extensive, extensible and has massive functionality. This means that it can be a challenge to learn how to use it properly.\nJust like the Nodes, there are a large number of classes and interfaces that extend from a single parent class. This is the Observable interface. This includes the ObservableList and Property interfaces which are used extensively in just about any JavaFX application.\nIn this section, we are taking an in-depth look at all things observable in JavaFX. You can get a long way just by reading the first two articles here. If you want to achieve comprehensive understanding about how the entire library hangs together, there are several articles in the section called \u0026ldquo;Guide to the Observable Classes\u0026rdquo;.\n","date":"5 August 2025","externalUrl":null,"permalink":"/javafx/elements/observables/properties/","section":"JavaFX","summary":"The absolute core element of observability in JavaFX is the Property and it’s related sub-classes and interfaces. Properties are used for virtually every aspect of every screen Node that you can interact with. Values in TextFields and Labels are stored in a StringProperty, while elements like maxWidth and minWidth are stored in DoubleProperties. The items in a ListView or a TableView are stored in an ObservableList.\nThe key to creating Reactive applications in JavaFX is to use these Properties as observables, and not simply annoying wrappers around their values. Create Bindings to link them together instead of stuffing values in them and scraping values out them.\nBut to do that, you need to understand they work, and how to use them properly.\n","title":"Observable Objects","type":"javafx"},{"content":" Introduction # From time to time the question comes up about Swing vs JavaFX. Which is better? Is Swing dead? Should you convert your old Swing application to JavaFX?\nPeople always tend to focus in these discussions on the look and feel of Swing. Out-of-the-box GUI\u0026rsquo;s built in Swing tend to look like refuges from the 1990\u0026rsquo;s, which isn\u0026rsquo;t surprising considering the age of Swing. JavaFX tends to look more modern by default, although still very \u0026ldquo;corporate\u0026rdquo;. But it seems easier to customize the look and feel with JavaFX.\nBut I\u0026rsquo;m not sure that any of that is enough reason to go through a painful conversion from Swing to JavaFX.\nNote The most important difference between JavaFX and Swing is that JavaFX is designed to support Reactive GUI development.\nWhen you look at the scope of Observable classes and utilities this - at least to me - becomes obvious.\nYes, Swing does support the \u0026ldquo;Observer Pattern\u0026rdquo;, but it is extremely crude compared to JavaFX. The vast library of JavaFX Properties, Bindings, Listeners and Subscriptions, and the way that they are integrated natively into all of the screen Nodes, make it virtually trivial to implement a Reactive framework.\nSuccess My experience has been that applications architected with a Reactive design are perhaps an order of magnitude easier to implement, enhance and maintain. Now that could be a valid reason to convert a Swing application to JavaFX.\nThe problem, though, is that it is also possible to write a JavaFX application in an imperative/declarative fashion. And it works, although it\u0026rsquo;s not as clean as using a Reactive approach.\nEven worse, nobody talks about \u0026ldquo;Reactive JavaFX\u0026rdquo;. It\u0026rsquo;s not mentioned at all in the Oracle documentation or tutorials. It\u0026rsquo;s a shame.\nIn this article we are going to look at how Reactive GUI\u0026rsquo;s work, the different kinds of Reactive systems that are available and how JavaFX fits in with those. Then we are going to look at how a Reactive JavaFX system is different from a Declarative/Imperative system, and we\u0026rsquo;ll see how Reactive design makes the application simpler by removing coupling. Finally, we\u0026rsquo;ll look at how Reactive Design fits in with frameworks like MVC, MVVM and MVCI.\nWhat is Reactive Design? # Any \u0026ldquo;Reactive\u0026rdquo; design involves creating a data representation of the \u0026ldquo;State\u0026rdquo; of your GUI. When the values in this State construct change, then the Reactive system will update the GUI such that it reflects those changes.\nThe layout programmer\u0026rsquo;s job, therefore, is define how the layout will look and behave with any given set of values in the State.\nWhen you start to work with a Reactive system, you realize that there are two kinds of things that can happen in a GUI:\nChanges to State\nFor instance a user types something in a textfield, moves a slider, or selects an item in a list.\nActions\nThis could be a user clicking on a button, or actions associated with the user moving a slider or selecting an item in a list.\nIn a Declarative (or \u0026ldquo;Imperative\u0026rdquo;) approach to GUI design, everything is an \u0026ldquo;action\u0026rdquo;. For instance, the user types in a textfield, but this affects only the screen until they shift the focus to another field, at which point a \u0026ldquo;focus lost\u0026rdquo; action might be triggered that would cause code to run that moves data around or changes the layout.\nA Reactive system usually looks something like this:\nThe layout has elements that control how it behaves that are \u0026ldquo;bound\u0026rdquo; or \u0026ldquo;synchronized\u0026rdquo; in both directions with the \u0026ldquo;Presentation Model\u0026rdquo;, which is the data representation of \u0026ldquo;State\u0026rdquo;.\nAdditionally, actions might be triggered by the layout. Whatever handles these actions also has access to the data elements in the Presentation Model, but usually in a more normal read and write manner - not through binding. It is possible that the \u0026ldquo;Action Handler\u0026rdquo; could tell the layout to take some action, although I\u0026rsquo;m not sure that all Reactive systems allow this.\nTypes of Reactive Systems # Personally, I\u0026rsquo;ve encountered three different Reactive systems: JavaFX, Jetpack Compose, and React.\nJetpack Compose is the preferred system for devoloping Android applications, while React is for web development and JavaFX, of course, is for desktop applications. I have some experience with Jetpack Compose, and a passing familiarity with React.\nFrom what I have seen, these break down into two different approaches to Reactivity. I\u0026rsquo;ve never seen them defined anywhere, so I am going to call them \u0026ldquo;Compositional Reactivity\u0026rdquo; and \u0026ldquo;Layout Reactivity\u0026rdquo;. Let\u0026rsquo;s take a look at them\u0026hellip;\nCompostional Reactivity # This is the implementation of Reactivity that is use by both Jetpack Compose and React.\nEssentially, the code that \u0026ldquo;composes\u0026rdquo; the layout is divided up into snippets, and each snippet is dependent on whatever elements of State that it uses. If one of those State elements changes, then that code snippet (and presumably any code snippets that it calls) will be re-executed and redraw the screen. All of the screen elements themselves are truly static, and depend on the re-execution of the composition code in order to appear to behave dynamically.\nWith Compositional Reactivity, you expect that your layout code is going to be run over and over again - at least in part. Each time it executes, it will build the static layout according the values in the Presentation Model at the time that it executes.\nHere\u0026rsquo;s an example from the official Jetpack Compose tutorial:\n@Composable fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { // Changes to count are now tracked by Compose val count: MutableState\u0026lt;Int\u0026gt; = mutableStateOf(0) Text(\u0026#34;You\u0026#39;ve had ${count.value} glasses.\u0026#34;) Button(onClick = { count.value++ }, Modifier.padding(top = 8.dp)) { Text(\u0026#34;Add one\u0026#34;) } } } The @Composable annotation declares this function as something that Jetpack Compose is going to use to build the layout and which is dependent on State. This function creates a Column and sticks a Text and a Button in it.\nThe line that instantiates count as MutableState\u0026lt;Int\u0026gt; is where it defines the element of State that the this function is dependent on. Changes to count will trigger re-execution of this code - but let\u0026rsquo;s just call it \u0026ldquo;recomposition\u0026rdquo;.\nNotice that the Text just shows a static String that includes the current value of count at the time that it executes. There is no way to change the value displayed by a Text.\nThe Button just increments count. Here you see how an action updates the Presentation Model, and that update triggers the recomposition of the layout.\nAs soon as count changes, this piece of layout is determined to be defunct and needs to be re-composed, so this function will be re-executed, the Column will be rebuilt and the Text will be recreated and populated with a String that now contains the new value of count.\nTo the user, it just looks like the value in the Text changed. But from a programming perspective, a piece of the layout has been recomposed.\nYou can put any logic that you want in there. Try this:\n@Composable fun WaterCounter(modifier: Modifier = Modifier) { Column(modifier = modifier.padding(16.dp)) { // Changes to count are now tracked by Compose val count: MutableState\u0026lt;Int\u0026gt; = mutableStateOf(0) if (count \u0026gt; 0) { Text(\u0026#34;Glug...glug...glug...\u0026#34;) } Text(\u0026#34;You\u0026#39;ve had ${count.value} glasses.\u0026#34;) Button(onClick = { count.value++ }, Modifier.padding(top = 8.dp)) { Text(\u0026#34;Add one\u0026#34;) } } } The first time through count will be \u0026ldquo;0\u0026rdquo; and that \u0026ldquo;Glug..glug\u0026hellip;glug\u0026hellip;\u0026rdquo; Text will not appear. Every other time, count will be non-zero and that Text will appear.\nLayout Reactivity # With \u0026ldquo;Layout Reactivity\u0026rdquo; the layout code is executed just once, but the layout itself behaves dynamically in response to changes to the Presentation Model. This is the approach that JavaFX uses.\nLet\u0026rsquo;s see how you would do the same type of counting exercise as in the Jetpack Compose example:\nfun waterCounter() : Region = VBox().apply { val counter :IntegerProperty = SimpleIntegerProperty(0) children += Label(\u0026#34;Glug...glug...glug...\u0026#34;).apply { visibleProperty().bind(counter.greaterThan(0)) managedProperty().bind(visibleProperty()) } children += Label().apply { textProperty().bind(counter.map{ \u0026#34;You\u0026#39;ve had $it glasses.\u0026#34; }) } childern += Button(\u0026#34;Add one\u0026#34;).apply { setOnClick { evt -\u0026gt; counter.value += 1 } } } Here we have a static layout that behaves dynamically: It\u0026rsquo;s \u0026ldquo;static\u0026rdquo; in that layout itself doesn\u0026rsquo;t change. No Nodes are added or removed from the layout. It behaves dynamically, because the individual Nodes do things in reaction to the Presentation Model.\nThat \u0026ldquo;Glug\u0026hellip;\u0026rdquo; Text is always there. However, its visibility is bound to whether or not count is greater than zero. Its managed Property which controls whether or not the layout manager gives it space on the screen is also bound to the same condition. When count is zero, it\u0026rsquo;s neither visible, nor given any space on the screen.\nThe text in the second Label is bound to counter through a map{} functiont that provides the rest of the String.\nOnce again, the Button simply triggers an action that updates the Presentation Model which, in turn, causes the layout to react and change its behaviour.\nThese are Very Different Approaches # I think you can see that to a user, there is no difference between either approach. The GUI\u0026rsquo;s appear to behave in an identical manner.\nIt\u0026rsquo;s the implementation which is completely different.\nIn the Jetpack Compose approach, that \u0026ldquo;Glug\u0026hellip;\u0026rdquo; Text didn\u0026rsquo;t even exist in the layout before the Button was clicked. In the JavaFX version, it was always there - just invisible.\nThe truth is that both approaches boil down to the same thing. At some point the screen has to be redrawn to show that \u0026ldquo;Glug\u0026hellip;\u0026rdquo; Label\\Text. But with JavaFX, that redrawing happens deep, deep inside the guts of the Layout Manager, and as programmers we don\u0026rsquo;t ever need to think about it. In Jetpack Compose and React, that\u0026rsquo;s just about all that you think about - \u0026ldquo;How will you build the layout when the data looks like this..or this..or this?\u0026rdquo;\nObviously, Jetpack Compose and React are far more popular than JavaFX, so when people think about \u0026ldquo;Reactive\u0026rdquo; systems, they are probably imagining a Compositionally Reactive design.\nHow Reactive Design Makes Systems Better # Back in the introduction, I claimed that Reactive Design makes systems nearly an order of magnitude easier to enhance, debug and maintain. Let\u0026rsquo;s look at how that might be\u0026hellip;\nThe Same Example In an Imperative Design # Before we go any further, let\u0026rsquo;s look at the example, and how you would write it in JavaFX like it was Swing:\nfun waterCounter() : Region = VBox().apply { val counter : Int = 0 val glugLabel = Label(\u0026#34;Glug...glug...glug...\u0026#34;) val countLabel = Label(\u0026#34;You\u0026#39;ve had $counter glasses.\u0026#34;) val button = Button(\u0026#34;Add one\u0026#34;) button.setOnAction { evt -\u0026gt; countLabel.setText(\u0026#34;You\u0026#39;ve had $counter glasses.\u0026#34;) getChildren().setAll(listOf(glugLabel, countLabel, button)) } children += listOf(countLabel, button) } Instead of a Binding, the value of countLabel is changed via setText(), and instead of using a static layout with glugLabel toggling between invisible and visible, the layout is modified to add glugLabel in response to the Button click.\nCoupling # One thing you\u0026rsquo;ll notice in this Imperative example is that we now have variables to hold Labels and Button. That\u0026rsquo;s because we need references to them so that we can update them from inside the Button action. You\u0026rsquo;ll also see that button is calling a method on the enclosing VBox - getChildren().\nThis are examples of \u0026ldquo;coupling\u0026rdquo;.\nImportant Coupling is probably the single most important factor that makes applications difficult to understand, maintain and enhance.\nWhen we expose glugLabel, countLabel and the enclosing VBox to button, we don\u0026rsquo;t just reveal their existence, but also their implementation.\nOnce we\u0026rsquo;ve shared the implementation details of something, then we\u0026rsquo;ve created coupling. It\u0026rsquo;s coupling because we cannot change that implementation without looking at how that implementation knowledge is used through the system.\nEven here, where the variable scope is just 10 lines of code, it can cause problems. Imagine that we wanted to change the text of counterLabel to this:\nval countLabel = Label(\u0026#34;You\u0026#39;ve had $counter pints of beer.\u0026#34;) We\u0026rsquo;ll have a problem if we just stop there, because button is going to set it back to \u0026quot;You've had $counter glasses.\u0026quot; as soon as it is clicked. Any change to the implementation of countLabel means our application might be broken.\nLet\u0026rsquo;s say that we decided to change our implementation of counterLabel to be something other than a Label. Perhaps an ImageView sprite with a number of Images of different numbers of beer glasses, tied somehow to counter. Now, our Button action won\u0026rsquo;t work at all, and we\u0026rsquo;ll have to change its logic to manipulate an ImageView instead. At least here we\u0026rsquo;ll get a compiler error.\nThis situation gets worse if button is defined somewhere else. Then, we\u0026rsquo;ll need to push countLabel and our VBox up to fields in our layout class, so that we can access them with button:\nprivate val countLabel = Label(\u0026#34;You\u0026#39;ve had 0 glasses.\u0026#34;) private val countPane : Pane = waterCounter() . . . fun waterCounter() : Pane = VBox().apply { . . children += countLabel . . } . . . fun someOtherPane() : Region = VBox().apply{ . . val counter : Int = 0 val glugLabel = Label(\u0026#34;Glug...glug...glug...\u0026#34;) val button = Button(\u0026#34;Add one\u0026#34;) button.setOnAction { evt -\u0026gt; countLabel.setText(\u0026#34;You\u0026#39;ve had ${counter++} glasses.\u0026#34;) countPane.getChildren().setAll(listOf(..., glugLabel, countLabel, ...)) } children += button } Now, we need to know the contents of countPane in order to alter it without losing things already in it. Any changes to waterCounter() will require changes to someOtherPane() as well. Also, countPane has to be exposed as at least a Pane since Region doesn\u0026rsquo;t expose getChildren() publicly.\nEven worse, in any implementation of this, we still have button reconfiguring countLabel. Ideally, the only thing that configures countLabel should be countLabel itself, and the layout code that instantiates countLabel.\nFinally, in any case where you have a reference to an object (Node or otherwise), that exposes the nature of its implementation, you have to check through the entire scope of that reference any time you contemplate making changes to that implementation. In the first imperative example, the scope was just waterCounter(){}, but in the last example the scope was the entire layout building class.\nIn this lastest example, if you did decide to change countLabel to something else, maybe that sprite ImageView or a Slider or a pie chart, you\u0026rsquo;d have to make sure that no other part of the layout class uses the implementation of countLabel. Maybe there\u0026rsquo;s some other Node that looks at the length of countLabel.getText() to determine some aspect of its layout.\nThe Presentation Model as a Layout Builder Field # In the first Reactive example, we had counter instantiated as a StringProperty variable reference local to waterCounter(){}. If we move button somewhere else, then we\u0026rsquo;ll need to expand the scope of counter to a field in the layout builder class. The code would look like this:\nprivate val counter : IntegerProperty = SimpleIntegerProperty(0) . . . fun waterCounter() : Region = VBox().apply { children += Label(\u0026#34;Glug...glug...glug...\u0026#34;).apply { visibleProperty().bind(counter.greaterThan(0)) managedProperty().bind(visibleProperty()) } children += Label().apply { textProperty().bind(counter.map{ \u0026#34;You\u0026#39;ve had $it glasses.\u0026#34; }) } } . . . fun someOtherPane() { . . . childern += Button(\u0026#34;Add one\u0026#34;).apply { setOnClick { evt -\u0026gt; counter.value += 1 } } . . . } You can see that button remains completely ignorant of the existence of the counter Label.\nThere still is a dependency - there has to be. The counter Label is dependent on counter, and the Button knows about the nature of counter.\nThere are some things that make this a very different kind of coupling:\nThe nature of counter is very generic.\nIt is an IntegerProperty, which in the world of Properties is no more specific than declaring a regular variable as Int. Neither the counter Label, nor the Button is dependent on, even aware of the other. Neither the counter Label, nor the Button is aware of, or dependent on the implementation the other. Neither the counter Label, nor the Button is aware of the implementation of counter. Any other Node in the layout can interact with counter without impacting the implementation of either the Button or the counter Label. There has to be some understanding of the meaning of counter within some scope. That scope might just be within the waterCounter(){} function, but it might be the entire layout builder class. It\u0026rsquo;s hard to imagine any degree of coupling that is less than this which is still functional.\nIn a very important way, though, we have compartmentalized and isolated that coupling. In this example, it\u0026rsquo;s counter, and we\u0026rsquo;ve essentially declared it the key the coupling point. Any Node that we create that interacts with counter needs to share that understanding of the meaning of counter, and behave accordingly. But it doesn\u0026rsquo;t have to understand how any other Nodes in the layout are interacting with counter.\nReactive Vs Imperative Design in a Framework # The goal of any framework, like MVC, MVP, MVVM or MVCI, is to reduce coupling. Specifically, they are designed to provide a clear separation between the business/application logic and the layout. This means that business logic works without any knowledge of the implementation of the layout, and the layout without any knowledge of the implementation of the business logic.\nLet\u0026rsquo;s go back to our example\u0026hellip;\nImperative Design With a Framework # Imagine that whatever change needs to happen to counter is determined to be \u0026ldquo;application logic\u0026rdquo;, so it can no longer reside in the layout. We\u0026rsquo;ll need to change the logic for the Button action. Let\u0026rsquo;s look at a first try at the imperative design approach first:\nclass layoutBuilder(private val counterChanger : (Int, (Int) -\u0026gt; Unit) -\u0026gt; Unit) { . . . fun someOtherPane() : Region = VBox().apply{ . . . val counter : Int = 0 val glugLabel = Label(\u0026#34;Glug...glug...glug...\u0026#34;) val button = Button(\u0026#34;Add one\u0026#34;) button.setOnAction { evt -\u0026gt; counterChanger.invoke(counter) { newCounter -\u0026gt; counter = newCounter countLabel.setText(\u0026#34;You\u0026#39;ve had ${counter} glasses.\u0026#34;) if (counter \u0026gt; 0) { countPane.getChildren().setAll(listOf(..., glugLabel, countLabel, ...)) } else { countPane.getChildren().setAll(listOf(..., countLabel, ...)) } } } children += button } } Let\u0026rsquo;s look at that (Int, (Int) -\u0026gt; Unit) -\u0026gt; Unit first. This is the equivalent of the Java, BiConsumer\u0026lt;Integer, Consumer\u0026lt;Integer\u0026gt;\u0026gt;. It\u0026rsquo;s a functional element that accepts an Integer and another function that also accepts an Integer.\nWe don\u0026rsquo;t know what counterChanger does. Maybe it calls a website or some other external API, either of which would block the FXAT. So counterChanger might use Task to run the processing on a background thread. That means that we cannot just use (Int) -\u0026gt; Int, or Function\u0026lt;Integer, Integer\u0026gt; because we can\u0026rsquo;t wait for the answer to come back. Instead, we define a Consumer that will handle the answer when it comes back, and we send that off along with the current value of counter.\nWe also cannot assume that counter is going to increase through counterChanger. So we can\u0026rsquo;t just automatically add glugLabel when button is clicked. So we\u0026rsquo;ll need some logic to decide that.\nYou can see, though, that we still have a lot of coupling between button and the application logic that deals with counter. For instance, we know that it requires the current value of counter and nothing else. What if there were other factors involved? How about Spinner somewhere on the screen with a maximum value for counter? How about a CheckBox or ToggleButton that determined if the counter is incremented or decremented. Or something else that determined by how much? What if counterChanger also needs to reset that \u0026ldquo;how much\u0026rdquo; value back to 1 each time? All of this would change how button was implemented.\nLet\u0026rsquo;s look at the next step towards solving this\u0026hellip;\nThe Presentation Model in the Framework # Any of the frameworks - MVC, MVVM or MVCI - is going to have the Presentation Model represented somewhere. In MVC, it\u0026rsquo;s part of the \u0026ldquo;Model\u0026rdquo;, in MVVM it\u0026rsquo;s in the \u0026ldquo;ViewModel\u0026rdquo; and in MVCI it is the \u0026ldquo;Model\u0026rdquo; component. For the sake of simplicity, let\u0026rsquo;s assume that we\u0026rsquo;ve designed our framework implementation such that we can get the Presentation Model with a single method call, even if we are using MVC, or MVVM. That would be model.getPresentationModel() or viewModel.getPresentationModel().\nOnce we do that, we can assume that all of the components of the framework have easy access to the Presentation Model.\nIn this case, we would supply the Presentation Model to the layout builder class somehow, probably as a constructor parameter. But, because the Presentation Model is now shared, we don\u0026rsquo;t need to pass counter around any more. So we get this:\nclass layoutBuilder(private val presentationModel : PresentationModel, private val counterChanger : (() -\u0026gt; Unit) -\u0026gt; Unit) { . . . fun someOtherPane() : Region = VBox().apply{ . . . val glugLabel = Label(\u0026#34;Glug...glug...glug...\u0026#34;) val button = Button(\u0026#34;Add one\u0026#34;) button.setOnAction { evt -\u0026gt; counterChanger.invoke { countLabel.setText(\u0026#34;You\u0026#39;ve had ${presentationModel.counter} glasses.\u0026#34;) if (presentationModel.counter \u0026gt; 0) { countPane.getChildren().setAll(listOf(..., glugLabel, countLabel, ...)) } else { countPane.getChildren().setAll(listOf(..., countLabel, ...)) } } } children += button } } Whatever counterChanger does, it\u0026rsquo;s impact is to modify counter inside presentationModel. Now, counterChanger is essentially a Consumer\u0026lt;Runnable\u0026gt;.\nWhat does counterChanger do?\nWe don\u0026rsquo;t know the specifics, but there is an understanding that it will \u0026ldquo;change PresentationModel.counter\u0026rdquo;. Since it\u0026rsquo;s defined as a Consumer of a Runnable, it will execute that Runnable when it has finished updating PresentationModel.counter.\nLet\u0026rsquo;s look at one of those, \u0026ldquo;What if?\u0026rdquo; questions from the end of the last section. The \u0026ldquo;How much?\u0026rdquo; question, and the idea of resetting it after each call to counterChanger. We\u0026rsquo;ll assume we have a Spinner\u0026lt;Int\u0026gt; somewhere in our layout, and it\u0026rsquo;s called howMuchSpinner. We\u0026rsquo;ll also add a value into our Presentation Model:\nclass PresentationModel { var counter : Int = 0 var howMuch : Int = 1 } Now, this is what our layout code looks like:\nclass layoutBuilder(private val presentationModel : PresentationModel, private val counterChanger : (() -\u0026gt; Unit) -\u0026gt; Unit) { . . . fun someOtherPane() : Region = VBox().apply{ . . . val glugLabel = Label(\u0026#34;Glug...glug...glug...\u0026#34;) val button = Button(\u0026#34;Add one\u0026#34;) button.setOnAction { evt -\u0026gt; presentationModel.howMuch = howMuchSpinner.getValue() counterChanger.invoke { howMuchSpinner.setValue(presentationModel.howMuch) countLabel.setText(\u0026#34;You\u0026#39;ve had ${presentationModel.counter} glasses.\u0026#34;) if (presentationModel.counter \u0026gt; 0) { countPane.getChildren().setAll(listOf(..., glugLabel, countLabel, ...)) } else { countPane.getChildren().setAll(listOf(..., countLabel, ...)) } } } children += button } } Our button still doesn\u0026rsquo;t know exactly what counterChanger does. But it does need to know that its implementation requires howMuch and that it might change howMuch. It also needs to know that there is a Spinner called howMuchSpinner and that it needs to extract its value to update presentationModel before it invokes counterChanger.\nUnfortunately, this is the end of the road for the Imperative approach. As long as the \u0026ldquo;State\u0026rdquo; of the GUI is stored inside the Nodes that make up the layout, there is no way to avoid building the logic to pick the pieces that you need out of those Nodes and updating the Presentation Model. And that, all by itself, demands that the layout logic is coupled to the implementation of the application logic.\nBut, you ask\u0026hellip;What if you just always copy all of the Node data into the Presentation Model before any business logic call, and then copy it all back into the Nodes afterwards???\nWell, if you\u0026rsquo;re going to do that, you might as well go with a Reactive design. Let\u0026rsquo;s look at how that would work:\nReactive Design Framework # We\u0026rsquo;ll start out by looking at the Presentation Model:\nclass PresentationModel { val counter : IntegerProperty = SimpleIntegerProperty(0) val howMuch : IntegerProperty = SimpleIntegerProperty(1) } Not that exciting, we\u0026rsquo;ve just changed the two fields over to IntegerProperty. Here\u0026rsquo;s our layout code:\nclass layoutBuilder(val presentationModel : PresentationModel, val counterUpdater : () -\u0026gt; Unit) { . . . fun waterCounter() : Region = VBox().apply { children += Label(\u0026#34;Glug...glug...glug...\u0026#34;).apply { visibleProperty().bind(presentationModel.counter.greaterThan(0)) managedProperty().bind(visibleProperty()) } children += Label().apply { textProperty().bind(presentationModel.counter.map{ \u0026#34;You\u0026#39;ve had $it glasses.\u0026#34; }) } } . . . fun someOtherPane() { . . . children += Button(\u0026#34;Add one\u0026#34;).apply { setOnClick { evt -\u0026gt; counterUpdater.invoke() } } . . . } } Hmmmm\u0026hellip;that doesn\u0026rsquo;t look different from the last Reactive version. We now have presentationModel passed in as constructor parameter and counter has been removed as a stand-alone field. References to counter have been changed to presentationModel.counter.\nThe only other change is that the Button event now calls the counterUpdater which, in this version, is the Kotlin equivalent to Runnable. We don\u0026rsquo;t need to pass anything to it, because everything is already synchronized with presentationModel, which the application logic has access to.\nNote that the Button logic does nothing except invoke counterUpdater. It has no need to know about any other Nodes in the layout, nor does it have any need to know anything about the implementation of counterUpdater.\nYou\u0026rsquo;ll also note that I haven\u0026rsquo;t put in any code at all that deals with PresentationModel.howMuch. Presumably, there\u0026rsquo;s some kind of Node, or Nodes that synchronize with PresentationModel.howMuch, but they are not relevant to this discussion at all.\nNone of anything that we are discussing here is coupled to the implementation of PresentationModel.howMuch at all.\nThis Approach \u0026ldquo;Breaks\u0026rdquo; MVC # In Model-View-Controller there is a rule that says that the View is able to read the Presentation Model from the Model, but all changes to the Presentation Model must go through the Controller.\nRight there, you cannot build a Reactive system with MVC. Because a Reactive system demands that the UI can update State, and this is explicitly forbidden in MVC. From a JavaFX perspective, this means that you cannot bidirectionally bind the Presentation Model to the layout Nodes, nor can you bind unidirectionally from a Node to the Presentation Model.\nShould you care?\nProbably not. But, if you do ignore this rule, and if you implement your Presentation Model as an enclosing object around your State Properties (even if it is still contained within the Model), then you\u0026rsquo;re 90% of the way from MVC to MVCI. And you might as well use MVCI - it\u0026rsquo;s better for this stuff anyways.\nApplication Logic in a Reactive Design # Let\u0026rsquo;s look at the other end of the framework, the application logic\u0026hellip;\nIn MVC and MVVM, the application logic is in the Model. In MVCI the application logic is in the Interactor. MVC and MVCI are very similar in that application logic in both has access to the Presentation Model and can freely read and write from and to it. MVVM is different in that the Presentation Model is in the ViewModel and the Model doesn\u0026rsquo;t have open access to its elements. But that\u0026rsquo;s a different issue, and we can\u0026rsquo;t go into it here. So we\u0026rsquo;ll concentrate on MVCI, with the understanding that much of it applies to MVC as well.\nTypically, in MVCI you would pass a reference to the Presentation Model to the Interactor as a constructor parameter so that the Interactor can access it freely. Let\u0026rsquo;s take a look at how we might implement our counterUpdater logic:\nclass Interactor(private val model : PresentationModel) { fun updateCounter() : Int { return someService(model.counter.value, model.howMuch.value) } fun newCounterValue(newVal : Int) { model.counter.value = newVal model.howMuch.value = 1 } } For context, this would be called from something like this, in the Controller:\nfun updateCounter() { val task : Task\u0026lt;Int\u0026gt; = object : Task() { override fun call() : Int { return interactor.updateCounter() } } task.setOnSucceeded {evt -\u0026gt; interactor.newCounterValue(task.get())} Thread(task).start() } If you are familiar with Task then you\u0026rsquo;ll see that Interactor.updateCounter() is called on the background thread, and Interactor.newCounterValue() is called from FXAT. This latter method, therefore, is allowed to update Properties bound to the Scene Graph. You should also notice, that the Interactor doesn\u0026rsquo;t have any explicit exposure to the threads involved, even though it is logically divided up into methods that can or cannot be run on the FXAT.\nExposing the Properties to the Interactor # You might have noticed that in our Interactor we have lines like this:\nmodel.counter.value = newVal Clearly, to do this, the Interactor has to be aware of the Property nature of model.counter or, in other words, the Interactor is dependent on the implementation of model.counter as a Property. Can we limit this?\nYou can. You can create an Interface that essentially has the getters and setters for the values of the Properties and then pass the Presentation Model to the Interactor as that Interface. Like this:\ninterface InteractorModel { var counter: Int var howMuch: Int } class PresentationModel : InteractorModel { val counterProperty: IntegerProperty = SimpleIntegerProperty(0) val howMuchProperty: IntegerProperty = SimpleIntegerProperty(0) override var counter: Int get() = counterProperty.get() set(value) = counterProperty.set(value) override var howMuch: Int get() = howMuchProperty.get() set(value) = howMuchProperty.set(value) } This looks a bit different in Kotlin. We\u0026rsquo;ve defined Int fields in the Interface, which is allowed in Kotlin. Then we\u0026rsquo;ve implemented these fields in PresentationModel such that they delegate their set() and get() methods to the get() and set() methods of the respective IntegerProperties. Now, we can do this:\nclass Interactor(private val model: InteractorModel) { fun updateCounter() : Int { return someService(model.counter, model.howMuch) } fun newCounterValue(newVal : Int) { model.counter = newVal model.howMuch= 1 } } And the Interactor can only interact with model.counter and model.howMuch as Int. The layout code, would now use bindings to model.counterProperty and model.howMuchProperty.\nBut what if you want to prevent the layout from changing model.counter? That actually seems like a reasonable requirement. We could expose model.counterProperty as ObservableIntegerValue, but the layout is still going to be able to update model.counter. How do we prevent that?\nWe just do the same thing as we did for the Interactor, except instead of exposing the getters and setters, expose only the Properties:\ninterface LayoutModel { val counterProperty: ReadOnlyIntegerProperty val howMuchProperty: IntegerProperty } class PresentationModel : InteractorModel, LayoutModel { private val counterPropertyImpl: ReadOnlyIntegerWrapper = ReadOnlyIntegerWrapper(0) override val counterProperty: ReadOnlyIntegerProperty = counterPropertyImpl.readOnlyProperty override val howMuchProperty: IntegerProperty = SimpleIntegerProperty(0) override var counter: Int get() = counterPropertyImpl.get() set(value) = counterPropertyImpl.set(value) override var howMuch: Int get() = howMuchProperty.get() set(value) = howMuchProperty.set(value) } I\u0026rsquo;ve called the new Interface LayoutModel because ViewModel would get confusing. This Interface exposes howMuchProperty as IntegerProperty and counterProperty as ReadOnlyIntegerProperty. In the PresentationModel we now have a private IntegerProperty instantiated as ReadOnlyIntegerWrapper. Then we implement the Interface by instantiating counterProperty with counterPropertyImpl.getReadOnlyProperty().\nObviously, we pass PresentationModel to the layout builder class as LayoutModel and the layout builder only has acces to the fields as Properties, and counter is read-only.\nThe result is that the application logic treats the Presentation Model like set of normal Java types and objects, while the layout treats the same Presentation Model like a set of Observable objects that it can connect to the layout through Bindings and Listeners. In the end, the application logic performs, model.counter = newVal and the Label on the screen instantly changes.\nConclusion # I think that JavaFX gets missed as a Reactive GUI environment because it\u0026rsquo;s different from the other, commonly used systems like React.\nI spent a bit of time looking through the results of a web search for \u0026ldquo;JavaFX reactive\u0026rdquo;, and it wasn\u0026rsquo;t very encouraging. Most of the results seemed to deal with integrating Reactive tools like RxJava with JavaFX. This is an interesting subject, but only tangential to Reactive GUI design. Those results that did deal with JavaFX as a Reactive GUI environment consisted of superficial surveys of a few Observable classes and not much else.\nIt\u0026rsquo;s almost disappointing that you can build a working application using a declarative/imperative approach to GUI design with JavaFX, because that\u0026rsquo;s as far as most people seem to get. Add FXML into the mix, and Reactive design gets pushed even further into the background. I don\u0026rsquo;t think I\u0026rsquo;ve ever seen an example of an FXML based Reactive JavaFX application. They\u0026rsquo;re probably out there, but nobody seems to be talking about it.\nIt\u0026rsquo;s a shame, because, like so many things with JavaFX, once you know how to do it properly it becomes so much easier than doing it the \u0026ldquo;wrong\u0026rdquo; way. Reactive GUI applications in JavaFX are really that much simpler than declarative/imperative designs, have so much less coupling - all over the application - and are easier to maintain and enhance.\nWhen you integrate a Reactive design into MVCI (which is what it is designed for) you end up with a Presentation Model that is treated as a collection of Observables for binding at the layout end of the application, and treated as a collection of generic Java/Kotlin types and objects at the application logic. That Presentation Model becomes a conduit for each end of the application - View and Application Logic - to communicate with each other in the manner that makes the most sense to them, without knowing about each other. That allows them to interact without becoming coupled.\n","date":"16 July 2025","externalUrl":null,"permalink":"/javafx/techniques/reactive-javafx/","section":"JavaFX","summary":"What is Reactive programming, and how does it work in JavaFX","title":"Reactive Programming in JavaFX","type":"javafx"},{"content":" Introduction # From time to time the question about, \u0026ldquo;To FXML or not to FXML\u0026rdquo; comes up on Reddit and other social media, and I\u0026rsquo;m know as being firmly in the \u0026ldquo;Not FXML\u0026rdquo; camp. I\u0026rsquo;ve had a few people message me about this, so I thought it might be a good time to examine this topic in depth.\nFull disclosure: # I haven\u0026rsquo;t used SceneBuilder and FXML since about 2015, so my only experience with it was as a beginner to JavaFX and I have no intention of ever using it again.\nWhile I\u0026rsquo;m not an expert user of FXML, I am, however, very much aware of most of its capabilities and quirks because I read about them all the time. I\u0026rsquo;ve also seen tons of FXML when looking at other programmers\u0026rsquo; projects on GitHub, and helping people with issues on-line.\nI wouldn\u0026rsquo;t hesitate to say that I know more about FXML and SceneBuilder than the average JavaFX beginner or intermediate programmer. I just don\u0026rsquo;t use it.\nTrue or False - Claims About FXML # There are a lot of claims about all of the wonderous benefits of using FXML. Are they true? Let\u0026rsquo;s take a look at them and see\u0026hellip;\nSeparation of Concerns # This is the claim you hear the most, from the Oracle tutorial for FXML we have this:\nQuote FXML is an XML-based language that provides the structure for building a user interface separate from the application logic of your code. This separation of the presentation and application logic is attractive to web developers\nThere\u0026rsquo;s actually a lot to unpack in these two sentences. With FXML, we have two components, the FXML file and the FXML Controller, so any talk of \u0026ldquo;separation\u0026rdquo; has to speak to the relationship between these two elements. So let\u0026rsquo;s concentrate on that\u0026hellip;\nWhen we talk about \u0026ldquo;Separation of Concerns\u0026rdquo;, we are really talking about \u0026ldquo;coupling\u0026rdquo;, or rather, keeping low levels of coupling. And what this means is that you can make changes to one element of your application without having to make changes to another. Does FXML achieve this?\nSpecifically, for FXML, we are concerned about whether or not we can make changes to the FXML file without needing to make changes to the FXML Controller, and visa versa. What do these two components \u0026ldquo;know\u0026rdquo; about each other?\nMost FXML implementation that I see follow a basic pattern:\nEventHandler subroutines in the FXML Controller are exposed to the FXML file. Named Nodes are exposed to the FXML Controller through @FXML annotations. This is pretty easy. The FXML file needs to be able to invoke subroutines in the FXML Controller in order to actually do anything. So at a minimum, the FXML file is coupled to this aspect of the FXML Controller. You can make any changes that you like to the FXML Controller, as long as you don\u0026rsquo;t make critical changes to those subroutines which would impact the FXML file.\nThe other direction is tighter. If you want to get data in or out of any Node on the screen, you\u0026rsquo;ll need a typed reference to it declared as a field in your FXML Controller. This means that everything that isn\u0026rsquo;t purely static content in your GUI will need to be exposed to the FXML Controller. You have to have each one of those Nodes in your FXML file, and they have to be the correct type of Node or your Controller code will break. This also creates a dependency the other way, your FXML file now requires that the FXML Controller has some code that loads data into these Nodes.\nIs there some way to remove all this coupling? Obviously, no\u0026hellip; But can we do anything to make it \u0026ldquo;one way\u0026rdquo; coupling?\nIt turns out that we can. If we expose all of the elements that might generate Events that we want to handle in the FXML Controller to that Controller by giving them id\u0026rsquo;s in the FXML file, then we can configure them, including attaching EventHandlers to them, in the FXML Controller. If you do this then you don\u0026rsquo;t have any references from the FXML file to the FXML Controller.\nI haven\u0026rsquo;t looked at enough FXML systems to say definitively that this technique is never used, but\u0026hellip; I\u0026rsquo;ve never seen it used.\nWhat about in the other direction? Can we create an FXML Controller that doesn\u0026rsquo;t need any references to Nodes defined in the FXML file?\nThis turns out to be more complicated. It is possible to define data elements as Properties in your FXML Controller that are exposed to the FXML file, and can be used in bindings defined in the FXML file. The downside to this is that the FXML file is now even more tightly coupled to the FXML Controller, as it needs to know about all of the data elements available in the FXML Controller.\nExcept that this won\u0026rsquo;t work.\nFXML does not support bi-directional binding. This is something that apparently has been a \u0026ldquo;top priority\u0026rdquo; for over a decade, and it\u0026rsquo;s still not been implemented. Without bi-directional binding, you cannot update those Properties in the FXML Controller and you cannot get data out of your GUI. So this is a dead end.\nFinally, we haven\u0026rsquo;t even considered cases where you have dynamic layouts, or layouts that are dependent on data. These simply cannot be defined purely in FXML and will need code in the FXML Controller that performs this layout.\nNow, let\u0026rsquo;s go back to \u0026ldquo;Separation of Concerns\u0026rdquo;. What separation do we have?\nIn some cases, we have separation of most of the details of the actual layout, and the styling and configuration. For instance, the FXML file might be the only component that knows that a couple of Labels are displayed in a VBox or an HBox, but if those Labels have dynamic content, then the FXML Controller needs to know that they are Labels and what data needs to be stuffed into them.\nNote I highly doubt that this is the level of \u0026ldquo;separation\u0026rdquo; that programmers are looking for when they expect \u0026ldquo;Separation of Concerns\u0026rdquo;.\nBut that Oracle tutorial doesn\u0026rsquo;t even seem to be talking about this, it says:\nQuote provides the structure for building a user interface separate from the application logic of your code\nApplication logic? This leads to the next claim about the benefits of FXML\u0026hellip;\nFXML Supports MVC # Also from that Oracle tutorial for FXML:\nQuote From a Model View Controller (MVC) perspective, the FXML file that contains the description of the user interface is the view.\nIn MVC, the View isn\u0026rsquo;t just a layout, it\u0026rsquo;s a complete, independent user interface. That means it handles everything GUI related. This includes data binding and handling GUI events like resizing, mouse dragging and button clicks. When one of these events requires activity outside of the GUI, then it communicates with the Controller to initiate that activity.\nYou should understand that there are Events and there are \u0026ldquo;Actions\u0026rdquo;. Events are very specifically JavaFX GUI elements, and in FXML they are specified in the FXML file, and implemented in the FXML Controller (or they can be implemented and defined in the FXML Controller). \u0026ldquo;Actions\u0026rdquo; are a much more generic concept and aren\u0026rsquo;t necessarily GUI elements at all.\nI know that this sounds like nit-picking, but it\u0026rsquo;s an important practical consideration when talking about MVC. Let\u0026rsquo;s look at what typically happens when you click a Button that requires some business logic to be run\u0026hellip;\nThe first thing that happens is that the Button is disabled because you don\u0026rsquo;t want to be able to initiate a the action again before it completes. There may be other GUI set-up things you want to do, like clearing out some data from some Nodes, starting up a ProgressBar or putting up a \u0026ldquo;Please Wait\u0026rdquo; message. The next thing that happens is the action is run, often in a background thread. Once the action completes, the Presentation Model is updated as required. Finally, the Button is re-enabled, the ProgressBar is removed, and the \u0026ldquo;Please Wait\u0026rdquo; message is cleared. The first and last steps are 100% GUI activity and should belong solely to the View. Steps 2 and 3 are actions that should be taken by the Controller and the Model. However, if you consider FXML file alone to be the View, it has no way to actually perform steps 1 and 4. To implement steps 1 and 4, you\u0026rsquo;ll have to write code in the FXML Controller!\nWhat does this really mean?\nAs a result of the coupling - that cannot be removed - between the FXML file and the FXML Controller, these two elements have to be considered together as a single unit that comprises the MVC View! Not the MVC View and Controller.\nOnce you look at it this way, everything else makes much more sense and you don\u0026rsquo;t have the cognitive dissonance that results from treating the FXML and FXML Controller as View and MVC Controller.\nBut you don\u0026rsquo;t automatically get an MVC Controller, you\u0026rsquo;ll have to deliberately create one. We\u0026rsquo;ll look at this later.\nWarning This is the most dangerous of all of the false claims about FXML. That\u0026rsquo;s because if you buy into this then you\u0026rsquo;ll actually architect your system incorrectly.\nMultiple Layouts With One Controller # The idea that the layout is a self-contained element, separate from the GUI logic, suggests that it is possible to create a single FXML Controller that works with multiple FXML layouts. Also, that it\u0026rsquo;s possible to do the reverse, and create multiple FXML Controllers that all work with the same FXML layout file. Perhaps this second scenario is easier to picture. You could have a single layout for a CRUD type application, and have a different FXML Controller for \u0026ldquo;create\u0026rdquo;, \u0026ldquo;update\u0026rdquo;, \u0026ldquo;read\u0026rdquo; and \u0026ldquo;delete\u0026rdquo;.\nIn practice, I\u0026rsquo;m not sure that anyone does this, or that it would be a good idea. Essentially, you\u0026rsquo;d be coupling all of those FXML Controllers to each other through the FXML file. Meaning that if you needed to change the FXML file to accommodate some change to one of the FXML Controllers, it could impact all of the other FXML Controllers. The same thing happens in the reverse implementation where all of the FXML files are coupled through their shared FXML Controller.\nMy sense is that trying to do this makes your application more complicated, not simpler.\nBut the Layout is Separate From the Logic! # It\u0026rsquo;s important to recognize that \u0026ldquo;physically separate\u0026rdquo; and \u0026ldquo;uncoupled\u0026rdquo; are NOT the same thing. It should be clear that although the FXML file and the the FXML Controller are two distinctly separate things, they are, in fact, very, very, very tightly coupled. You literally cannot add any non-static element to your FXML file without making corresponding changes to the associated FXML Controller.\nSo what is the advantage to having the layout separate from the logic when both are still tightly coupled?\nI\u0026rsquo;m not sure that there is one.\nI guess you could argue that you can concentrate on the layout without worrying about the GUI logic. But is that actually an advantage? Personally, I prefer the exact opposite, and I\u0026rsquo;ll usually add one element at time to my layout while flushing out the support for it in the Model, Controller and Interactor as I go. In this way, my GUI is always fully operational in so far as the elements that have been implemented and any issues that arise from downstream concerns are addressed immediately. And that\u0026rsquo;s going a lot further than just building the GUI logic as I go.\nFXML`s Declarative Syntax # This is touted as a great advantage to FXML, that it has a declarative syntax with a hierarchical structure which reflects the structure of your scene graph.\nI think that this means that it is supposed to be easy to look at an FXML file and understand what it\u0026rsquo;s doing, and to quickly find the elements that you are interested in and to understand how they are configured and relate to each other.\nLet\u0026rsquo;s see if this is true\u0026hellip;\nWhile I would prefer not to include a huge FXML file, I think we need to look at one to understand this issue. So here\u0026rsquo;s the FXML file from Trinity on GitHub:\n\u0026lt;?import javafx.geometry.Insets?\u0026gt; \u0026lt;?import javafx.scene.control.*?\u0026gt; \u0026lt;?import javafx.scene.layout.*?\u0026gt; \u0026lt;AnchorPane fx:id=\u0026#34;root\u0026#34; style=\u0026#34;-fx-background-color: #00000000;\u0026#34; xmlns=\u0026#34;http://javafx.com/javafx\u0026#34; xmlns:fx=\u0026#34;http://javafx.com/fxml\u0026#34; fx:controller=\u0026#34;edu.jhuapl.trinity.javafx.controllers.ManifoldControlController\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;TabPane fx:id=\u0026#34;tabPane\u0026#34; tabClosingPolicy=\u0026#34;UNAVAILABLE\u0026#34;\u0026gt; \u0026lt;tabs\u0026gt; \u0026lt;Tab closable=\u0026#34;false\u0026#34; text=\u0026#34;UMAP\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;BorderPane fx:id=\u0026#34;majorPane\u0026#34; minHeight=\u0026#34;200.0\u0026#34; minWidth=\u0026#34;400.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;top\u0026gt; \u0026lt;/top\u0026gt; \u0026lt;center\u0026gt; \u0026lt;GridPane hgap=\u0026#34;10.0\u0026#34; vgap=\u0026#34;5.0\u0026#34; BorderPane.alignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;columnConstraints\u0026gt; \u0026lt;ColumnConstraints hgrow=\u0026#34;SOMETIMES\u0026#34; maxWidth=\u0026#34;288.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;206.0\u0026#34;/\u0026gt; \u0026lt;ColumnConstraints hgrow=\u0026#34;SOMETIMES\u0026#34; maxWidth=\u0026#34;380.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;380.0\u0026#34;/\u0026gt; \u0026lt;/columnConstraints\u0026gt; \u0026lt;rowConstraints\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;/rowConstraints\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Number of Components\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;numComponentsSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Number of Epochs\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;numEpochsSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Nearest Neighbors\u0026#34; GridPane.rowIndex=\u0026#34;4\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Negative Sample Rate\u0026#34; GridPane.rowIndex=\u0026#34;6\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Local Connectivity\u0026#34; GridPane.rowIndex=\u0026#34;8\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;nearestNeighborsSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;negativeSampleRateSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;7\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;localConnectivitySpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;9\u0026#34;/\u0026gt; \u0026lt;HBox spacing=\u0026#34;10.0\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;10\u0026#34; GridPane.rowSpan=\u0026#34;2\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Distance Metric\u0026#34;/\u0026gt; \u0026lt;ChoiceBox fx:id=\u0026#34;metricChoiceBox\u0026#34; prefWidth=\u0026#34;200.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER_LEFT\u0026#34; layoutX=\u0026#34;10.0\u0026#34; layoutY=\u0026#34;10.0\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Threshold (if applicable)\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;thresholdSpinner\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;\u0026gt; \u0026lt;editable\u0026gt;true\u0026lt;/editable\u0026gt; \u0026lt;valueFactory\u0026gt; \u0026lt;SpinnerValueFactory.DoubleSpinnerValueFactory min=\u0026#34;0.01\u0026#34; max=\u0026#34;1.0\u0026#34; initialValue=\u0026#34;0.1\u0026#34; amountToStepBy=\u0026#34;0.01\u0026#34;/\u0026gt; \u0026lt;/valueFactory\u0026gt; \u0026lt;/Spinner\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;Label text=\u0026#34;Repulsion Strength\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Minimum Distance\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Spread\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;4\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Op Mix Ratio\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;6\u0026#34;/\u0026gt; \u0026lt;Slider fx:id=\u0026#34;repulsionSlider\u0026#34; blockIncrement=\u0026#34;0.1\u0026#34; majorTickUnit=\u0026#34;0.1\u0026#34; max=\u0026#34;2.0\u0026#34; showTickLabels=\u0026#34;true\u0026#34; showTickMarks=\u0026#34;true\u0026#34; snapToTicks=\u0026#34;true\u0026#34; value=\u0026#34;1.0\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;Slider fx:id=\u0026#34;minDistanceSlider\u0026#34; blockIncrement=\u0026#34;0.1\u0026#34; majorTickUnit=\u0026#34;0.1\u0026#34; max=\u0026#34;0.6\u0026#34; showTickLabels=\u0026#34;true\u0026#34; showTickMarks=\u0026#34;true\u0026#34; snapToTicks=\u0026#34;true\u0026#34; value=\u0026#34;0.1\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;Slider fx:id=\u0026#34;spreadSlider\u0026#34; blockIncrement=\u0026#34;0.1\u0026#34; majorTickUnit=\u0026#34;0.1\u0026#34; max=\u0026#34;1.5\u0026#34; min=\u0026#34;0.5\u0026#34; showTickLabels=\u0026#34;true\u0026#34; showTickMarks=\u0026#34;true\u0026#34; snapToTicks=\u0026#34;true\u0026#34; value=\u0026#34;1.0\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;Slider fx:id=\u0026#34;opMixSlider\u0026#34; blockIncrement=\u0026#34;0.1\u0026#34; majorTickUnit=\u0026#34;0.1\u0026#34; max=\u0026#34;1.0\u0026#34; showTickLabels=\u0026#34;true\u0026#34; showTickMarks=\u0026#34;true\u0026#34; snapToTicks=\u0026#34;true\u0026#34; value=\u0026#34;0.5\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;7\u0026#34;/\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;15.0\u0026#34; GridPane.columnSpan=\u0026#34;2147483647\u0026#34; GridPane.halignment=\u0026#34;CENTER\u0026#34; GridPane.rowIndex=\u0026#34;12\u0026#34; GridPane.rowSpan=\u0026#34;2\u0026#34; GridPane.valignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#loadUmapConfig\u0026#34; prefWidth=\u0026#34;200.0\u0026#34; text=\u0026#34;Load New Config\u0026#34;/\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#saveUmapConfig\u0026#34; prefWidth=\u0026#34;200.0\u0026#34; text=\u0026#34;Save Current Config\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;useHypersurfaceButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;Use Hypersurface\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;useHyperspaceButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Use Hyperspace\u0026#34;/\u0026gt; \u0026lt;CheckBox fx:id=\u0026#34;verboseCheckBox\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Progress Output\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Button defaultButton=\u0026#34;true\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#project\u0026#34; prefWidth=\u0026#34;200.0\u0026#34; text=\u0026#34;Run UMAP\u0026#34;/\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#exportMatrix\u0026#34; prefWidth=\u0026#34;200.0\u0026#34; text=\u0026#34;Export TMatrix\u0026#34;/\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#saveProjections\u0026#34; prefWidth=\u0026#34;200.0\u0026#34; text=\u0026#34;Export Projections\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;2.0\u0026#34; left=\u0026#34;2.0\u0026#34; right=\u0026#34;2.0\u0026#34; top=\u0026#34;2.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;Label text=\u0026#34;Target Weight\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;8\u0026#34;/\u0026gt; \u0026lt;Slider fx:id=\u0026#34;targetWeightSlider\u0026#34; blockIncrement=\u0026#34;0.1\u0026#34; majorTickUnit=\u0026#34;0.1\u0026#34; max=\u0026#34;1.0\u0026#34; showTickLabels=\u0026#34;true\u0026#34; showTickMarks=\u0026#34;true\u0026#34; snapToTicks=\u0026#34;true\u0026#34; value=\u0026#34;0.5\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;9\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;25.0\u0026#34; left=\u0026#34;25.0\u0026#34; right=\u0026#34;25.0\u0026#34; top=\u0026#34;25.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/GridPane\u0026gt; \u0026lt;/center\u0026gt; \u0026lt;/BorderPane\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/Tab\u0026gt; \u0026lt;Tab closable=\u0026#34;false\u0026#34; text=\u0026#34;PCA\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;BorderPane fx:id=\u0026#34;majorPane\u0026#34; minHeight=\u0026#34;200.0\u0026#34; minWidth=\u0026#34;400.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;top\u0026gt; \u0026lt;/top\u0026gt; \u0026lt;center\u0026gt; \u0026lt;GridPane hgap=\u0026#34;10.0\u0026#34; vgap=\u0026#34;5.0\u0026#34; BorderPane.alignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;columnConstraints\u0026gt; \u0026lt;ColumnConstraints hgrow=\u0026#34;SOMETIMES\u0026#34; maxWidth=\u0026#34;288.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;200.0\u0026#34;/\u0026gt; \u0026lt;ColumnConstraints hgrow=\u0026#34;SOMETIMES\u0026#34; maxWidth=\u0026#34;380.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;200.0\u0026#34;/\u0026gt; \u0026lt;/columnConstraints\u0026gt; \u0026lt;rowConstraints\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;/rowConstraints\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Number of Components\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;numPcaComponentsSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;HBox spacing=\u0026#34;20.0\u0026#34; GridPane.rowIndex=\u0026#34;3\u0026#34; GridPane.rowSpan=\u0026#34;2\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;VBox spacing=\u0026#34;15.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Fit Start Index\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;fitStartIndexSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;VBox spacing=\u0026#34;15.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Fit End Index\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;fitEndIndexSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;pcaRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;PCA (EigenValue)\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Component Analysis Type\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;svdRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;Singular Value Decomposition\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;pcaUseHypersurfaceButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;Use Hypersurface\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;4\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;pcaUseHyperspaceButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Use Hyperspace\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Output Scaling Factor\u0026#34; GridPane.rowIndex=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;pcaScalingSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;6\u0026#34;/\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;10.0\u0026#34; GridPane.columnSpan=\u0026#34;2147483647\u0026#34; GridPane.rowIndex=\u0026#34;8\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Button defaultButton=\u0026#34;true\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#runPCA\u0026#34; prefWidth=\u0026#34;175.0\u0026#34; text=\u0026#34;Project Data\u0026#34;/\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#saveProjections\u0026#34; prefWidth=\u0026#34;175.0\u0026#34; text=\u0026#34;Export Projections\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;Label text=\u0026#34;Input Data Source\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;CheckBox fx:id=\u0026#34;rangedFittingCheckBox\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;Enabled Ranged Fitting (Experimental)\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;15.0\u0026#34; left=\u0026#34;15.0\u0026#34; right=\u0026#34;15.0\u0026#34; top=\u0026#34;15.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/GridPane\u0026gt; \u0026lt;/center\u0026gt; \u0026lt;/BorderPane\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/Tab\u0026gt; \u0026lt;Tab closable=\u0026#34;false\u0026#34; text=\u0026#34;Distances\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;BorderPane fx:id=\u0026#34;majorPane\u0026#34; minHeight=\u0026#34;200.0\u0026#34; minWidth=\u0026#34;400.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;top\u0026gt; \u0026lt;/top\u0026gt; \u0026lt;center\u0026gt; \u0026lt;GridPane hgap=\u0026#34;10.0\u0026#34; vgap=\u0026#34;5.0\u0026#34; BorderPane.alignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;columnConstraints\u0026gt; \u0026lt;ColumnConstraints hgrow=\u0026#34;SOMETIMES\u0026#34; maxWidth=\u0026#34;288.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;206.0\u0026#34;/\u0026gt; \u0026lt;ColumnConstraints fillWidth=\u0026#34;false\u0026#34; hgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;ColumnConstraints hgrow=\u0026#34;SOMETIMES\u0026#34; maxWidth=\u0026#34;380.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;380.0\u0026#34;/\u0026gt; \u0026lt;/columnConstraints\u0026gt; \u0026lt;rowConstraints\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;/rowConstraints\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Connector Thickness\u0026#34; GridPane.rowIndex=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;5.0\u0026#34; GridPane.rowIndex=\u0026#34;7\u0026#34; GridPane.rowSpan=\u0026#34;2\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Connector Color\u0026#34;/\u0026gt; \u0026lt;ColorPicker fx:id=\u0026#34;connectorColorPicker\u0026#34; editable=\u0026#34;true\u0026#34; minHeight=\u0026#34;40.0\u0026#34; prefWidth=\u0026#34;200.0\u0026#34; promptText=\u0026#34;Change the color of the 3D connector\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;connectorThicknessSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;6\u0026#34;/\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;20.0\u0026#34; GridPane.columnIndex=\u0026#34;2\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Collected Distances\u0026#34;/\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#clearAllDistances\u0026#34; prefWidth=\u0026#34;150.0\u0026#34; text=\u0026#34;Clear All\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;VBox spacing=\u0026#34;5.0\u0026#34; GridPane.rowSpan=\u0026#34;2\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Distance Metric\u0026#34;/\u0026gt; \u0026lt;TextField fx:id=\u0026#34;distanceMetricTextField\u0026#34; minHeight=\u0026#34;40.0\u0026#34; promptText=\u0026#34;Select Distance From ListView\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;ScrollPane fitToHeight=\u0026#34;true\u0026#34; fitToWidth=\u0026#34;true\u0026#34; pannable=\u0026#34;true\u0026#34; GridPane.columnIndex=\u0026#34;2\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34; GridPane.rowSpan=\u0026#34;2147483647\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;ListView fx:id=\u0026#34;distancesListView\u0026#34; prefHeight=\u0026#34;200.0\u0026#34; prefWidth=\u0026#34;200.0\u0026#34;\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/ListView\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/ScrollPane\u0026gt; \u0026lt;Separator orientation=\u0026#34;VERTICAL\u0026#34; prefHeight=\u0026#34;200.0\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowSpan=\u0026#34;2147483647\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;pointToGroupRadioButton\u0026#34; disable=\u0026#34;true\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;Point to Group\u0026#34; GridPane.rowIndex=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;pointToPointRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Point to Point\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;10.0\u0026#34; left=\u0026#34;10.0\u0026#34; right=\u0026#34;10.0\u0026#34; top=\u0026#34;10.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/GridPane\u0026gt; \u0026lt;/center\u0026gt; \u0026lt;/BorderPane\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/Tab\u0026gt; \u0026lt;Tab closable=\u0026#34;false\u0026#34; text=\u0026#34;Hull Geometry\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;BorderPane\u0026gt; \u0026lt;center\u0026gt; \u0026lt;VBox spacing=\u0026#34;5.0\u0026#34; BorderPane.alignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;20.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Generated Manifolds\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; layoutX=\u0026#34;15.0\u0026#34; layoutY=\u0026#34;15.0\u0026#34; spacing=\u0026#34;20.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#clearAll\u0026#34; prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Clear All\u0026#34;/\u0026gt; \u0026lt;Button layoutX=\u0026#34;100.0\u0026#34; layoutY=\u0026#34;10.0\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#exportAll\u0026#34; prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Export All\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;ScrollPane fitToHeight=\u0026#34;true\u0026#34; fitToWidth=\u0026#34;true\u0026#34; pannable=\u0026#34;true\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;ListView fx:id=\u0026#34;manifoldsListView\u0026#34;\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/ListView\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/ScrollPane\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/center\u0026gt; \u0026lt;top\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; BorderPane.alignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;GridPane maxHeight=\u0026#34;1.7976931348623157E308\u0026#34; maxWidth=\u0026#34;1.7976931348623157E308\u0026#34;\u0026gt; \u0026lt;columnConstraints\u0026gt; \u0026lt;ColumnConstraints halignment=\u0026#34;CENTER\u0026#34; hgrow=\u0026#34;ALWAYS\u0026#34; maxWidth=\u0026#34;-Infinity\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;/\u0026gt; \u0026lt;ColumnConstraints halignment=\u0026#34;CENTER\u0026#34; hgrow=\u0026#34;ALWAYS\u0026#34; maxWidth=\u0026#34;216.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;214.0\u0026#34;/\u0026gt; \u0026lt;ColumnConstraints halignment=\u0026#34;CENTER\u0026#34; hgrow=\u0026#34;ALWAYS\u0026#34; maxWidth=\u0026#34;145.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;118.0\u0026#34;/\u0026gt; \u0026lt;ColumnConstraints halignment=\u0026#34;CENTER\u0026#34; hgrow=\u0026#34;ALWAYS\u0026#34; maxWidth=\u0026#34;145.0\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;118.0\u0026#34;/\u0026gt; \u0026lt;/columnConstraints\u0026gt; \u0026lt;rowConstraints\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;ALWAYS\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;40.0\u0026#34; vgrow=\u0026#34;ALWAYS\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;40.0\u0026#34; vgrow=\u0026#34;ALWAYS\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; vgrow=\u0026#34;ALWAYS\u0026#34;/\u0026gt; \u0026lt;/rowConstraints\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label layoutX=\u0026#34;15.0\u0026#34; layoutY=\u0026#34;62.0\u0026#34; text=\u0026#34;Point Set\u0026#34;/\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.valignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;useVisibleRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; prefWidth=\u0026#34;75.0\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Visible\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;useAllRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; prefWidth=\u0026#34;75.0\u0026#34; text=\u0026#34;All\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;Label text=\u0026#34;Distance Tolerance\u0026#34; textAlignment=\u0026#34;CENTER\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;10.0\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;CheckBox fx:id=\u0026#34;automaticCheckBox\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Auto\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;manualSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;75.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;Label text=\u0026#34;Find by Label\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;ChoiceBox fx:id=\u0026#34;labelChoiceBox\u0026#34; maxWidth=\u0026#34;200.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34; GridPane.columnIndex=\u0026#34;1\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;Separator prefWidth=\u0026#34;200.0\u0026#34; GridPane.columnSpan=\u0026#34;2147483647\u0026#34; GridPane.rowIndex=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;20.0\u0026#34; GridPane.columnIndex=\u0026#34;2\u0026#34; GridPane.columnSpan=\u0026#34;2\u0026#34; GridPane.halignment=\u0026#34;CENTER\u0026#34; GridPane.rowSpan=\u0026#34;3\u0026#34; GridPane.valignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#generate\u0026#34; prefWidth=\u0026#34;175.0\u0026#34; text=\u0026#34;Generate\u0026#34;/\u0026gt; \u0026lt;Button mnemonicParsing=\u0026#34;false\u0026#34; onAction=\u0026#34;#clusterBuilder\u0026#34; prefWidth=\u0026#34;175.0\u0026#34; text=\u0026#34;Cluster Tools\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;10.0\u0026#34; left=\u0026#34;10.0\u0026#34; right=\u0026#34;10.0\u0026#34; top=\u0026#34;10.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/GridPane\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;/top\u0026gt; \u0026lt;left\u0026gt; \u0026lt;VBox spacing=\u0026#34;10.0\u0026#34; BorderPane.alignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Selected Manifold Properties\u0026#34;/\u0026gt; \u0026lt;TitledPane collapsible=\u0026#34;false\u0026#34; text=\u0026#34;Material\u0026#34; VBox.vgrow=\u0026#34;ALWAYS\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;VBox spacing=\u0026#34;5.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Diffuse Color\u0026#34;/\u0026gt; \u0026lt;ColorPicker fx:id=\u0026#34;manifoldDiffuseColorPicker\u0026#34; editable=\u0026#34;true\u0026#34; prefHeight=\u0026#34;50.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Wire Mesh Color\u0026#34;/\u0026gt; \u0026lt;ColorPicker fx:id=\u0026#34;manifoldWireMeshColorPicker\u0026#34; editable=\u0026#34;true\u0026#34; prefHeight=\u0026#34;50.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Specular Color\u0026#34;/\u0026gt; \u0026lt;ColorPicker fx:id=\u0026#34;manifoldSpecularColorPicker\u0026#34; editable=\u0026#34;true\u0026#34; prefHeight=\u0026#34;50.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/TitledPane\u0026gt; \u0026lt;TitledPane collapsible=\u0026#34;false\u0026#34; layoutX=\u0026#34;10.0\u0026#34; layoutY=\u0026#34;396.0\u0026#34; text=\u0026#34;MeshView\u0026#34; VBox.vgrow=\u0026#34;ALWAYS\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;GridPane\u0026gt; \u0026lt;columnConstraints\u0026gt; \u0026lt;ColumnConstraints hgrow=\u0026#34;SOMETIMES\u0026#34; maxWidth=\u0026#34;-Infinity\u0026#34; minWidth=\u0026#34;10.0\u0026#34; prefWidth=\u0026#34;300.0\u0026#34;/\u0026gt; \u0026lt;/columnConstraints\u0026gt; \u0026lt;rowConstraints\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;SOMETIMES\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;SOMETIMES\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;SOMETIMES\u0026#34;/\u0026gt; \u0026lt;/rowConstraints\u0026gt; \u0026lt;children\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;15.0\u0026#34; GridPane.halignment=\u0026#34;CENTER\u0026#34; GridPane.valignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;75.0\u0026#34; text=\u0026#34;Cull Face\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;frontCullFaceRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; prefWidth=\u0026#34;70.0\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Front\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;backCullFaceRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;Back\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;noneCullFaceRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;None\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;15.0\u0026#34; GridPane.halignment=\u0026#34;CENTER\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34; GridPane.valignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;75.0\u0026#34; text=\u0026#34;Draw Mode\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;fillDrawModeRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; prefWidth=\u0026#34;70.0\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Fill\u0026#34;/\u0026gt; \u0026lt;RadioButton fx:id=\u0026#34;linesDrawModeRadioButton\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; text=\u0026#34;Lines\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;10.0\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;CheckBox fx:id=\u0026#34;showWireframeCheckBox\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Show Wire Frame\u0026#34;/\u0026gt; \u0026lt;CheckBox fx:id=\u0026#34;showControlPointsCheckBox\u0026#34; mnemonicParsing=\u0026#34;false\u0026#34; selected=\u0026#34;true\u0026#34; text=\u0026#34;Show Control Points\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/GridPane\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/TitledPane\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;5.0\u0026#34; left=\u0026#34;5.0\u0026#34; right=\u0026#34;5.0\u0026#34; top=\u0026#34;5.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/left\u0026gt; \u0026lt;/BorderPane\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/Tab\u0026gt; \u0026lt;/tabs\u0026gt; \u0026lt;/TabPane\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/AnchorPane\u0026gt; All right, maybe if you\u0026rsquo;re an FXML expert user you can look at these 462 lines of FXML and make some sense out of it at a glance\u0026hellip;but I can\u0026rsquo;t.\nI don\u0026rsquo;t really know anything about the project this came from. I don\u0026rsquo;t know if it would be considered \u0026ldquo;good\u0026rdquo; FXML or not. I don\u0026rsquo;t know if the author was an expert or a beginner. My sense is that it is fairly typical, and is probably what came out of SceneBuilder.\nWhat I can say for sure is that it\u0026rsquo;s monolithic. SceneBuilder/FXML tends to push programmers in this direction, because I see this a lot. It is possible to carve a layout up into smaller parts, but the FXML management then becomes a headache all in itself. So it\u0026rsquo;s rarely done.\nThis layout has everything encased in an AnchorPane that has only one child, a TabPane, but it has no anchoring specified. Once again, this is something the SceneBuilder seems to encourage because you see it a lot. Like, really\u0026hellip;a lot. The AnchorPane appears to have no purpose in the layout, but you have to scroll through the entire file to see that it only has the TabPane in it. You also have to scroll through the whole file to see that the TabPane has 4 Tabs defined.\nLet\u0026rsquo;s look at the first GridPane defined. It has this block of RowConstraints\n\u0026lt;rowConstraints\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;RowConstraints minHeight=\u0026#34;10.0\u0026#34; prefHeight=\u0026#34;30.0\u0026#34; vgrow=\u0026#34;NEVER\u0026#34;/\u0026gt; \u0026lt;/rowConstraints\u0026gt; Yeah, you have to count them. There\u0026rsquo;s 15 and they are all the same. You cannot do DRY with FXML. Need to change the height of all of them? Edit 15 lines (or do it 15 times in SceneBuilder). Needless to say, you wouldn\u0026rsquo;t do anything this crude in code. And there are 26 more GridPane rows scattered throughout this file that use the same parameters!\nMoving on to the content of the GridPane, we\u0026rsquo;ll just look at a small section:\n\u0026lt;Label text=\u0026#34;Number of Components\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;numComponentsSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;1\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Number of Epochs\u0026#34; GridPane.rowIndex=\u0026#34;2\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;numEpochsSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;3\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Nearest Neighbors\u0026#34; GridPane.rowIndex=\u0026#34;4\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Negative Sample Rate\u0026#34; GridPane.rowIndex=\u0026#34;6\u0026#34;/\u0026gt; \u0026lt;Label text=\u0026#34;Local Connectivity\u0026#34; GridPane.rowIndex=\u0026#34;8\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;nearestNeighborsSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;5\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;negativeSampleRateSpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;7\u0026#34;/\u0026gt; \u0026lt;Spinner fx:id=\u0026#34;localConnectivitySpinner\u0026#34; editable=\u0026#34;true\u0026#34; prefWidth=\u0026#34;100.0\u0026#34; GridPane.rowIndex=\u0026#34;9\u0026#34;/\u0026gt; First off, I guess we have to assume that the first Label goes in row/colum 0/0, because it has nothing specified. I\u0026rsquo;m guessing this is what SceneBuilder does by default???\nThe Spinner that goes with that first Label is actually declared in the next line, as well as the next Label/Spinner combination. After that we get the next three Labels and then we find that the Spinners that go along with them follow after the last Label. The rest of the GridPane seems to go more like that. Perhaps this is simply the order in which the contents were added in SceneBuilder??\nI wonder what\u0026rsquo;s in all these Spinners? Integers? Decimals? What are there ranges? How do they increment, and what are the default values? Do they change dynamically? You can\u0026rsquo;t tell any of this without looking at the FXML Controller, because clearly that\u0026rsquo;s where this stuff is defined. I don\u0026rsquo;t know if you can define these parameters in SceneBuilder, but the author has chosen not to here. This is probably fairly common. BTW: The associated FXML Controller in this project is nearly 800 lines long.\nWhat can see here from these 462 lines of FXML is that the assertion that the \u0026ldquo;declarative, hierarchical syntax\u0026rdquo; of FXML is somehow easy to read, understand and maintain is just not true.\nI think it\u0026rsquo;s clear that well structured and organized layout code is infinitely easier to read, understand and maintain than the equivalent FXML.\nWhat Are the Actual Benefits of FXML? # As far as I can tell, there is only one: You can use SceneBuilder.\nIt\u0026rsquo;s up to you to decide if SceneBuilder is something that you find value in. It\u0026rsquo;s entirely possible that the drag-and-drop approach of SceneBuilder makes it easier for you to visualize how your layouts will work as you construct them.\nA lot of people mention that they find the ability to quickly run a simulation of the layout in SceneBuilder to be valuable. For me, I find it just as easy to run the code and see it actually running. While I imagine that SceneBuilder is probably a bit faster, I don\u0026rsquo;t find that incremental builds in Gradle take very long at all, and I have never hesitated to launch a test run because I didn\u0026rsquo;t want to spend time waiting.\nI have worked on large systems where the specific screens I am developing are deep down a workflow that a significant amount of time to navigate to in the actual application. I those cases, I wouldn\u0026rsquo;t develop a screen layout \u0026ldquo;in situ\u0026rdquo;, I\u0026rsquo;d whip up a mini-application for testing the layout (and the Model, Controller and Interactor for that screen).\nThe downside of ScreenBuilder is that it strongly encourages you to develop the layout in isolation. In reality, the layout needs to work hand-in-glove with the Presentation Model. I like to let the GUI dictate the needs of the Presentation Model, and create the relationships between the Nodes in the layout and the Properties in the Presentation Model as I build the layout itself. In this way, I don\u0026rsquo;t just create the layout, I create a good part of the entire MVCI construct that it\u0026rsquo;s part of at the same time.\nSceneBuilder as a Beginners\u0026rsquo; Tool # One of the biggest problems that I have with JavaFX is that it is a huge, complicated library with a nearly complete lack of good tutorial material. This is an absolute nightmare for beginners.\nThere is this idea that SceneBuilder, as a graphical, drag-and-drop interface for JavaFX GUI design, somehow makes JavaFX more accessible to beginners. I seem to remember that Oracle touted SceneBuilder as huge benefit to switching over from Swing in the early days.\nBut for a beginner, starting up SceneBuilder is probably like sitting in the cockpit of a modern jet fighter when you\u0026rsquo;ve never even flown a kite before. There are a dizzying array of Controls you can use, and a head-spinning number of options that you can fiddle with for each one. And there\u0026rsquo;s no explanations for any of it (at least there weren\u0026rsquo;t years ago).\nMy main memory of SceneBuilder is the countless hours that I frittered away trying to make something\u0026hellip;anything work the way I wanted it to. I was lucky enough that I was on the clock, and I quickly realized that I couldn\u0026rsquo;t keep throwing this time away when I was supposed to be productive. So we decided to abandon SceneBuilder after a few months. Things got better after that.\nI often see comments from beginners that indicate that they are having the same experience. I\u0026rsquo;ve also seen the the resultant FXML that beginners create with SceneBuilder, which are often full of silly things like wrapping the whole layout in an AnchorPane with a single child and no anchors.\nDealing With the Complexity of FXML # Beginners, by definition, don\u0026rsquo;t understand what they are doing and there\u0026rsquo;s always a very big element of \u0026ldquo;paint by numbers\u0026rdquo; in their work. This is especially true because the SceneBuilder -\u0026gt; FXMLLoader -\u0026gt; FXML Controller process seems to add some layer of inpenetrable magic that they cannot get past. It\u0026rsquo;s clear that most beginners don\u0026rsquo;t even understand that the output of FXMLLoader.load() is just a Node subclass of some sort. They only ever see it referred to as root and dropped immediately into a Scene, so they think that\u0026rsquo;s all you can do with it.\nImportant Once you realize that FXML itself doesn\u0026rsquo;t actually give you anything of value, you can easily see it instead as the cost of using SceneBuilder.\nOver the decades, I\u0026rsquo;ve used all kinds of different screen generators in different systems, I\u0026rsquo;ve even written one myself for a \u0026ldquo;green screen\u0026rdquo; system. They all have to include some method to allow programmers to handle the stuff that doesn\u0026rsquo;t neatly fall into values and parameters that can be easily included in the screen building tool. Some use \u0026ldquo;hooks\u0026rdquo;. Some provide a place to enter code and subroutines. Some just generate code that you can modify yourself - I think Swing had one of those.\nSceneBuilder has FXML and the FXML Controller, along with the FXMLLoader that ties them together. That\u0026rsquo;s how it deals with things that you cannot easily define in SceneBuilder. It introduces a layer of complexity that you do not have with coded layouts.\nLet\u0026rsquo;s face it. Complexity is a huge issue with JavaFX, even without FXML.\nThink about some beginner who scratches his head for hours trying to figure out why that Node where he\u0026rsquo;s set visible to false is still taking up space in his layout. Why would they think they would need to set managed to false as well? How would they even know what managed meant? How are they supposed to cope with the magic that happens inside FXMLLoader?\nIt\u0026rsquo;s clear that beginners have difficulty understanding how elements inside an FXML Controller relate to the associated layout. It\u0026rsquo;s also clear that they have trouble understanding how to apply JavaFX concepts to an FXML/FXML Controller context, and that they even have trouble applying basic Java concepts to that context.\nThe Cost of FXML # The first cost of FXML, as you can see from that giant FXML example above, is that it actually makes it more difficult to read and understand how a layout works than the equivalent in well constructed and organized code.\nBut the most important cost of FXML is that it makes it very difficult, if not impossible, to develop a library of helpers, builders and factory methods (or the equivalent in classes) that simplify and standardize layout creation the way that you approach it.\nProgrammer complaints about JavaFX seem to revolve around one of two things: Too much boilerplate, or the lack of some particular Node or Control class.\nWhat do people mean by \u0026ldquo;boilerplate\u0026rdquo; with regards to JavaFX?\nUsually, it centres around the idea that you have to instantiate a Node as a variable, and then call several of its methods to configure it. Then put the Node into the layout. Something like this:\nLabel label = new Label(); label.getStyleClass().add(\u0026#34;some-selector\u0026#34;); label.textProperty().bind(model.someProperty()); HBox hBox = new HBox(10, label, otherNode); If you have ten Labels in your layout, and they are all configured in a similar way, then you\u0026rsquo;ve got 30 lines of boilerplate, which bloats your layout code and makes it difficult to read.\nBut you don\u0026rsquo;t have to live with this. You can create a static builder method that will do this for you:\npublic class Labels { public static Label promptLabel(ObseravbleStringProperty boundProperty) { Label label = Label(); label.textProperty().bind(boundProperty); label.getStyleClass().add(\u0026#34;some-selector\u0026#34;); return label; } } And you can call it like this:\nHBox hBox = new HBox(10, Labels.promptLabel(model.someProperty()),.....); And there you go, no more boilerplate. You can do this locally, as a method in your ViewBuilder class, or you can assemble a library of these methods that you put into a separate project.\nYou can do some of this stuff with FXML. You obviously cannot use builders, but you can create custom classes that compile into a jar file that you import into SceneBuilder to do some of the same things.\nWhich leads to the second point, missing Node classes\u0026hellip;\nThere are many instances in JavaFX where the only tools that we are given are very generic. Take text entry as an example. We have TextField.\nNow, TextField by itself is just a box that you can type anything into. But TextField supports the inclusion of a TextFormatter which allows you to take complete control over the input into a TextField. You can create TextFormatters for money amounts, integer amounts, postal codes, or phone numbers, or anything else you can think of. Personally, I wouldn\u0026rsquo;t use TextField without a TextFormatter for anything other than truly free-form text entry.\nLet\u0026rsquo;s look at phone numbers which have different formats all over the world. In Canada and the US we use something like \u0026ldquo;#(###)###-####\u0026rdquo;. In France they use \u0026ldquo;\u0026rdquo;## # ## ## ## ##\u0026quot;, while in Ukraine they use \u0026ldquo;### ## ### ####\u0026rdquo;, and in China it\u0026rsquo;s, \u0026ldquo;## ### #### ####\u0026rdquo;. And in many of these places, if not all, the format is different for calling in-country.\nOne possible implementation is to have a different TextFormatter for every phone number layout. TextFormatter is a Property of TextField, so it is possible to change the TextFormatter on-the-fly, or bind it to some other value. This suggests some possible implementation approaches. Alternatively, you could create builder methods to return specific styles of phone number entry. For instance: I feel that seeing one can make my point\nchildren += HBox(10.0, promptLabel(\u0026#34;Phone Number:\u0026#34;), frenchPhoneNumberTF(phoneNumber))) And now none of the mechanics of setting up the TextField are exposed to the layout code at all. Alternatively,\nchildren += HBox(10.0, promptLabel(\u0026#34;Phone Number:\u0026#34;), phoneNumberTF(countryCode, phoneNumber))) works too.\nIf you were in a domain where you really did need a range of different formats based on the country code, you\u0026rsquo;d probably be tempted to have a ComboBox for the country selection, and then a TextField for the phone number. You see this on websites all the time. In this case, I\u0026rsquo;d probably create a custom control that had both the ComboBox and the TextField in it, along with their associated prompt Labels, and the connections between the country code and the TextFormatter could be established internally. You could include a Property\u0026lt;Orientation\u0026gt; which would determine if the elements were layed out horizontally or vertically.\nYou\u0026rsquo;d use it like this:\nchildren += PhoneNumberEntryBox(countryCode, phoneNumber, orientation))) Can you do this with FXML?\nSomewhat. You cannot access the builder approach, but you could create FrenchPhoneNumberTF class and use it. You can reference PhoneNumberEntryBox, but you cannot connect the countryCode or phoneNumber parameters via FXML. Nor can you access the orientation parameter in FXML. In truth, you could make orientation a StlyeableProperty and have a custom attribute for it in a CSS file, which might be just as easy.\nBut here\u0026rsquo;s the thing: Anyone that spends a lot of time writing JavaFX applications is going to encounter the same use cases, that require the same customized or custom Controls, over and over again. Integrating them into your layouts is trivial - trivial to the point where they might as well be native Nodes in the JavaFX library - when you code your layouts by hand. It\u0026rsquo;s much more difficult to do the same with FXML.\nThe Importance of Builders and Custom Controls # I simply cannot over-emphasize the importance and value of this.\nThe core concept is that there is layout, and there is configuration, and there is logic.\nConfiguration is where the boilerplate and repeated code lives. Simply refuse to repeat yourself - apply DRY contstantly and consistently - and you\u0026rsquo;ll find yourself extracting the configuration out of your layouts. This leaves the layouts to be\u0026hellip;layout.\nThis is the really big issue (in my opinion) with FXML. It splits the logic out from layout, but leaves much of the configuration in the layout. And the rest of the configuration gets mingled in with the logic.\nAnd the truth is that almost all of the configuration is a small set of paramaters that are always combined in the same way for each use case. And the use cases are limited in number.\nIn the real layout code that I write, you\u0026rsquo;ll almost never see me use something like, new Label(). That\u0026rsquo;s because I\u0026rsquo;m almost always going to configure that Label in one of four or five ways, and I have builders for each of them. I do the same thing for many other Controls, too. I have custom TableColumns that have TableCells for a number of data types like dates, money, integers and booleans.\nWhen you look at layout code, you don\u0026rsquo;t care about the details of how a Label created through a call to Labels.dataLabel(model.someProperty) is configured. You know it\u0026rsquo;s a Label and it\u0026rsquo;s configured to display data. You also know that Labels.dataLabel() has been used hundreds of times, is thoroughly tested, and works properly. So it\u0026rsquo;s not likely to be a source of any problem you\u0026rsquo;re having.\nBecause of this, it\u0026rsquo;s not just code that\u0026rsquo;s not cluttering up your layouts, it\u0026rsquo;s not cluttering up your mind either. Do you worry about how new Label(\u0026quot;Something\u0026quot;) works? No, of course not. Nor do you worry about Labels.dataLabel().\nFXML With a Framework # If you are going to develop real applications that actually do something, and you want to do it right, you\u0026rsquo;ll need to adopt one of the commonly used frameworks like MVC, MVVM or my own MVCI. These frameworks all isolate your UI logic from your business (or application) logic, and provide the \u0026ldquo;separation of concerns\u0026rdquo; that make systems easier to maintain and enhance.\nWe now know that FXML doesn\u0026rsquo;t give you a head start on MVC, because the FXML Controller is not an MVC Controller. So here we\u0026rsquo;ll look at how you would go about implementing a framework in both an FXML and non-FXML application, and see how they are different\u0026hellip;\n{% include notice_kotlin %}\nIf I go into Intellij Idea and use the \u0026ldquo;New -\u0026gt; Project\u0026hellip;\u0026rdquo; wizard to create a JavaFX project, I\u0026rsquo;ll get a \u0026ldquo;Hello World\u0026rdquo; application that uses FXML. So let\u0026rsquo;s do that and see what it looks like:\nclass HelloApplication : Application() { override fun start(stage: Stage) { val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource(\u0026#34;hello-view.fxml\u0026#34;)) val scene = Scene(fxmlLoader.load(), 320.0, 240.0) stage.title = \u0026#34;Hello!\u0026#34; stage.scene = scene stage.show() } } fun main() { Application.launch(HelloApplication::class.java) } and the FXML Controller looks like this:\nclass HelloController { @FXML private lateinit var welcomeText: Label @FXML private fun onHelloButtonClick() { welcomeText.text = \u0026#34;Welcome to JavaFX Application!\u0026#34; } } while the FXML file looks like this:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;?import javafx.geometry.Insets?\u0026gt; \u0026lt;?import javafx.scene.control.Label?\u0026gt; \u0026lt;?import javafx.scene.layout.VBox?\u0026gt; \u0026lt;?import javafx.scene.control.Button?\u0026gt; \u0026lt;VBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;20.0\u0026#34; xmlns:fx=\u0026#34;http://javafx.com/fxml\u0026#34; fx:controller=\u0026#34;ca.pragmaticcoding.fxmlstuff.HelloController\u0026#34;\u0026gt; \u0026lt;padding\u0026gt; \u0026lt;Insets bottom=\u0026#34;20.0\u0026#34; left=\u0026#34;20.0\u0026#34; right=\u0026#34;20.0\u0026#34; top=\u0026#34;20.0\u0026#34;/\u0026gt; \u0026lt;/padding\u0026gt; \u0026lt;Label fx:id=\u0026#34;welcomeText\u0026#34;/\u0026gt; \u0026lt;Button text=\u0026#34;Hello!\u0026#34; onAction=\u0026#34;#onHelloButtonClick\u0026#34;/\u0026gt; \u0026lt;/VBox\u0026gt; This is good enough for an example.\nNow, let\u0026rsquo;s look at what the exact same application would look like without FXML:\nclass HelloNoFXML : Application() { override fun start(stage: Stage) { with(stage) { title = \u0026#34;Hello!\u0026#34; scene = Scene(createContent(), 320.0, 240.0) show() } } private fun createContent(): Region = VBox(20.0).apply { padding = Insets(20.0) alignment = Pos.CENTER val welcomeText = Label() children += welcomeText children += Button(\u0026#34;Hello \u0026#34;).apply { onAction = EventHandler { welcomeText.text = \u0026#34;Welcome to JavaFX Application!\u0026#34; } } } } fun main() { Application.launch(HelloNoFXML::class.java) } And you can count and see that both versions have 20 lines of Kotlin code, but the FXML version also has 8 lines of layout FXML.\nIn my opinion, the non-FXML version has the advantage that it\u0026rsquo;s incredibly staight-forward. You can look at it and you can see that createContent() is a builder, and that everything in it is just plain, vanilla Kotlin code. You can see instantly how the action of the Button relates directly to the Label and how they are both in a VBox with a specific alignment, padding and spacing.\nBut now, let\u0026rsquo;s imagine that we are going to something more involved with this. We\u0026rsquo;re going to connect this up to some database or external API that will do things for us, and we are going to reflect the results of this external action in our GUI. For this simple example, we\u0026rsquo;ll just do this via the message in the Label. This means that we\u0026rsquo;ll need to implement a framework, in this case I\u0026rsquo;m going to use my own MVCI (Model-View-Controller-Interactor).\nIn the non-FXML version, this is pretty simple. The first thing I\u0026rsquo;ll do is create a Model:\nclass HelloModel { val welcomeMessage: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) } I\u0026rsquo;ll split that createContent() method out to be the build() method in a Builder:\nclass HelloViewBuilder(private val model: HelloModel) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region = VBox(20.0).apply { padding = Insets(20.0) alignment = Pos.CENTER children += Label().apply { textProperty().bind(model.welcomeMessage) } children += Button(\u0026#34;Hello \u0026#34;).apply { onAction = EventHandler { model.welcomeMessage.value = \u0026#34;Welcome to JavaFX Application!\u0026#34; } } } } Then we\u0026rsquo;ll introduce a Controller:\nclass HelloMvciController { private val model: HelloModel = HelloModel() private val viewBuilder = HelloViewBuilder(model) fun getView() = viewBuilder.build() } And we\u0026rsquo;ll link it back into the Application:\nclass HelloNoFXML : Application() { override fun start(stage: Stage) { with(stage) { title = \u0026#34;Hello!\u0026#34; scene = Scene(HelloMvciController().getView(), 320.0, 240.0) show() } } } You can see that this is still fundamentally the same thing. We\u0026rsquo;ve split the contents of the Label off into the Model, but this actually improves on the original code because now the Button refers to the Model, and has no reference to the Label, and we no longer need a variable for it. This removes internal coupling between the layout Nodes.\nBut now we need some business logic that will simulate some sort of access to an external API to fetch some data, and this will require an Interactor:\nclass HelloInteractor(private val model: HelloModel) { fun getWelcome(): Unit { model.welcomeMessage.value = \u0026#34;Hello from the Interactor\u0026#34; } } This doesn\u0026rsquo;t actually do anything special, but if you had some code that needed to access an external database or API, this is where you would put it.\nTo use this, we\u0026rsquo;ll need to update the Controller and the View to handle an \u0026ldquo;action\u0026rdquo; triggered by the View (internally from the Button). First the ViewBuilder:\nclass HelloViewBuilder(private val model: HelloModel, private val actionHandler : () -\u0026gt; Unit) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region = VBox(20.0).apply { padding = Insets(20.0) alignment = Pos.CENTER children += Label().apply { textProperty().bind(model.welcomeMessage) } children += Button(\u0026#34;Hello \u0026#34;).apply { onAction = EventHandler { actionHandler.invoke() } } } } The ViewBuilder now as an additional constructor parameter that takes the Kotlin equivalent of Runnable. It now just invokes the Runnable when the Button is activated.\nAnd then the Controller:\nclass HelloMvciController { private val model: HelloModel = HelloModel() private val interactor = HelloInteractor(model) private val viewBuilder = HelloViewBuilder(model) {interactor.getWelcome()} fun getView() = viewBuilder.build() } Now, when the Button is activated, the Interactor\u0026rsquo;s getWelcome() method is invoked by the Controller and the Model is updated. It\u0026rsquo;s important to understand that the EventHandler itself is very much a GUI component - it responds to JavaFX events - while the \u0026ldquo;action\u0026rdquo; is actually GUI agnostic. The JavaFX nature of the EventHandler has been stripped away, and we\u0026rsquo;re just communicating via a generic Kotlin\\Java functional interface.\nIf we needed to disable and the later re-enable the Button, we\u0026rsquo;d define that in the View, and pass the re-enabling code to the Controller as a Runnable. The original Runnable would be transformed to a Consumer\u0026lt;Runnable\u0026gt;. All of this still remains just generic Kotlin\\Java function elements.\nThis is just a silly example, but in real life, the Controller would handle any threading issues, and the Interactor would connect to any external databases or API\u0026rsquo;s to do whatever it needed.\nBut regardless of what that was, the View wouldn\u0026rsquo;t change at all. Which is the point of using a framework like MVCI - it keeps the business logic far away from the UI logic.\nAs for the FXML\u0026hellip;\nTheoretically, we should be able to treat the FXML file, the FXMLController and the FXMLLoader as a View in MVCI. Let\u0026rsquo;s see how that would work.\nThe getView() method in the Controller would now call the FXMLLoader to create the view:\nclass HelloMvciControllerFxml { private val model: HelloModel = HelloModel() private val interactor = HelloInteractor(model) fun getView() : Region { val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource(\u0026#34;hello-view.fxml\u0026#34;)) return fxmlLoader.load() } } Then we can update the Application to work the same way that the non-FXML version worked:\nclass HelloApplication : Application() { override fun start(stage: Stage) { stage.title = \u0026#34;Hello!\u0026#34; stage.scene = Scene(HelloMvciControllerFxml().getView(), 320.0, 240.0) stage.show() } } And it runs just like it did before.\nBut now it gets more complicated. We need to get the Model into the FXML Controller, and we need to modify the FXML Controller such that it can call our MVCI controller to perform the action. The first thing to try is to grab the FXML Controller from the FXMLLoader, and then update it with these references:\nclass HelloMvciControllerFxml { private val model: HelloModel = HelloModel() private val interactor = HelloInteractor(model) fun getView(): Region { val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource(\u0026#34;hello-view.fxml\u0026#34;)) val view: Region = fxmlLoader.load() val fxmlController = fxmlLoader.getController() as HelloController fxmlController.setModel(model) fxmlController.setAction { interactor.getWelcome() } return view } } Let\u0026rsquo;s look at the FXML Controller:\nclass HelloController { @FXML private lateinit var welcomeText: Label private var actionHandler: (() -\u0026gt; Unit)? = null fun setModel(model: HelloModel) { welcomeText.textProperty().bind(model.welcomeMessage) } fun setAction(newActionHandler: () -\u0026gt; Unit) { actionHandler = newActionHandler } @FXML private fun onHelloButtonClick() { actionHandler?.invoke() } } The problem with this approach is that you have to deal with the fact that the Model is not present in the FXML Controller at the time that the FXML Controller is instantiated by the FXMLLoader. This means that you cannot perform setup operations in initialize() as you ordinarily would. In this example, I\u0026rsquo;ve loaded all that stuff (well, it\u0026rsquo;s just one thing but you get the idea) into setModel(), but that might not be the best approach.\nFor the action handler, I\u0026rsquo;ve taken a different approach and created it as a field in the FXML Controller which is initially null. Then it\u0026rsquo;s set later by the MVCI Controller after the FXMLLoader.load() has been invoked. The downside to this is that every reference to actionHandler has to deal with the possible null value. On the upside, the FXML file still implements the EventHandler in the standard FXML way and doesn\u0026rsquo;t need to be modified to accommodate the changes to the FXML Controller.\nThere is a better way. First, modify the FXML Controller so that it has constructor parameters :\nclass HelloController(private val model: HelloModel, private val actionHandler: () -\u0026gt; Unit) : Initializable { @FXML private lateinit var welcomeText: Label @FXML private fun onHelloButtonClick() { actionHandler.invoke() } override fun initialize(location: URL?, resources: ResourceBundle?) { welcomeText.textProperty().bind(model.welcomeMessage) } } You can see that now we can set the binding in initialize() and we don\u0026rsquo;t have to worry about null in the actionHandler for the Button.\nThen we have to tell the FXMLLoader to use a \u0026ldquo;Controller Factory\u0026rdquo; to instantiate the FXML Controller. This is just a Callback that will return an instantiation of our FXML Controller:\nclass HelloMvciControllerFxml { private val model: HelloModel = HelloModel() private val interactor = HelloInteractor(model) fun getView(): Region { val fxmlLoader = FXMLLoader(HelloApplication::class.java.getResource(\u0026#34;hello-view.fxml\u0026#34;)) fxmlLoader.setControllerFactory { HelloController(model) { interactor.getWelcome() } } return fxmlLoader.load() } } This works, and functions exactly the same as the non-FXML version.\nWhen you look at the code, it\u0026rsquo;s really not that complicated. Just create an FXML Controller with constructor parameters (just like the ViewBuilder in the non-FXML version) and use a ControllerFactory to instantiate the FXML Controller inside the FXMLLoader. From that point on, you can treat the FXML Controller as the interface for the View, and presence of FXML should be invisible to the rest of the framework.\nYou should note that there were no changes at all to the FXML file throughout this example. This means that, generally speaking, you can design your FXML files with SceneBuilder without any regard to the presence (or not) of an MVC, MVVM or MVCI framework sitting behind it.\nYou should also note that you\u0026rsquo;re probably not going to see this approach outlined anywhere else. I spent a bit of time specifically searching for variants on \u0026ldquo;JavaFX FXML MVC\u0026rdquo; and didn\u0026rsquo;t see anything like this anywhere.\nWhy not?\nI think mostly because any tutorial that you see gets sidetracked with the idea that the FXML Controller is the Controller in the MVC sense. The other thing you always see with this is that the \u0026ldquo;Model\u0026rdquo; that they propose is just a POJO of Observable classes, and doesn\u0026rsquo;t have any domain/application logic in it. All the application logic goes into the FXML Controller, which becomes essentially a \u0026ldquo;God\u0026rdquo; class.\nConclusion # As an experienced JavaFX developer I can say that if I\u0026rsquo;m building a typical business type application, then the View components (which include both the layout and the GUI logic) comprise a fairly small portion of the code base. I\u0026rsquo;d guess something approaching 10% of the total code in the application.\nThis probably sounds fantastical to most beginners. Impossible, really. That\u0026rsquo;s because JavaFX is a huge library which is horribly badly documented for beginners. Virtually everything that beginners think they are doing right is probably wrong. They do things like using EventHandlers and ChangeListeners when Binding is the better approach. Or, things like using ComboBox.getSelectionModel().getSelectedItem() instead of just ComboBox.getValue().\nNone of these \u0026ldquo;wrong\u0026rdquo; approaches is simpler or takes less code than doing it the right way. In fact, a typical beginner probably writes hundreds of lines of code for every screen that are just not necessary.\nSo when we talk about \u0026ldquo;To FXML or not to FXML\u0026rdquo;, we\u0026rsquo;re not talking about some profound decision that dictates the entire structure of your application. We\u0026rsquo;re talking about how you architect that 10% which comprises the View.\nThat is, however, as long as you don\u0026rsquo;t buy into the idea that the FXML Controller acts as the MVC Controller. Because it\u0026rsquo;s not, and if you treat it like it is then your application structure is going to suffer.\nAt the end of the day, the answer to, \u0026ldquo;Should you use FXML?\u0026rdquo;, depends on whether or not you see enough value in SceneBuilder to overcome the added complexity of dealing with the FXML. For most beginners, this is probably not true, but they are also probably not going to realize this. If you are a beginner, and you got this far down this article and you are curious about how you go about creating a layout without FXML, you should read my Absolute Beginners Guide to JavaFX.\n","date":"10 March 2025","externalUrl":null,"permalink":"/javafx/techniques/nofxml/fxml-or-not/","section":"JavaFX","summary":"FXML.  There are all kinds of claims about its value, but are they right?","title":"Should You Use FXML?","type":"javafx"},{"content":" Introduction # In this article we are going to look at the JavaFX classes that apply the \u0026ldquo;Observer Pattern\u0026rdquo; to lists of objects, ObservableList. You\u0026rsquo;ve probably encountered these before, as you need to use them to populate TableViews, ListViews and ComboBoxes. But what exactly is an ObservableList, and what can you do with it in your JavaFX application?\nThe JavaDocs for ObservableList aren\u0026rsquo;t particular helpful, as the introduction is very short:\nQuote A list that allows listeners to track changes when they occur. Implementations can be created using methods in FXCollections such as observableArrayList, or with a SimpleListProperty.\nWhich is, to say the least, a little bit underwhelming.\nIn this article, we\u0026rsquo;re going to take a look at the basics of dealing with ObservableList, so that you can understand what it really does and how to use it.\nA Wrapper For a List # Taking a step back, List is actually an interface. It\u0026rsquo;s implemented by classes such as ArrayList, Vector and LinkedList and Stack. Undoubtedly, the most common implementation you\u0026rsquo;ll see in most applications is ArrayList.\nObservableList is also an interface, but it extends List. This means that anything that you can do with a List, you can do with an ObservableList. What ObservableList adds to List is a handful of convenience methods like setAll(), and some methods that deal with the \u0026ldquo;observable\u0026rdquo; nature of ObservableList. These \u0026ldquo;observable\u0026rdquo; functions boil down to adding and removing Listeners to/from the ObservableList.\n[There\u0026rsquo;s also some functions that create FilteredLists and SortedLists, but we\u0026rsquo;ll look at these in the next article in this series.]\nBut ObservableList is still an interface, and that means that it doesn\u0026rsquo;t talk about \u0026ldquo;how\u0026rdquo; these functions are going to be delivered, just \u0026ldquo;what\u0026rdquo; these functions do. To understand the \u0026ldquo;how\u0026rdquo;, we need to look at implementations. But really, there aren\u0026rsquo;t any concrete, stand-alone, classes in JavaFX that implement ObservableList.\nSo how do you get something you can look at to see how they work? The answer is in that tiny introduction in the JavaDocs:\nQuote Implementations can be created using methods in FXCollections\u0026hellip;\nSo that\u0026rsquo;s where you would have to look to see how it\u0026rsquo;s done. It turns out, if you do look, that you\u0026rsquo;ll find there is a hidden class called ObservableListWrapper that is used as the concrete implementation. Inside this class, it has some concrete implementation of List stored as a variable called backingList.\nThe List part of ObservableList is handled by this backingList. When you call a List function like add() on an ObservableList, it will eventually call backingList.add(). But before and after it calls backingList.add() it is going to do some \u0026ldquo;observable\u0026rdquo; stuff - which is largely about servicing the Listeners that have been attached to the ObservableList.\nCreating an ObservableList # Since you\u0026rsquo;re most likely to want an ObseravbleList backed by an ArrayList, let\u0026rsquo;s look at how you would create one of those.\nThere are four methods in FXCollections that are going to be useful:\nFXCollections.observableArrayList()\nThis will create an ObservableList backed by an empty ArrayList. FXCollections.observableArrayList(E... items)\nThis will create an ObservableList backed by an ArrayList populated with items. FXCollections.observableArrayList(Collection\u0026lt;E\u0026gt; col)\nThis will create an ObservableList backed by an ArrayList populated with the contents of col. FXCollections.observableList(List\u0026lt;E\u0026gt; list)\nThis will create an ObservableList backed by the supplied list. Note that if you use this method you should not make any further changes to the backing list directly as that will not invalidate or trigger any Listeners on the returned ObservableList. It\u0026rsquo;s highly likely that you\u0026rsquo;re interested in having an ObservableList because you need it to use a TableView, ListView or ComboBox. In these cases, you should be aware that these Nodes already have an ObservableList that you can access via getItems(). So you might not need to create your own ObservableList - depending on what you\u0026rsquo;re doing.\nUnderstanding ListChangeListener # The Observer Pattern methods that are unique to ObservableList are:\naddListener(ListChangeListener\u0026lt;? super E\u0026gt; listener) removeListener(ListChangeListener\u0026lt;? super E\u0026gt; listener) You can see that you\u0026rsquo;ll need to understand the concept of ListChangeListener, or rather more importantly, ListChangeListener.Change.\nListChangeListener itself is actually an interface, and it\u0026rsquo;s pretty simple. It has only one method, and that\u0026rsquo;s, onChanged(ListChangeListener.Change\u0026lt;? extends E\u0026gt; c). When you\u0026rsquo;re adding a Listener to an ObservableList it\u0026rsquo;s up to you to create the Listener that implements ListChangeListener. And that boils down to writing the onChanged() method.\nListChangeListener is a functional interface, so you can define it using a lambda expression for the onChanged() method. But to write one, you need to understand ListChangeListener.Change, and this is where it gets a bit messy.\nUnderstanding ListChangeListener.Change # Despite its singular name, a ListChangeListener.Change may actually contain several changes. The JavaDocs call it \u0026ldquo;a report of changes done to an ObservableList\u0026rdquo;. I think they use \u0026ldquo;report\u0026rdquo; to avoid using \u0026ldquo;List\u0026rdquo; or \u0026ldquo;Collection\u0026rdquo;, both of which would imply specific Java types - which ListChangeListener.Change is most definitely not.\nThe best way to think of ListChangeListenter.Change is as a self-referential iterable with an internal pointer. The pointer starts out in some initial, undefined state, and you\u0026rsquo;ll need to perform next() to move it to the first item. At that point all of the methods that fetch value will return the value associated with the internal pointer. There is a reset() method to put the pointer back in its initial, undefined state.\nWithin each change, there are three basic types of change that may happen:\nPermutation Change\nThis is where two or more members of the list are swapped around. Add Change\nA new member is added to the list. Remove Change\nA list member is removed. Additionally a change may be classified as an \u0026ldquo;replace\u0026rdquo; if one or members are removed and new members added to the same locations in the ObseravbleList.\nThere are two methods, getFrom() and getTo() that specify the beginning and end of the sublist of values in the ObseravbleList that were changed.\nAddition changes will return true to a call to the wasAdded() method. You can call getAddedSubList() to get a list of the elements that were added, and you can call getAddedSize() to find out how many elements were added.\nRemoval changes will return true to a call to the wasRemoved() method. You can call getRemoved() to get a List of all of the removed members, and getRemovedSize() to find out how many elements were removed.\nPermutation changes will return true to a call to the wasPermutated() method. The method getPermutation() will return an array of the indices of the affected list members.\nExploring ListChangedListener.Change # In order to understand how to interpret ListChangedListener.Change we\u0026rsquo;ll need to write some code to create some changes, and to capture them and display what\u0026rsquo;s happened. Let\u0026rsquo;s take a look at that:\nclass ObListExample0 : Application() { private val obList: ObservableList\u0026lt;Int\u0026gt; = FXCollections.observableArrayList() private val messages: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) override fun start(stage: Stage) { obList.addListener(ListChangeListener { change -\u0026gt; messages.value = \u0026#34;Change started...\\n\\n\u0026#34; var counter = 0 while (change.next()) { messages.value += \u0026#34;Change element: ${counter++}\\n\u0026#34; messages.value += \u0026#34; From Index = ${change.from}\\n\u0026#34; messages.value += \u0026#34; To Index = ${change.to}\\n\\n\u0026#34; messages.value += \u0026#34; wasAdded = ${change.wasAdded()}\\n\u0026#34; messages.value += \u0026#34; Added SubList = ${change.addedSubList}\\n\\n\u0026#34; messages.value += \u0026#34; wasRemoved = ${change.wasRemoved()}\\n\u0026#34; messages.value += \u0026#34; Removed List = ${change.removed}\\n\\n\u0026#34; messages.value += \u0026#34; wasPermutated = ${change.wasPermutated()}\\n\u0026#34; if (change.wasPermutated()) { messages.value += \u0026#34; Permutated Indices = \u0026#34; for (idx in change.from..\u0026lt;change.to) messages.value += \u0026#34; ${change.getPermutation(idx)}\u0026#34; messages.value += \u0026#34;\\n\\n\u0026#34; } messages.value += \u0026#34; wasUpdated = ${change.wasUpdated()}\\n\\n\u0026#34; } }) stage.scene = Scene(createContent(), 480.0, 600.0).apply { } stage.show() } private fun createContent(): Region = BorderPane().apply { val listView = ListView\u0026lt;Int\u0026gt;().apply { items = obList selectionModel.selectionMode = SelectionMode.MULTIPLE prefWidth = 150.0 } left = listView right = TextArea().apply { prefWidth = 280.0 textProperty().bind(messages) } bottom = HBox(20.0).apply { children += Button(\u0026#34;Add Item\u0026#34;).apply { setOnAction { obList.add(Random.nextInt(100)) } } children += Button(\u0026#34;Remove Item\u0026#34;).apply { disableProperty().bind(listView.selectionModel.selectedItemProperty().isNull) setOnAction { obList.removeAll(listView.selectionModel.selectedItems) } } children += Button(\u0026#34;Sort\u0026#34;).apply { setOnAction { obList.sort() } } children += Button(\u0026#34;Change Item\u0026#34;).apply { disableProperty().bind(listView.selectionModel.selectedItemProperty().isNull) setOnAction { obList[listView.selectionModel.selectedIndex] = Random.nextInt(100) } } padding = Insets(10.0, 0.0, 0.0, 0.0) } padding = Insets(20.0) } } fun main() = Application.launch(ObListExample0::class.java) We have a ListView on the left of our layout, which will show us which items are in the ObservableList, and allow us to select items to work with. The ListView selection mode is set to MULTIPLE.\nOn the right side, there\u0026rsquo;s a TextArea which is bound to the messages StringProperty. This will let us see the contents of the ListChangeListener.Change.\nAt the bottom are a set of Buttons that will invoke various actions on the ObservableList so that we can see how the changes are reported.\nThere is a ListChangeListener added to the ObservableList that just creates some text to add to messages. The StringProperty is cleared out for each new Change. It\u0026rsquo;s just a loop that iterates through all of the elements in the Change and then calls the various methods to show what\u0026rsquo;s stored in each element.\nAdding Items # Let\u0026rsquo;s take a look at the application after it has been started and an item added:\nAnd this is what it looks like after the \u0026ldquo;Add\u0026rdquo; Button has been clicked a few times:\nRemoving Items # Removing items one at a time looks like you would expect it to after having seen the results for adding. It\u0026rsquo;s more interesting if you select elements that are NOT in a contiguous group in the list:\nAnd then when we click the \u0026ldquo;Remove Item\u0026rdquo; Button, we get this:\nYou can see that we get a Change element for each contiguous segment of the removal. In this case, we get three elements. Change element 1 had three items in it, while the other 2 each had one element.\nYou can also see that the from index for the element 1 says \u0026ldquo;3\u0026rdquo; even though it started at element 7 in our list before clicking the Button. That\u0026rsquo;s because each element uses indexes calculated from after the previous element was applied. In this case, since the elements \u0026ldquo;25\u0026rdquo;, \u0026ldquo;61\u0026rdquo;, \u0026ldquo;15\u0026rdquo; and \u0026ldquo;11\u0026rdquo; would have already been removed, that would have moved the last items from 7 to 3. Also note that remove doesn\u0026rsquo;t give the a different index for the to index. So you cannot use to in any meaninful way with a removal Change element.\nAnd if you were wondering how you could get more than one element in a Change, now you know.\nPermutating the List # \u0026ldquo;Permutating\u0026rdquo; just means to move the items around. To simulate this, we\u0026rsquo;ll just use sort() on the list. It looks like this before we sort it:\nAfter clicking \u0026ldquo;Sort\u0026rdquo;\nThis is what you\u0026rsquo;d expect after seeing addition and removal in action. Note that the \u0026ldquo;From\u0026rdquo; and \u0026ldquo;To\u0026rdquo; indexes are populated in the Change data, and that the indices are presumably in the order in which they were moved.\nUpdating items # Our list is just Int primitives, so the meaning of \u0026ldquo;Update\u0026rdquo; is simply going to be to replace a value with a new value. Here\u0026rsquo;s what it looks like with an items selected, and just before we click on the \u0026ldquo;Change Item\u0026rdquo; Button:\nAfter clicking \u0026ldquo;Change\u0026rdquo;\nYou can see that this is a single Change, and a single Change element. However, both wasAdded() and wasRemoved() return true. We also have an added sublist and a removed sublist, each with one item.\nYou should also note that wasUpdated() returned false. So it doesn\u0026rsquo;t look like wasUpdated() means what you probably thought it did. We\u0026rsquo;ll investigate this some more in the next artcle.\nUsing ObservableLists # Now that you understand how ObservableList delivers its changes to any listeners, you can see how fiddly it can be to try and doing anything with it. It just looks like a lot of work - and it hard to see how you could do anything useful that doesn\u0026rsquo;t get really complicated really quickly.\nAs a practical matter, what can you do with an ObservableList besides stuff it into a TableView?\nBinding ObservableLists # If you\u0026rsquo;ve ever tried to use the observer pattern to do something practical with an ObservableList you probably found yourself dealing with ListChangeListener, which can be an amazing amount of work to do fairly simple things. Let\u0026rsquo;s say that you want to track the total of some field in an ObservableList of objects. You end up creating the total, then adding a Listener that adds a value in for new additions to the list, subtracts from the total for removals and does both for updates. If you think about it, you realize that you\u0026rsquo;ve ended up creating a transaction processor for an amount that should be readily available from the ObservableList at any time.\nThis is something that should be possible with a Binding! But how?\nIf you go back to the JavaDocs for ObservableList you\u0026rsquo;ll see that this interface extends, amongst other things, Observable. And Observable introduces the idea of \u0026ldquo;Invalidation\u0026rdquo;. And invalidation is the basis for Bindings, and this means that you don\u0026rsquo;t have to go through ListChangeListener to do a lot of things.\nLet\u0026rsquo;s look at how the invalidation works first:\nclass ObListExample1 : Application() { val obList: ObservableList\u0026lt;Int\u0026gt; = FXCollections.observableArrayList() val messages: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) override fun start(stage: Stage) { obList.subscribe { messages.value += \u0026#34;Invalidated \\n\u0026#34; } stage.scene = Scene(createContent()).apply { } stage.show() } private fun createContent(): Region = BorderPane().apply { center = TextArea().apply { textProperty().bind(messages) } bottom = Button(\u0026#34;Add Item\u0026#34;).apply { setOnAction { obList.add(3) } } padding = Insets(20.0) } } fun main() = Application.launch(ObListExample1::class.java) We have a TextArea to show messages and an ObservableList of Int. There\u0026rsquo;s a Button, and each time it\u0026rsquo;s pressed, it adds a new \u0026ldquo;3\u0026rdquo; to the ObservableList. We have an invalidation Subscription on the ObservableList that simply adds a new line to the messages that says, \u0026ldquo;Invalidated\u0026rdquo;:\nAs you can see, even though there is no code here that would re-validate the ObservableList, it invalidates every time an entry is added to it. That\u0026rsquo;s different from other Observables, but makes our life easier.\nThis is because all we need is for our ObservableList to invalidate for us to create a Binding with it. Let\u0026rsquo;s see how that would be done:\nclass ObListExample2 : Application() { private val obList: ObservableList\u0026lt;Int\u0026gt; = FXCollections.observableArrayList() private val messages: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val totBinding = object : IntegerBinding() { init { super.bind(obList) } override fun computeValue(): Int = obList.sum() } override fun start(stage: Stage) { obList.subscribe { messages.value += \u0026#34;Invalidated \\n\u0026#34; } stage.scene = Scene(createContent()).apply { } stage.show() } private fun createContent(): Region = BorderPane().apply { top = Label().apply { textProperty().bind(totBinding.asString()) } center = TextArea().apply { textProperty().bind(messages) } bottom = Button(\u0026#34;Add Item\u0026#34;).apply { setOnAction { obList.add(3) } } padding = Insets(20.0) } } fun main() = Application.launch(ObListExample2::class.java) Our Binding is dependant on the ObservableList, but simply treated as Observable. Then, in computeValue() we do the Kotlin equivalent of streaming and collecting the stream as a sum (but it\u0026rsquo;s easier in Kotlin). When we add a new element to the list, it invalidates and the binding of value to Label.textProperty() will cause computeValue() to be called which will recalculate the total.\nThis is useful because this allows us to establish some aspect of the contents of the ObservableList as a Property and then bind it to something else, without any need to track and apply transactions manually. In my experience this is often way more useful than anything you can do with ListChangeListener.\nOne possible downside to this approach is that it involves recalculating the total from scratch every time that the ObservableList is invalidated. Is this likely to become a performance problem? Unless you\u0026rsquo;ve got literally thousands of items in your ObservableList, this is probably not going to be significantly slower than doing the transaction processing of using ListChangeListener.\nThis is a extremely important concept. We can ignore the complicated ListChangeListener stuff completely, and just treat our ObservableList like any other Obseravble class that will trigger an invalidation when it changes. And it just works.\nBindings Class Methods # The Bindings utility class has a number of static methods that deal with ObservableLists and return Bindings that reflect the state of ObservableLists.\nThe valueAt() Functions # There\u0026rsquo;s a whole group of these functions each of which come in two forms. All of them return some kind of Binding which is dependent on a particular value in an ObservableList.\nLet\u0026rsquo;s look at the generic valueAt() function:\nThe first form is with an Integer index: valueAt(ObservableList\u0026lt;E\u0026gt; op, int index)). This will create a Binding that will invalidate whenever the ObservableList changes, and possibly calculate a new value.\nThe second form is with an ObservableIntegerValue as the index: valueAt(ObservableList\u0026lt;E\u0026gt; op, ObservableIntegerValue index). This will create a Binding that will invalidate whenever the ObservableList or the index change, and possibly create a new value.\nOne of the values of these methods is that they allow you to essentially treat a List of primitives or POJOs as Properties. Just wrap your List in an ObservableList and you can create a Binding for each value in the List.\nIn addition to valueAt(), there is also integerValueAt(), stringValueAt(), booleanValueAt(), and doubleValueAt() which will create Bindings of the appropriate types. There\u0026rsquo;s also methods for Long and Float types.\nThe bindContent() Functions # There are two versions of this function:\nbindContent(List\u0026lt;E\u0026gt; list1, ObservableList\u0026lt;? extends E\u0026gt; list2) bindContentBidirectional(ObservableList\u0026lt;E\u0026gt; list1, ObservableList\u0026lt;E\u0026gt; list2) The first version is used to keep an ordinary, non-observable List synchronized with an ObservableList. This is useful when you have been given a List reference from some other class or API, and you would rather have an ObservableList. You can create an ObservableList and then use bindContent() to keep that other List reference synchronized with it.\nThe one caveat with this Binding is that you cannot have changes coming in directly to the non-observable List.\nThe second version allows you to keep two ObservableLists synchronized with each other. This is useful if you have two ObservableLists from different sources and you cannot replace one or other of them but you want to keep them synchronized.\nThe isEmpty() Functions # There are two functions in this category: isEmpty(ObservableList\u0026lt;E\u0026gt; op) and isNotEmpty(ObservableList\u0026lt;E\u0026gt; op). These will return BooleanBindings that will be synchronized with emptiness or non-emptiness of whatever ObservableList you pass them.\nThe size() Function # This function is very simple, size(ObseravbleList\u0026lt;E\u0026gt; op) will return an IntegerBinding that is synchronized with the size of the ObservableList that you pass to it.\nObservableLists in Node Classes # The truth is that you\u0026rsquo;re most likely to be encountering ObservableLists when you deal with them in standard JavaFX Node classes. Virtually anything that is of a List nature in a Node subclass is going to be available as an ObservableList. For example:\ngetStyleClass() getTransforms() getChildren() getStyleSheets() getItems() All of these functions will return an ObservableList of some sort. With getItems(), which is in TableView, ListView and some other Nodes you have the option of calling setItems(), which means that you can actually replace the ObservableList involved.\nLet\u0026rsquo;s say that you have some sort of Presentation Model in your application, and in this Presentation Model you have an ObservableList that you want to use to control one of these Node internal ObservableLists. If you can\u0026rsquo;t replace the ObservableList like you can with setItems(), then the easiest thing that you can do is to Bindings.bindContentBidirectional() to link the two ObservableLists.\nThe other approach that you can do is to install the internal list from a Node (through, for instance, Node.getStyleClass) as an element of your Presentation Model. But this has a couple of problems, especially if your Presentation Model is part of some sort of MVC, MVVM or MVCI framework:\nYou cannot make the element in your Presentation Model final. You cannot reference the element in your Presentation Model until it has be initialized from the View. Except for items these ObservableLists all contain very View-centric data elements. The first two items are really just flip sides of the same problem.\nBinding With Conversion # Let\u0026rsquo;s look \u0026ldquo;under the hood\u0026rdquo; to see how Bindings.bindContent() works. The method call eventually ends up calling the constructor of this class:\nprivate static class ListContentBinding\u0026lt;E\u0026gt; implements ListChangeListener\u0026lt;E\u0026gt;, WeakListener { private final WeakReference\u0026lt;List\u0026lt;E\u0026gt;\u0026gt; listRef; public ListContentBinding(List\u0026lt;E\u0026gt; var1) { this.listRef = new WeakReference(var1); } public void onChanged(ListChangeListener.Change\u0026lt;? extends E\u0026gt; var1) { List var2 = (List)this.listRef.get(); if (var2 == null) { var1.getList().removeListener(this); } else { while(var1.next()) { if (var1.wasPermutated()) { var2.subList(var1.getFrom(), var1.getTo()).clear(); var2.addAll(var1.getFrom(), var1.getList().subList(var1.getFrom(), var1.getTo())); } else { if (var1.wasRemoved()) { var2.subList(var1.getFrom(), var1.getFrom() + var1.getRemovedSize()).clear(); } if (var1.wasAdded()) { var2.addAll(var1.getFrom(), var1.getAddedSubList()); } } } } } . . . } Hardly surprising, this boils down to a ListChangeListener, and you can see how it handles the three cases of adding, removing permutating in the source ObservableList. Especially interesting, though is that you can see that, even deep inside the JavaFX library, this code that just uses the standard building blocks that are available to all of us.\nI\u0026rsquo;ve left out some of the housekeeping methods of this class, because they are not relevant to this discussion. Also, there\u0026rsquo;s a little added complexity around the WeakListener handling. While this is necessary in the JavaFX library, a compelling argument could be made that in application code you could make assumptions about the nature of the dependent List and if it\u0026rsquo;s unlikely that it would ever be disposed of then the WeakListener handling just adds unnecessary complications to the code base.\nLet\u0026rsquo;s see how to add a conversion into this process. Rather than create a class to handle this, in Kotlin we can just create an Extension Function that we add to Observable List. Like this:\nfun \u0026lt;E, T\u0026gt; ObservableList\u0026lt;E\u0026gt;.bindToList(boundList: MutableList\u0026lt;T\u0026gt;, mapper: (E) -\u0026gt; T) = apply { val listRef: WeakReference\u0026lt;MutableList\u0026lt;T\u0026gt;?\u0026gt; = WeakReference(boundList) val listGone: BooleanProperty = SimpleBooleanProperty(false) val listener = ListChangeListener\u0026lt;E\u0026gt; { change -\u0026gt; listRef.get()?.let { otherList -\u0026gt; while (change.next()) { if (change.wasPermutated()) { otherList.subList(change.from, change.to).clear() otherList.addAll(change.from, change.list.subList(change.from, change.to).map(mapper) as Collection\u0026lt;T\u0026gt;) } else { if (change.wasRemoved()) { otherList.subList(change.from, change.from + change.removedSize).clear() } if (change.wasAdded()) { otherList.addAll(change.from, change.addedSubList.map(mapper)) } } } } listGone.value = (listRef.get() == null) } addListener(listener) listGone.subscribe { newVal -\u0026gt; if (newVal) removeListener(listener) } } Inside this function this refers to the ObservableList that this is called on, and boundList is the List that is going to be kept in sync with the ObservableList. I\u0026rsquo;ve tried to keep the WeakReference functionality that was in the original class, so the onChange method is going to check that the WeakReference to the bound List hasn\u0026rsquo;t been garbage collected. If it has, then it updates a BooleanProperty that has a Subscription which will remove the ListChangeListener from the ObservableList.\nThis logic is a little bit convoluted because the ListChangeListener would need to refer to itself in order to remove itself from the ObservableList.\nThis method has a second, Functional parameter that performs the mapping between the type of the ObservableList which is \u0026lt;E\u0026gt; and the type of the bound list which is \u0026lt;T\u0026gt;. In Kotlin this is (E) -\u0026gt; T, which would be Function\u0026lt;E,T\u0026gt; in Java. In the functions that call List.addAll() the collection of added entries is passed through the mapping function before being passed to the bound List. In Kotlin, map() is a native function of Collection so we don\u0026rsquo;t need past the elements through a Stream as you would have to do in Java.\nBinding Conversion in Action # One place where you might want to us this is when you want to have a dynamic layout where the children of a container are going to change in response to some ObservableList of values in the Presentation Model. For whatever reason, you don\u0026rsquo;t want to use ListView for this purpose.\nThe fundamental challenge is that the children of a container have to be some subclass of Node, and you never put Nodes into a Presentation Model. Creation of Nodes is totally the responsibility of the View. If your business logic is adding elements from some routine, it can only put \u0026ldquo;data\u0026rdquo; into the Presentation Model, and not Nodes.\nTo demonstrate how to handle this with our conversion Binding let\u0026rsquo;s take a look at this code:\nclass ObListExample3 : Application() { private val obList: ObservableList\u0026lt;Int\u0026gt; = FXCollections.observableArrayList() private val messages: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) override fun start(stage: Stage) { obList.subscribe { messages.value += \u0026#34;Invalidated \\n\u0026#34; } stage.scene = Scene(createContent()).apply { } stage.show() } private fun createContent(): Region = BorderPane().apply { val listView = ListView\u0026lt;Int\u0026gt;().apply { items = obList } left = listView right = FlowPane().apply { obList.bindToList(children) { createLabel(it) } } bottom = HBox(20.0).apply { children += Button(\u0026#34;Add Item\u0026#34;).apply { setOnAction { obList.add(Random.nextInt(100)) } } children += Button(\u0026#34;Remove Item\u0026#34;).apply { disableProperty().bind(listView.selectionModel.selectedItemProperty().isNull) setOnAction { obList.remove(listView.selectionModel.selectedItem) } } } padding = Insets(20.0) } private fun createLabel(labelInt: Int) = Label(labelInt.toString()).apply { minWidth = 80.0 minHeight = 30.0 border = Border( BorderStroke( javafx.scene.paint.Color.RED, BorderStrokeStyle.SOLID, null, BorderWidths(3.0) ) ) alignment = Pos.CENTER } } fun main() = Application.launch(ObListExample3::class.java) This layout has a ListView on the left and a FlowPane on the right, with two Buttons at the bottom. The ListView has items that are just the obList from the imaginary Presentation Model. The first Button generates a new value in obList with a random number, and the second Button deletes whatever item has been selected in the ListView\nThe statement obList.bindToList(children) { createLabel(it) } creates the Binding between obList and the FlowPane.getChildren() list. The mapping function takes an Int from obList and creates a Label with it.\nIn this example there\u0026rsquo;s a fair bit of code in createLabel() that formats up the Labels. This is just to make a point that you can create any kind of Node, as complicated as you like, that uses the data that the ObservableList supplies. And you should also note that this approach means that the only element that has any knowledge of how obList is going to be used in the layout is the View.\nHere\u0026rsquo;s what it looks like when it\u0026rsquo;s running.\nJust one final note about this: This approach is okay when the lists are fairly short and the Nodes are relatively simple, but when the list gets to be much much bigger you would be better off using a something like a ListView with a custom cell design. This is because ListView only creates a small number of Nodes and recycles them as you scroll through the list - which is much more efficient than this dynamic Node creation approach.\nFor Java, you can look at this StackOverflow answer which details the same thing as an implementation of the ListContentBindng extended to include a mapping function.\nConclusion # ObservableLists do three things at once:\nThey implement the List interface. So you can treat them just like a List. They report on changes to their elements, and fire ListChangeListeners that are registered with them. They implement the Observable interface, which means that they will invalidate when any of there elements are added, deleted, moved or replaced. Most of us, most of the time are just creating an ObservableList to use with some element that requires one. In these cases we\u0026rsquo;re only actively going to leverage item 1 above, and just treat it like a List. Once in a very occasional while, we\u0026rsquo;ll need to do something that requires monitoring the exact nature of the changes to an ObseravbleList and we\u0026rsquo;ll be forced to deal with ListChangeListener.Change.\nBut if you do need to do something \u0026ldquo;observable\u0026rdquo; with an ObseravbleList, item 3 might be the most important one. It\u0026rsquo;s highly likely that you can leverage ObservableList's implementation of Observable to create a Binding, and the resulting code will be far simpler than dealing with ListChangeListener.Change. For some reason, this almost seems to be \u0026ldquo;secret knowledge\u0026rdquo; in the JavaFX world, even though it\u0026rsquo;s actually very easy to implement - as you saw above.\n","date":"12 November 2024","externalUrl":null,"permalink":"/javafx/elements/observable-lists-basics/","section":"JavaFX","summary":"ObservableLists are used in a number of JavaFX elements, such as TableView, ListView and ComboBox.  But what are they, and what can you do with them?","title":"Observable Lists - The Basics","type":"javafx"},{"content":" Introduction # One of the biggest advantages to JavaFX is it\u0026rsquo;s seamlessly integrated support for the \u0026ldquo;Observer\u0026rdquo; Pattern. This can also be one of the biggest challenges to learning JavaFX, because the library is so extensive it can become overwhelming.\nIn this article, we\u0026rsquo;re not going to go too deep into the details. This is intended for beginners so that they can get started and create their JavaFX applications the right way, and to avoid a lot of the pitfalls to which many beginners fall victim. By the end of this article you should have a good understanding of what JavaFX Properties are, how they work, and how you should use them.\nKotlin The examples in this article are written in Kotlin. If you need some help understanding Kotlin, then refer to this article. What is a Property? # A Property is a special type of object designed to contain a data value and to support the \u0026ldquo;Observer\u0026rdquo; Pattern. It does this by keeping track of whenever the value is changed, and running code that other Property objects have registered with it when the value changes. Properties can also \u0026ldquo;bind\u0026rdquo; to other Properties, so that when one or more of those other Properties changes, its value will be recalculated.\nYou can use this ability to create a network of connections that link almost every aspect of your GUI to a set of data that you create. This means that you can create your layout, connect the layout Properties to other layout Properties and Properties that you create, and then discard any references to the Nodes in your layout because you won\u0026rsquo;t need them any more. You can control the layout by modifying the Properties that you have created.\nThat\u0026rsquo;s it in a nutshell, but there\u0026rsquo;s a lot of details you\u0026rsquo;ll need to understand.\nThe exhaustive nature of the Property library in JavaFX and it\u0026rsquo;s integration with all of the classes that make up JavaFX make a compelling argument that JavaFX is primarily designed to be used as a \u0026ldquo;Reactive\u0026rdquo; framework. We\u0026rsquo;ll go into this some more later, but for now we can just think of \u0026ldquo;Reactive\u0026rdquo; as what you get when you treat all your data as Properties and connect everything together through Bindings and Listeners.\nProperty Basics # There are a number of Property types, and we\u0026rsquo;ll see them all in a bit. For now, we\u0026rsquo;ll just look at StringProperty as an example. A StringProperty is a Property designed to hold a String (no big surprise there).\nAt it\u0026rsquo;s most basic, we can think of StringProperty as a container for a String. We have a get() method that returns a String and a void set(newVal : String) method to update the value. All of that is pretty straight-forward.\nval stringProperty: StringProperty = SimpleStringProperty() stringProperty.set(\u0026#34;abc\u0026#34;) println(stringProperty.get()) We can see here that our StringProperty is implemented as SimpleStringProperty. That\u0026rsquo;s because StringProperty is an abstract class, while SimpleStringProperty is concrete. However, there are no new methods in SimpleStringProperty, and we literally only need it because of its constructors. This means that there\u0026rsquo;s no reason to retain a reference to a SimpleStringProperty as a SimpleStringProperty over retaining it as a StringProperty.\nThe most common thing you might do with a StringProperty is to bind it to another StringProperty using the bind() method. This would look something like this:\nval x:StringProperty = SimpleStringProperty(\u0026#34;abc\u0026#34;) val y:StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) y.bind(x) Now, any changes in x will be immediately reflected in y. Also, any attempt to call the y.set() method will now generate a runtime error, indicating that a \u0026ldquo;bound value cannot be set\u0026rdquo;.\nBut why would you want to do this?\nUsually, one of those two StringProperties, either x or y is given to you from somewhere else. In JavaFX it will probably be an internal Property of some JavaFX class. Let\u0026rsquo;s take a look at a more typical example:\nval x = SimpleStringProperty(\u0026#34;abc\u0026#34;) val heading = Label() heading.textProperty().bind(x) Here we\u0026rsquo;ve replaced y with the textProperty() of a Label. Usually, x would be an element of a Presentation Model, or it might be a Property variable that\u0026rsquo;s global to your layout that you use to avoid coupling Nodes on your layout. Either way, whenever x changes, the text on the screen associated with that Label will change.\nProperty-Like Objects # In JavaFX there are a number of types that behave very much like Properties but aren\u0026rsquo;t actually Properties. I think a lot of people use the term \u0026ldquo;Property\u0026rdquo; as a catch-all, meaning not just Properties but also these other types.\nSo let\u0026rsquo;s have a look at them\u0026hellip;\nObservableValue # Sometimes you don\u0026rsquo;t want other code, or other people\u0026rsquo;s code, updating the value in your Property. Sometimes, you don\u0026rsquo;t want to be given something that you can update. In those cases, you can use the read-only ObservableValue interface. You can do anything you can do with a Property, but you cannot call set() or bind() on an ObservableValue.\nHere\u0026rsquo;s an example:\nfun createLabel(obVal : ObservableStringValue)= Label().apply{ textProperty().bind(obVal) } This is a function that returns a Label with its textProperty() bound to a supplied Property, but, since it isn\u0026rsquo;t going to update the value in that Property it just asks for an ObservableStringProperty. The client code can pass a StringProperty, a StringBinding or any other class that implements ObservableStringValue.\nReadOnlyProperty # This is something that you\u0026rsquo;ll see much more than you will ever create. In fact, you\u0026rsquo;ll probably never create a ReadOnlyProperty. There are lots of Nodes in JavaFX that have Properties that they really, really don\u0026rsquo;t want you to change. Internally, Node will manipulate the value, but it\u0026rsquo;s only will to give you something that you can never change. That something is ReadOnlyProperty.\nFor instance, Region has a widthProperty() function that returns a ReadOnlyDoubleProperty. You\u0026rsquo;ll find a Region.getWidth() function, but you won\u0026rsquo;t find a Region.setWidth() function. Region has things that you can do to influence how it calculates its width, but you cannot directly set it. So Region.widthProperty() doesn\u0026rsquo;t give you something that will let you change the width directly.\nFor most purposes you might have, you can treat ReadOnlyProperty exactly like ObservableValue.\nBinding # OK, Binding is a type as well as an action/method. You will need to get these straight in your head.\nFor the sake of clarity, I will always use Binding for the type, and \u0026ldquo;binding\u0026rdquo; for the action.\nEvery Binding will have a list of Properties that it is bound to, which are called its \u0026ldquo;dependencies\u0026rdquo;. Whenever any one of its dependencies changes, the next call to the Binding.get() will cause the Binding to recalculate its value.\nIt\u0026rsquo;s important to note that the dependencies are simply used to trigger a recalculation of the value. There is no rule that says that the Binding has to use any of these dependencies in its calculation, or that it cannot use other values that aren\u0026rsquo;t listed as dependencies. That being said, 99% of the time, the dependencies will be the values that are used in calculating the Binding's value.\nMore often than not, you\u0026rsquo;ll see Binding when you create one yourself, and usually you\u0026rsquo;ll use it as an argument to Property.bind(). However, you can pass a Binding around and it will behave pretty much the same as any ObservableValue.\nObservableLists # An ObservableList is very much like a Property wrapping a List of some sort. The key word there is \u0026ldquo;like\u0026rdquo;. ObservableLists are quite a bit different from the other Properties that we discuss here.\nInstead of reporting on changes to a value, it reports changes to a list of values. These changes include additions to the list, removals from the list and replacement of items in the list.\nInvalidation and Listeners # When you are talking about Properties and bind(), it\u0026rsquo;s all about \u0026ldquo;changes\u0026rdquo;. But there\u0026rsquo;s actually more going on than just that. There is a very, very important concept called \u0026ldquo;Invalidation\u0026rdquo;.\nSimply put, Properties need a mechanism to tell other properties that they need to come and fetch the latest value that they hold, because the one that they last read may not be correct any more. This mechanism is called \u0026ldquo;Invalidation\u0026rdquo;. Whenever you call Property.set(), that will cause an internal valid flag inside the Property to flip to false. The only way to flip the valid flag back to true is to call Property.get().\nSo how do the other Properties know that our Property has been invalidated?\nThey know because they register Listeners with our Property. A listener is just a snippet of code that\u0026rsquo;s called by the Property that just invalidated and is donated by the listening object. Note that a Listener can be created by any code, and doesn\u0026rsquo;t need to be part of a Property itself.\nInside the Property it keeps a list of all of the Listeners that have been registered with it. When it is invalidated, the Property will then execute each of those Listeners, one after the other.\nWarning If a Property is already invalid, then it cannot be invalidated again and it will not execute any Listeners until after it has been revalidated.\nInvalidation Chains # When a Property is bound to another Property it registers a special kind of Listener with that Property that simply invalidates the second Property too. Then, if that Property is, in turn, bound to another Property then its Listener will simply invalidate the third Property. And so on, and so on, and so on.\nNormally, at some point at the end of the chain is a Listener that actually calls get() on the Property it\u0026rsquo;s listening to, and that will trigger the binding logic to call get() on the next Property back up the chain. All the way back to the beginning. And then coming back down the chain, all of those Properties will be revalidated.\nThese chains are why an invalidated Property might only possibly have a different new value. And why invalidation doesn\u0026rsquo;t mean that a value has changed. Let\u0026rsquo;s look at an example:\nval x = SimpleIntegerProperty(3) val y = SimpleBooleanProperty(false) y.bind(x.greaterThan(7)) x.set(6) There\u0026rsquo;s actually three Properties in this snippet of code, not two. That\u0026rsquo;s because x.greaterThan(7) creates a Binding and that\u0026rsquo;s what y is bound to.\nThe line x.set(6), will invalidate x, and then the Binding that was created from x.greaterThan(7), and then y. However, even though y has been invalidated, it\u0026rsquo;s value hasn\u0026rsquo;t changed, and is still false. But anything that\u0026rsquo;s listening to y can\u0026rsquo;t know that until it calls y.get(), which will cause x.greaterThan(7) to re-evaluate (which will call x.get()) and then everything will be re-validated.\nThis is important to know and to understand, but it\u0026rsquo;s not something that you\u0026rsquo;ll need to deal with every day. A more usual use case for this is something like this:\nval x = SimpleIntegerProperty(3) val label = Label(\u0026#34;Too Big!\u0026#34;) label.visibleProperty().bind(x.greaterThan(7)) x.set(6) You can count on the fact the that internal workings of Label are going to respond immediately when its visible Property invalidates, and it will call visibleProperty().get() right away, causing everything to revalidate.\nInvalidation Listeners # The first kind of Listener than can be registered with a Property is an InvalidationListener. An InvalidationListener is one that fires whenever the Property becomes invalid, and the code that it executes is a Runnable. It is totally up to the InvalidationListener to decide if it wants to call get() on the Property which would revalidate it - but it\u0026rsquo;s usually recommended to do so.\nWhile you may or may not use InvalidationListeners yourself, you should know that they are the foundation of all of the other techniques described here. ChangeListeners, Subscriptions and Bindings all rely on InvalidationListeners internally.\nChangeListeners # ChangeListeners are different from InvalidationListeners in two ways:\nThey trigger only when the value actually has changed. They receive both the old value and the new value of the Observable in their Consumer. Here\u0026rsquo;s how you would create and register a ChangeListener on a Property\nval x:StringProperty = SimpleStringProperty(\u0026#34;abc\u0026#34;) x.addListener(ChangeListener{obVal, oldVal, newVal -\u0026gt; println(\u0026#34;Value of $obVal has changed from $oldVal to $newVal\u0026#34;) }) You can see that a ChangeListener takes a reference to the Property itself, the old value and the new value. Since you get the reference to the Property this means that you can create a single ChangeListener and install it on multiple Properties and still be able to determine which Property triggered the ChangeListener.\nSubscriptions # Now that you know about Listeners you should also know that you shouldn\u0026rsquo;t use them nowadays. We\u0026rsquo;ve got better stuff!\nIn version 21 of JavaFX, we got a new feature called Subscription. In a nutshell, Subscriptions provide the same functionality as Listeners but are better for two reasons:\nThey are easier to use. They are easier to keep track of and to remove, especially when using lambdas to define the action code. Three Types of Subscriptions # There are three different ways to create a Subscription, although all three involve calling a method named subscribe(). Let\u0026rsquo;s take a look at them, concentrating on the different parameters that you pass to subscribe().\nPassing a Runnable [() -\u0026gt; Unit] # This is equivalent to creating an InvalidationListener. The Runnable that you pass to it will be invoked whenever the Property that subscribe() is called on invalidates.\nPassing a Consumer [(x) -\u0026gt; Unit] # This is the equivalent to creating a ChangeListener and the Consumer will be invoked whenever the Property that subscribe() is called on changes its value. It\u0026rsquo;s way easier to set up than a ChangeListener however, and the only parameter passed to the Consumer is the new value of the Property.\nThere is one other important and useful feature of this Subscription: When this method is called, it immediately invokes the Consumer. Let\u0026rsquo;s look at what this means\u0026hellip;\nImagine that you have some action that you want taken every time that a Property changes its value, so you set up a ChangeListener. You\u0026rsquo;d do something like this:\nnameProperty.addListener(ChangeListener {obVal, oldVal, newVal -\u0026gt; someMethod(newVal)}) But what if its possible that nameProperty already has a value at the time that you add this ChangeListener and you want to run someMethod() on that original value. Then you\u0026rsquo;d have to do this:\nnameProperty.addListener(ChangeListener {obVal, oldVal, newVal -\u0026gt; someMethod(newVal)}) someMethod(nameProperty.get()) This is intrinsically different from setting up a Binding on nameProperty which would automatically execute computValue() immediately when it\u0026rsquo;s created. This is a huge \u0026ldquo;gotcha!\u0026rdquo; for many programmers, because it\u0026rsquo;s so easy to forget that nameProperty might already have a value depending on the circumstances in which this listener is added.\nWith a Subscription, you don\u0026rsquo;t need to do any of that. This is enough:\nnameProperty.subscribe{someMethod(it)} The {someMethod(it)} will be executed immediately, passing whatever the current value of nameProperty happens to be.\nPassing a BiConsumer [(x,y) -\u0026gt; Unit] # This is equivalent to creating a ChangeListener and it will be invoked whenever the Property this it is called on changes its value. The two parameters passed to the BiConsumer are the old value and the new value of the Property.\nHowever, unlike the previous version, passed a Consumer, this version will not be executed immediately when subscribe() is called. It makes sense, though, as there won\u0026rsquo;t be an \u0026ldquo;old value\u0026rdquo; at this point either.\nRemoving Subscriptions # If you look at this example from above:\nnameProperty.addListener(ChangeListener {obVal, oldVal, newVal -\u0026gt; someMethod(newVal)}) What if you later want to remove that ChangeListener? The only way to remove it is to call nameProperty.removeListener(), but that requires that you pass the Listener to removeListener(), which we don\u0026rsquo;t have. We would have to do this:\nval changeListener = ChangeListener {obVal, oldVal, newVal -\u0026gt; someMethod(newVal)} nameProperty.addListener(changeListener) . . . nameProperty.removeListener(changeListener) However, all the versions of subscribe() return Subscription. Which means that we can do this:\nval subscription = nameProperty.subscribe{someMethod(it)} . . . subscription.unsubscribe() You should also know that Subscription contains methods to combine Subscriptions together, so that you can call unsubscribe() on all of the Subscriptions at the same time, or to pass them between methods and classes as a single parameter.\nBindings # There are three ways to create a Binding:\nThe \u0026ldquo;Fluent API\u0026rdquo; Static methods in the Bindings class The \u0026ldquo;Low Level API\u0026rdquo; (creating a custom class) Inside the JavaFX library, the Fluent API works by calling methods in the Bindings class which, in turn, creates custom Binding classes on the fly. So you can see that everything eventually boils down to custom Binding classes.\nBecause of that, we\u0026rsquo;ll look at them in reverse order:\nCustom Binding Class # Let\u0026rsquo;s look at a simple example:\nclass NameBinding(private val fName: ObservableValue\u0026lt;String\u0026gt;, private val lName :ObservableValue\u0026lt;String\u0026gt;) : StringBinding() { init { super.bind(fName, lName) } override fun computeValue() : String { return \u0026#34;${lName.get()}, ${fName.get()}\u0026#34; } } The first thing to note is that we are extending StringBinding. Just like with Properties, we have about 5 different common types of Bindings to hold different kinds of values.\nThe next thing to note is that we are accepting ObservableValue\u0026lt;String\u0026gt; as our dependencies. This means that we are accepting the widest possible range of Observable types that we possibly can. This will work with Property\u0026lt;String\u0026gt;, StringProperty, StringBinding and ObservableStringValue, which keeps our Binding as flexible as possible.\nThe init{} section in Kotlin is very much like a constructor in Java. This init{} section establishes the two passed parameters as dependencies for the binding by passing them to super.bind(). This is super important.\nFinally, we have computeValue(). This is the method that\u0026rsquo;s called to actually figure out what the Binding's value is. Here we are just combining them together with a \u0026ldquo;, \u0026quot; between them. Note that this method returns String not an Observable wrapping String.\nIn a nutshell, a Binding consists of a list of dependencies, and a computeValue() method which is called to, well\u0026hellip; compute the current value of the Binding. Internally, Binding holds a copy of the latest value that it computed, and will return that if it is validated. However, when any of the dependencies becomes invalidated, the Binding will also become invalidated. The next call to get() will call computeValue(), reset the internal value and revalidate the Binding\nOf course, you can also create something very similar as an anonymous inner class, if you like.\nThe Bindings Class # From the JavaDocs:\nBindings is a helper class with a lot of utility functions to create simple bindings.\nThat\u0026rsquo;s an understatement. There are literally hundreds of static methods in this class, and they all build Bindings.\nIf we wanted to do the same thing as our custom Binding example, here\u0026rsquo;s how we would do it with Bindings:\nval binding = Bindings.createStringBinding({\u0026#34;${lName.get()}, ${fName.get()}\u0026#34;}, fName, lName) This is the definition of the method:\npublic static StringBinding createStringBinding(Callable\u0026lt;String\u0026gt; func, Observable... dependencies) In this case, it might be easier to think of Callable\u0026lt;String\u0026gt; as a Supplier\u0026lt;String\u0026gt;. It\u0026rsquo;s a function that takes no input parameters and returns a String.\nIt\u0026rsquo;s easy to see how this maps to our custom Binding. The first parameter corresponds to whatever code that we would put into computeValue() and the other parameters are the same dependencies that we passed to super.bind().\nYou should also know that Bindings has a method called concat() which is specifically designed to do this kind of Binding. We could use it instead:\nval binding = Bindings.concat(lName, \u0026#34;, \u0026#34;, fName) It\u0026rsquo;s clearly easier to use this method.\nYou should spend some time looking at the JavaDocs for Bindings. It\u0026rsquo;s worth the effort.\nThe Fluent API # The Fluent API provides a set of methods for Observables that allow you to transform, combine, compare and do various operations on the Observables, returning new Observables that are bound to the original Observable. That sounds like a mouthful, but it allows you to do things like this:\nval binding = integerProperty.add(2) Whenever integerProperty changes, binding will be updated to have a value 2 greater than integerProperty.\nKeeping with our name combination example, we can do this:\nval binding = lName.concat(\u0026#34;, \u0026#34;).concat(fName) In practice, the Fluent API is most useful when the operations are simple and not too plentiful. After a while, a big long string of .this() and .that() starts to become more difficult to read, understand and maintain. In those cases you\u0026rsquo;re better off using one of the other methods.\nThe ObservableValue.map() Function # Since JavaFX 19 there is a new facility that makes the Fluent API partially obsolete, ObservableValue.map() and ObservableValue.orElse().\nObservableValue.map() works very much like Stream.map() in that it creates a new ObservableValue by translating the value in the original ObservableValue. In many ways, it\u0026rsquo;s like a Binding with a single dependency. The parameter that you pass to ObservableValue.map() is a Function that transforms the value. And the result doesn\u0026rsquo;t have to be the same type as the input value either.\nObservableValue.map() is nice because you can do just about anything you want in normal Java/Kotlin code dealing with the value of the ObservableValue as a primitive. This is virtually guaranteed to be easier to read than the Fluent API unless the equivalent Fluent API calls are dead simple. I\u0026rsquo;ll give you the example from invalidation section above:\nval x = SimpleIntegerProperty(3) val label = Label(\u0026#34;Too Big!\u0026#34;) label.visibleProperty().bind(x.map{it \u0026gt; 7}) Now, maybe that\u0026rsquo;s not simpler than x.greaterThan(7), but what if it was this?\nval x = SimpleIntegerProperty(3) val label = Label(\u0026#34;Invalid!\u0026#34;) label.visibleProperty().bind(x.map{((it \u0026gt; 7) \u0026amp;\u0026amp; (it \u0026lt; 20)) || (it == 23)}) That might get a bit ugly with the Fluent API.\nThe only place where you cannot use this instead of the Fluent API is when you are combining multiple ObservableValues together. However, you can use both ObservableValue.map() and the Fluent API together:\nval binding = lName.map(\u0026#34;The Name is: $it, \u0026#34;).concat(fName) ObservableValue.orElse() is the companion function to ObservableValue.map() and it will return an ObservableValue with a fixed value if the original ObservableValue contains null.\nTypes of Properties # If you want to create a brand new place to store a value that you want to be observable, you\u0026rsquo;re going to need to instantiate a Property of some sort. There are a number of different types of Properties, and you\u0026rsquo;ll need to pick the right one. Notably, you\u0026rsquo;ll probably need to pick from one of these:\nStringProperty BooleanProperty IntegerProperty DoubleProperty ObjectProperty ObjectProperty is used for pretty much anything that isn\u0026rsquo;t a String, Boolean, Integer or Double. This could be a LocalDate, BigDecimal, an Enum or any other custom class you have created.\nBut you\u0026rsquo;ll find out pretty quickly that you can\u0026rsquo;t instantiate any of these classes because they are all abstract. But each one of these classes has a concrete subclass that starts with \u0026ldquo;Simple\u0026rdquo;. For example, SimpleStringProperty. Instantiate using these classes, but type your variables as the super-class.\nLike this:\nval nameProperty : StringProperty = SimpleStringProperty(\u0026#34;Fred\u0026#34;) val ageProperty : IntegerProperty = SimpleIntegerProperty(4) val dateProperty : ObjectProperty\u0026lt;LocalDate\u0026gt; = SimpleObjectProperty(LocalDate.now()) Notice that I\u0026rsquo;ve used val here and not var. This is the same as using final in Java. It\u0026rsquo;s a good practice to establish all of your Properties as final. The value inside them can change, even if they are final, but it will probably break everything if something changes the variable reference for the Property itself.\nNote Always make your Property variable references immutable using \u0026quot;final\u0026quot; in Java and \u0026quot;val\u0026quot; in Kotlin.\nThe Node Properties # If you take a look at the JavaDocs for Node and any of its subclasses, you\u0026rsquo;ll find a section near the top called \u0026ldquo;Property Summary\u0026rdquo;, listing all of the Properties included in that class. For subclasses of Node you\u0026rsquo;ll also see a section that shows all of the Properties inherited from its super-classes.\nJust about every aspect of the Node classes are represented by a Property.\nMost notably, for Node itself we have disable and disabled, effect, focused, hover, id, layoutX and layoutY, managed, opacity, scaleX and scaleY, scene, style, translateX and translateY and visible. Additionally, all of the various EventHandlers are also stored as Properties.\nWhat is important about these is that for virtually all of the Properties that are writeable, changing their values will impact the way that the Node behaves or appears in the layout. Things like scaleX/Y and translateX/Y will change the size and position of a Node and others like focused, disabled and hover will have corresponding PseudoClasses that can be styled.\nMoving to Region and its subclasses, we get width, minWidth, maxWidth and prefWidth along with height,minHeight,maxHeight and prefHeight.\nUtility classes also have Properties. Animations have things like autoReverse, cycleDuration, delay, status, while Transition adds interpolator. Task has all of its EventHandlers as Properties, along with progress, message, title, value, state and workDone.\nIn fact, throughout the entire library you\u0026rsquo;ll be hard pressed to find a single get{Something}() method that doesn\u0026rsquo;t delegate to somethingProperty().get(). They are literally everywhere.\nInfo This means that there\u0026rsquo;s almost nothing in JavaFX that you cannot connect to using the Observer Pattern.\u0026rdquo; %}\nGeneral Property Advice for Reactive GUI Applications # When you see the extent to which JavaFX incorporates the Observer Pattern into every element of every class, and when you see the wealth of library support to do so much with the Observer Pattern, it becomes obvious that JavaFX is designed from the ground up to be a Reactive library.\nBut what does that mean?\nI\u0026rsquo;ll steal from WikiPedia\nIn computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm, it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow.\nWell, that\u0026rsquo;s mouthful, but what it really describes is an approach where your code constructs a network that defines how data will move automatically between objects, and where data changes trigger predefined actions. Which is pretty much what Properties, Bindings, Listeners and Subscriptions do.\nThis means that your code shouldn\u0026rsquo;t be oriented towards doing stuff, but oriented towards connecting things together and setting up triggers to do things when certain conditions are met. And this looks vastly different from the imperative way that you would program up a Swing application. At first, it can seem difficult to do, but there is so much stuff that the Reactive features of JavaFX do for you - and do very well - that your application code becomes much simpler to understand than if you had built it in an imperative programming style.\nOkay, so how should you use all this stuff? Here are some general guidelines you should follow:\nUse a Presentation Model to Store Your GUI\u0026rsquo;s \u0026ldquo;State\u0026rdquo; # This is the key element of Reactive programming. Take all of the possible elements of the \u0026ldquo;State\u0026rdquo; of your GUI and represent them with Properties in a \u0026ldquo;Presentation Model\u0026rdquo;. This can include the following:\nThe data stored in the Nodes Properties shared between Nodes Values that influence how Nodes work Values that are used to trigger actions. Use Observer Pattern Methods in Your Layouts # This is probably the most important guideline. Do not use methods like set() in your layout code unless it is for something guaranteed to be static.\nFor instance:\nlabel.setText(model.nameProperty.value) and\nval label = Label(model.nameProperty.value) are most likely bad. However:\nval label = Label(\u0026#34;Name:\u0026#34;) is fine. Similarly:\nhBox.setPadding(Insets(20.0)) is perfectly good. Providing, of course, that you not planning on changing the padding in the HBox or the text in the Label.\nWhat you should be doing, when the values are not 100% static, is this:\nlabel.textProperty().bind(model.nameProperty) A good rule of thumb is that if some value in a Node Property might change, then create a separate Property and bind the Node Property to it. Then never access the Node Property again.\nIf that changing value is 100% local to your layout then you can just create a Property field in your layout class. But, if that changing value might be used outside of your layout code, then create it as a field in your Presentation Model so that it can be accessed by your application/business logic.\nUse Bindings Whenever You Can # The primary tool for connecting data is Binding. Your first question should always be, \u0026ldquo;How can I do this with a Binding?\u0026rdquo; If you can\u0026rsquo;t figure out how, then maybe you\u0026rsquo;ll need to use a Listener or a Subscription.\nUnderstand the Difference Between Actions and State Changes # The sounds banal, but actions are actions and state changes are state changes.\nNote A ChangeListener or a Subscription is primarily a tool to convert a state change into an action.\nUsing a ChangeListener to do nothing more than to propagate state changes through your application is usually a bad idea. Even when the data elements being updated are primitive data types. You\u0026rsquo;re likely better off to put them into Properties and the use Binding to connect them. Yes, there\u0026rsquo;s overhead with Properties, but you\u0026rsquo;re highly unlikely to see any performance improvement by using Listeners and primitives instead.\nThere are some parts of JavaFX that are inherently actions. For instance, running an Animation will require an action (although Animations do have properties that can be bound). So if you need to run an animation in response to a state change, then you\u0026rsquo;ll need to use a Listener or Subscription to do so.\nResponses to user interactions like mouse button clicks are usually actions. Basically, anything that uses an EventHandler of some sort is going to be an action. It\u0026rsquo;s entirely possible to use this code to update state elements that are then going to propagate through your application via Bindings.\nIt\u0026rsquo;s also possible to trigger business/application logic due to a state change. In this case again, Listeners and Subscriptions might be appropriate.\nMaking a change to a layout (meaning to add or remove Nodes), is also generally going to require an action. However\u0026hellip;\nUse Node Properties to Make Your Layouts Behave Dynamically # In a Reactive GUI, static layouts are generally best. These means that you generally do not add or remove Nodes. Two Node Properties are key here: managed and visible. A Node that has visible set to false will not appear in the layout. However, if its managed property is set to true, it will still be allocated space on the layout (which usually means that you\u0026rsquo;ll see an empty space). This is usually not desirable, so you can do:\nnode.managedProperty().bind(node.visibleProperty()) and it will disappear entirely.\nGenerally speaking, unless you\u0026rsquo;re doing something way out on the fringes of normal business applications, there\u0026rsquo;s no advantage to removing Nodes from your layout, and changing the layout is inherently slow. Even if you have hundreds of invisible/unmanaged Nodes in your layout, you won\u0026rsquo;t see any performance degradation as JavaFX won\u0026rsquo;t waste CPU cycles on them.\nThis means that you can have several container classes inside something like a StackPane and have the visible/managed Properties of each container bound to the selected Property of something in a ToggleGroup (like a CheckBox or a ToggleButton). Then only one container will be visible at any given time, depending on which element of the ToggleGroup is selected. To the user it will look like the layout is changing, but it isn\u0026rsquo;t really.\nA Reactive JavaFX Example # Here\u0026rsquo;s about the simplest example I could think up that uses as many of these concepts as possible. A simple password change screen with a requirement that the new password is at least 8 characters long\u0026hellip;\nFirst, we have the Presentation Model.\nclass PresentationModel { val password: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) val okToSave: BooleanProperty = SimpleBooleanProperty(false) init { okToSave.bind(password.map { it.length \u0026gt;= 8 }) } } Presumably whatever business logic that would go along with this screen would require the new password in order to save it. Next, the minimum length of the new password would also be something that would be decided by the business logic, along with any other requirements about the password. So this stuff all goes into the Presentation Model.\nHere\u0026rsquo;s the screen:\nclass ReactiveExample : Application() { private val model = PresentationModel() private val showWarning = model.okToSave.not().and(model.password.isNotEmpty) private val errorPC = PseudoClass.getPseudoClass(\u0026#34;error\u0026#34;) private val warningPC = PseudoClass.getPseudoClass(\u0026#34;warning\u0026#34;) override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 300.0).apply { ReactiveExample::class.java.getResource(\u0026#34;example.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent() = BorderPane().apply { center = createCentre() bottom = Button(\u0026#34;Save\u0026#34;).apply { disableProperty().bind(model.okToSave.not()) } padding = Insets(40.0) } private fun createCentre(): Region = VBox(10.0).apply { children += HBox(6.0).apply { children += Label(\u0026#34;New Password:\u0026#34;) children += TextField().apply { textProperty().bindBidirectional(model.password) } } children += Label(\u0026#34;New password must be at least 8 characters\u0026#34;).apply { styleClass += \u0026#34;status-label\u0026#34; visibleProperty().bind(showWarning) model.password.map { ((it.isNotEmpty()) \u0026amp;\u0026amp; (it.length \u0026lt; 5)) } .subscribe { newVal -\u0026gt; this.pseudoClassStateChanged(errorPC, newVal) } model.password.map { ((it.length \u0026gt;= 5) \u0026amp;\u0026amp; (it.length \u0026lt; 8)) } .subscribe { newVal -\u0026gt; this.pseudoClassStateChanged(warningPC, newVal) } } } } fun main() = Application.launch(ReactiveExample::class.java) First, showWarning is strictly a GUI concept, so it isn\u0026rsquo;t in the Presentation Model, it\u0026rsquo;s just a global variable in the layout code. We also have some PseudoClasses here to control the colour of the status Label. Application of PseudoClasses is inherently an \u0026ldquo;action\u0026rdquo; so we need to use Subscriptions to implement them. Finally, the visibility of the status Label is bound to showWarning, while the Button.disableProperty() is bound to model.okToSave.not().\nHere\u0026rsquo;s the CSS file:\n.root {} .status-label { -fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: black; } .status-label: warning { -fx-text-fill: blue; } .status-label: error { -fx-text-fill: red; } When it launches, it looks like this - with no status label and the Button disabled:\nAs soon as you type a few characters, the warning shows up:\nIt changes to blue when you get closer to having enough characters:\nAnd then the Button enables and the status Label disappears once you have 8 or more characters:\nYou can see here that the layout itself is absolutely static. All of the Nodes in the layout are created, configured and inserted into layout without creating any variable references to them, and they are never accessed by any other code or any other part of the layout. Yet, the Label appears and disappears, changes colour and generally behaves dynamically. The Button changes from disabled to enabled based solely on the Binding that was created when it was added to the layout.\nFinally, whatever business logic is going to save that new password has access to it directly from the Presentation Model. There is no reason to scrape it out of the TextField because the bidirectional Binding keeps it synchronized with the Presentation Model.\nConclusion # This article should have given you the answers to all the questions you\u0026rsquo;ve been scratching your head over about Properties. There\u0026rsquo;s enough information here to get a beginner started on the right track, and it might possibly be years before you\u0026rsquo;ll feel like you need to take a deeper dive into the subject to solve real problems that you encounter with writing real applications. And when you do get there, you can find out everything you\u0026rsquo;ll ever need to know from my Guide To the Obervable Classes.\n","date":"29 September 2024","externalUrl":null,"permalink":"/javafx/elements/beginners-properties/","section":"JavaFX","summary":"If you are a beginner with JavaFX and are wondering, “What are all these Property things anyway?”, then this is the article for you. Besides the Properties themselves, we also cover the basics of using them with Bindings and Listeners. Read this article and you should have a good idea how to start out with JavaFX Properties.","title":"An Introduction to Properties","type":"javafx"},{"content":" Introduction # One of the key elements to creating Reactive applications with JavaFX is understanding how the various Obseravble interfaces and classes work; how they relate to each other and how to use them best.\nIf you surf over to the JavaDoc page for Observable , you\u0026rsquo;ll see lists of subinterfaces and implementing classes that looks like this:\nThat really does look complicated, and in this article we\u0026rsquo;re going to break it down and put some structure to it so that you can understand exactly how you should be using the various classes and interfaces in a thoughtful and logic manner in your applications.\nThe Main Classes and Interfaces # Even though that huge list of interfaces and classes look daunting, there are lots of entries intended to wrap specific data types. If we strip those away, and just concentrate on the classes and interfaces that are defined generically, we can create a diagram like this:\nIn order to understand how this all goes together, it\u0026rsquo;s best to look first at the interfaces, as they tell us what the functionality is, and then look at the classes because they tell us how the functionality is implemented.\nSo we\u0026rsquo;ll start with the interfaces\u0026hellip;\nThe Interfaces # Starting at the Top: Observable # At the top of the chart, you\u0026rsquo;ll find the interface, Observable. This is the root for all of the other classes and interfaces, but it\u0026rsquo;s really quite simple and only defines three methods: addListener(), removeListener() and subscribe(). These are all related to the process called \u0026ldquo;Invalidation\u0026rdquo;.\nInvalidation is the key concept with Obseravbles. When an Observable has become \u0026ldquo;invalidated\u0026rdquo; it means that it might have changed its value, and all of the objects that have registered InvalidationListeners with the Observable have those InvalidationListeners triggered. It\u0026rsquo;s up to these other classes to recheck the value of the Observable inside their Listeners.\nIt\u0026rsquo;s possible for an Observable to be invalidated without having its value change. Consider this code:\nval numberProperty = SimpleIntegerProperty(2) val booleanObservable = numberProperty.greaterThan(5) numberProperty.setValue(4) After the third line of code runs, booleanObservable will have been invalidated, but its value still remains unchanged as false.\nOne important point about invalidation is that Observables remain invalidated until the value is read, at which point they are validated again.\nWarning An Observable that has been invalidated cannot be invalidated again until after it has been revalidated.\nAt this point, we don\u0026rsquo;t have any methods defined that will read and revalidate an Observable\u0026hellip;\nObservableValue # The next interface down is ObservableValue, which extends Observable. The key method in this interface is ObservableValue.getValue(), which allows us to actually read the value and will re-validate the Observable.\nNow that we can read the value we can have the methods related to ChangeListeners, including those related to Subscriptions. The other methods it adds are the map() and flatMap() methods used for transforming an ObservableValue into a different ObservableValue.\nBetween these two interfaces, we get the core functionality for observability: invalidation and changes. Changes, of course, are based on invalidation, and every ChangeListener has, at the heart of its implementation, an InvalidationListener.\nThere is also the ObservableObjectValue\u0026lt;T\u0026gt; interface, which introduces the get() method, which at this point is effectively identical to getValue().\nReadOnlyProperty and WritableValue # These are the two parent interfaces for all of the Property classes.\nReadOnlyProperty is, in my opinion, a useless and annoying interface because it simply introduces the getBean() and getName() methods. These are presumably meaningful methods if you are serializing your Properties, but honestly, who\u0026rsquo;s going to do this? Properties are, by their nature GUI elements and I\u0026rsquo;m not sure how you\u0026rsquo;d ever need Java Beans for them.\nHowever, because of this, all of our concrete Properties will need to implement these two methods.\nThe interface WritableValue gives us the setValue() method. Now we can actually put something into an Observable! There is also WritableObjectValue which adds the set() method, which is (at this point) identical to setValue().\nProperty # The Property interface brings together WritableValue and ObservableValue and adds the ability to bind to other observables. We get bind() and bindBidirectional(), and their corresponding unbinding methods. Also there is the isBound() method that will tell us if the Property has been bound to something.\nYou can see now why binding has to come below WritableValue. Whatever mechanism underlies a binding function will need to call setValue() to work.\nBinding # Binding is the last interface on the chart. It doesn\u0026rsquo;t seem to add much to Observable and ObservableValue except a method to force the Binding to invalidate, a method to check if the Binding is valid and a method to get the dependencies of the Binding.\nThese are all key methods, however. Since a Binding is the main way to connect Observables together, it really works through controlling validation. These methods give the basic tools to construct custom Bindings.\nThe Classes # Everything else on the chart is a class, let\u0026rsquo;s take a look at them.\nObjectExpression # This is a class you might not have heard about, but it is the root class for all of the Property classes, even though the diagram goes diagonally down to ObjectProperty.\nThis class provides the core functionality for the \u0026ldquo;Fluent API\u0026rdquo; for creating bindings. In ObjectExpression we have several versions of asString(), each of which provides a StringBinding. We also have methods for equality, inequality and checking for a Null value, each of which creates a BooleanBinding. That\u0026rsquo;s not really much, but when we look at the typed Observable classes in the Part II we\u0026rsquo;ll see a lot more functionality with the Fluent API.\nThis is the only class in the chart that introduces new concrete methods that don\u0026rsquo;t implement methods already defined by any of the interfaces in the chart.\nInfo Every class that extends from ObjectExpression can use the Fluent API\nReadOnlyObjectProperty # This class extends ObjectExpression and implements ReadOnlyProperty. As we\u0026rsquo;ve seen, there\u0026rsquo;s only two methods in ReadOnlyProperty and they aren\u0026rsquo;t much practical use. ReadOnlyObjectProperty doesn\u0026rsquo;t provide any implementation for these methods either. There really isn\u0026rsquo;t a practical reason to pass an Observable around as a ReadOnlyObjectProperty over an ObjectExpression, but you tend to see ReadOnlyObjectProperty used more often (we\u0026rsquo;ll see why soon).\nReadOnlyObjectProperty can also be thought of as very much like ReadOnlyProperty but with the ability to use the Fluent API to create bindings.\nObjectProperty # The best way to think about ObjectProperty is an implementation of Property but, since it inherits from ObjectExpression through ReadOnlyObjectProperty, also has the methods for creating bindings via the Fluent API. However, it only has implementations for bidirectional binding and setting the value. And it\u0026rsquo;s abstract, so you cannot use it directly without extending it and supplying a ton of functionality.\nThis is, however, a great class to pass your already instantiated Properties around as, or to declare your variables as. Like this:\nval someProperty : ObjectProperty\u0026lt;ClassA\u0026gt; = SimpleObjectProperty(someClassA) This is a bit more versatile than passing around Property\u0026lt;ClassA\u0026gt;, because it will enable the Fluent API.\nObjectPropertyBase # This is the first truly useful abstract class for Properties for extending to create custom classes. This class implements almost all the remaining methods defined in the various interfaces up hierarchy. We get methods to bind and unbind, add and remove Listeners and a get() method.\nThe methods that are missing are the two silly Java Bean related methods. This is the class that you probably want to extend from to create your own Property classes, especially if you want to ignore Java Bean stuff as much you can.\nIf you look at the JavaFX source code, you\u0026rsquo;ll see lots of examples of internally created custom Properties that are made by directly extending ObjectPropertyBase.\nSimpleObjectProperty # This is the class that everyone is familiar with instantiating, as it\u0026rsquo;s the only \u0026ldquo;normal\u0026rdquo; Property class in the chart that isn\u0026rsquo;t abstract.\nWhat makes SimpleObjectProperty different from ObjectPropertyBase?\nIt adds the getBean() and getName() methods and the infrastructure and constructors to set their values. That\u0026rsquo;s it. You\u0026rsquo;re probably never going to use those two methods either, nor are you going to use the constructors to set their values. So you can treat this is pretty much equivalent to ObjectPropertyBase 99% of the time.\nHowever, since this and ObjectPropertyBase do not add any methods to ObjectProperty other than the SimpleObjectProperty constructors, there\u0026rsquo;s no reason to retain a reference to a Property as either SimpleObjectProperty or ObjectPropertyBase. Consider these to be \u0026ldquo;implementation only\u0026rdquo; classes.\nReadOnlyObjectPropertyBase and ReadOnlyObjectWrapper # ReadOnlyObjectPropertyBase is very similar to ObjectPropertyBase in that it is an abstract class that has almost all of the interface methods implemented except for the \u0026ldquo;Bean\u0026rdquo; methods. It\u0026rsquo;s also missing the get() method (you\u0026rsquo;ll see why shortly).\nBut it also seems fairly useless. What\u0026rsquo;s the point of instantiating something as an Obseravble if the value can never change? The answer is in ReadOnlyObjectWrapper\u0026hellip;\nThe ReadOnlyObjectWrapper class extends from SimpleObjectProperty and can be used exactly like SimpleObjectProperty any time you want. However, it has one extra public method: getReadOnlyProperty() that returns a ReadOnlyObjectProperty that is synchronized with the ReadOnlyObjectWrapper that created it.\nWhat\u0026rsquo;s the point of this?\nIt all has to do with preventing client code from casting your \u0026ldquo;read only\u0026rdquo; Observable objects back to something implementing WritableValue. Consider that the following code will run and print \u0026ldquo;Is SimpleObjectProperty: true\u0026rdquo;:\nval x: ObservableValue\u0026lt;String\u0026gt; = SimpleObjectProperty(\u0026#34;\u0026#34;) println(\u0026#34;Is SimpleObjectProperty: ${x is SimpleObjectProperty}\u0026#34;) (x as ObjectProperty).set(\u0026#34;abc\u0026#34;) The variable x was instantiated as SimpleObjectProperty\u0026lt;String\u0026gt; but declared as a reference to an ObsevableValue\u0026lt;String\u0026gt;. The intention being that it should only be observable at this point. However, it\u0026rsquo;s still a SimpleObjectProperty and this cannot be hidden from any client code. There\u0026rsquo;s nothing stopping anyone from taking your ObservableValue\u0026lt;String\u0026gt; and casting it to SimpleObjectProperty or ObjectProperty and then calling its set() or bind() methods.\nHow do you stop this?\nReadOnlyObjectWrapper to the rescue!\nIf you look at the source code for ReadOnlyObjectWrapper you\u0026rsquo;ll find this:\npublic class ReadOnlyObjectWrapper\u0026lt;T\u0026gt; extends SimpleObjectProperty\u0026lt;T\u0026gt; { private ReadOnlyObjectWrapper\u0026lt;T\u0026gt;.ReadOnlyPropertyImpl readOnlyProperty; public ReadOnlyObjectProperty\u0026lt;T\u0026gt; getReadOnlyProperty() { if (this.readOnlyProperty == null) { this.readOnlyProperty = new ReadOnlyPropertyImpl(); } return this.readOnlyProperty; } protected void fireValueChangedEvent() { super.fireValueChangedEvent(); if (this.readOnlyProperty != null) { this.readOnlyProperty.fireValueChangedEvent(); } } private class ReadOnlyPropertyImpl extends ReadOnlyObjectPropertyBase\u0026lt;T\u0026gt; { private ReadOnlyPropertyImpl() { } public T get() { return ReadOnlyObjectWrapper.this.get(); } } } I\u0026rsquo;ve taken the \u0026ldquo;Bean\u0026rdquo; stuff out because it gets in the way.\nThere\u0026rsquo;s the ReadOnlyPropertyBase! And look, it delegates its get() method to the enclosing ReadOnlyObjectWrapper - and the value there can change.\nYou can see from this that getReadOnlyProperty() returns a completely separate Property object that extends ReadOnlyObjectProperty and has it\u0026rsquo;s value connected back to what is essentially a SimpleObjectProperty. And, as you can see from the charts, it does not implement WritableValue so it cannot be cast to any class that supports set().\nThe following code will print \u0026ldquo;Is SimpleObjectProperty: false\u0026rdquo; and then fail with an exception when doing the cast:\nval x: ObservableValue\u0026lt;String\u0026gt; = ReadOnlyObjectWrapper(\u0026#34;\u0026#34;).readOnlyProperty println(\u0026#34;Is SimpleObjectProperty: ${x is SimpleObjectProperty}\u0026#34;) (x as ObjectProperty).set(\u0026#34;abc\u0026#34;) But if we take off the call to getReadOnlyProperty() it will run just like the first example that allows the casting to work.\nThis technique is used inside many of the JavaFX Nodes. Let\u0026rsquo;s look at the source code for Region:\npublic final ReadOnlyDoubleProperty widthProperty() { if (this.width == null) { this.width = new ReadOnlyDoubleWrapper(this._width) { protected void invalidated() { Region.this.widthChanged(this.get()); } public Object getBean() { return Region.this; } public String getName() { return \u0026#34;width\u0026#34;; } }; } return this.width.getReadOnlyProperty(); } Internally, the width Property is implemented as ReadOnlyDoubleWrapper, but Region.widthProperty() returns width.getReadOnlyProperty() which means that you can never cast it to something that supports set(). However, inside of Region the width Property works just like any other read/write Property.\nTip Use ReadOnlyObjectWrapper and its getReadOnlyProperty() method when you really want to make sure that your client code cannot update your Property by casting.\nObjectBinding # This is the sole class on the Binding side of the chart, and it\u0026rsquo;s abstract and it extends ObjectExpression. It implements all of the methods defined in Observable and ObservableValue and inherits those in ObjectExpression.\nIn addition, there are a few protected methods designed to make it easier to create custom classes based on ObjectBinding. We get bind() and unbind() to start with. We also get allowValidation(), onInvalidating() and isObserved(). You are pretty much required to use bind() in any custom class that you extend from ObjectBinding or else your binding is not going to be much use.\nThere is one abstract method, and that\u0026rsquo;s computeValue() which is also protected.\nThe intention is clear about the standard use case for custom classes extended from ObjectBinding. It\u0026rsquo;s expected that you\u0026rsquo;ll either pass the Observables to be bound in the constructor (or use values available in the scope in which your class is defined) and bind them in the constructor of your class. You\u0026rsquo;ll also define a computeValue() method that will use those bound values to determine the return value of any calls to get() or getValue().\nNote Note that computeValue() is only called when a call is made to get() or getValue() when the Binding has been invalidated.\u0026quot;\nObjectBinding has internal storage for the last computed value, and will just return that when a call to get() is made while the Binding is valid.\nSince ObjectBinding extends from ObjectExpression you can use the Fluent API to modify the results or combine it with other ObjectExpressions.\nWe can look to the Bindings utility class for minimal implementation of ObjectBinding:\npublic static \u0026lt;T\u0026gt; ObjectBinding\u0026lt;T\u0026gt; createObjectBinding(final Callable\u0026lt;T\u0026gt; var0, final Observable... var1) { return new ObjectBinding\u0026lt;T\u0026gt;() { { this.bind(var1); } protected T computeValue() { try { return var0.call(); } catch (Exception var2) { Logging.getLogger().warning(\u0026#34;Exception while evaluating binding\u0026#34;, var2); return null; } } public void dispose() { super.unbind(var1); } public ObservableList\u0026lt;?\u0026gt; getDependencies() { return (ObservableList)(var1 != null \u0026amp;\u0026amp; var1.length != 0 ? (var1.length == 1 ? FXCollections.singletonObservableList(var1[0]) : new ImmutableObservableList(var1)) : FXCollections.emptyObservableList()); } }; } Most people don\u0026rsquo;t bother with the logging, the dispose() method or getDependencies() when they create their own custom Binding, but the methods in Bindings are intended for a wide variety of applications.\nReadOnlyJavaBeanObjectProperty # This is in the chart just for the sake of completeness. You aren\u0026rsquo;t going to use it. I\u0026rsquo;ve never seen it used. Just ignore it unless you really, really need to use it - in which case you can research it for yourself.\nWhere the Methods are Defined # In reality, the Observable types don\u0026rsquo;t define that many methods, and they can probably be best understand through a table:\nType Methods Description Observable addListener(InvalidationListener)\nremoveListener(InvalidationListener)\nsubscribe(Runnable) Methods for InvalidationListeners ObservableValue getValue()\naddListener(ChangeListener)\nsubscribe(Consumer)\nsubscribe(BiConsumer)\nmap() \u0026amp; flatMap()\nwhen() Methods for value based Listeners ObservableObjectValue get() WritableValue setValue() The key method for updating and binding WritableObjectValue set() Property bind()\nbindBidirectional()\nunbind()\nunbindBidirectional()\nisBound() All the binding methods ReadOnlyProperty getBean()\ngetName() The two Java Bean methods ObjectExpression asString()\nisEqualTo() \u0026amp; isNotEqualTo()\nisNull() \u0026amp; isNotNull() The Fluent API for object Properties Binding getDependencies()\ninvalidate()\nisValid() The core functionality for a Binding All the other entries in the chart simply implement these methods, but don\u0026rsquo;t introduce any new public methods. So they do not appear in this table.\nUsing this Information # I think that seeing all of this laid out in a charts and a table clarifies something that seems at first to be just mysterious and then later frustrating. But what are the practical implications of knowing this?\nYou Can\u0026rsquo;t Instantiate ObservableValue # Despite all the complexity of the hierarchial chart, any time that you encounter an ObservableValue\u0026lt;T\u0026gt; you know that it was instantiated in one of three ways:\nAs an extension of ObjectPropertyBase\u0026lt;T\u0026gt;, probably SimpleObjectProperty\u0026lt;T\u0026gt; As an extension of ObjectBinding\u0026lt;T\u0026gt;\nThis is true even if it was the result of using the Fluent API or the Bindings class library. As an extension of ReadOnlyPropertyBase\u0026lt;T\u0026gt; from the getReadOnlyProperty() function of a ReadOnlyObjectWrapper instance. This is true of any type on this chart that you receive.\nThe point of this is that how you choose to pass Observables around in your application has more to do with what you want to expose about them than anything else.\nGenerally speaking, you want to pass a type as high up the chart as you can while still providing the functionality that you need. This is going to become important in three places:\nHow you type variables How you return values from methods How you type method parameters The first two pertain to how you expose information about your Properties to other code. Never type a variable as SimpleObjectProperty\u0026lt;T\u0026gt;, use ObjectProperty\u0026lt;T\u0026gt; if you want to use the Fluent API, otherwise just use Property\u0026lt;T\u0026gt;. And only use these two types if you want to be able to call set() or bind().\nThe last item in the list refers to limiting how much you know about Properties passed to you. If all you are going to do with a Property make it the dependency of a Binding, then type it as ObservableValue\u0026lt;T\u0026gt;. If you are going to use it in a Fluent API binding, then type it as ReadOnlyObjectProperty, or ObjectExpression.\nWhat you are trying to do with all of this is to maximize versatility while minimizing risk. Don\u0026rsquo;t ask for something writable if you\u0026rsquo;re not going to call set() or bind() on it. Don\u0026rsquo;t pass something writable if you don\u0026rsquo;t want your client code to update it, pass a ReadOnlyProperty or an ObservableValue in those cases.\nOnly Properties Can be Bound # Yes, Binding\u0026lt;T\u0026gt; is bound internally, but since it only implements Observable and ObservableValue\u0026lt;T\u0026gt; there is no external bind() method that you can call.\nThis means, that if you plan to bind an Observable to anything, you have to have it exposed to you as a Property\u0026lt;T\u0026gt; or an implementing class. The same goes if you want to manually change the value through setValue(). Although you could technically pass WritableValue\u0026lt;T\u0026gt; in that case, although you don\u0026rsquo;t see that often. Perhaps if you wanted someone to be able to set the value but not bind it, you could pass them WritableValue\u0026lt;T\u0026gt;.\nConclusion # I hope that this article clears up most of the confusion that you might have had about what all these Observable types are all about and where they come from. In the next article in this series, we are going to look at the typed Observable classes and interfaces.\n","date":"3 August 2024","externalUrl":null,"permalink":"/javafx/elements/observables/properties/observable-classes/observable-classes-generics/","section":"JavaFX","summary":"The place to start looking at the Observable types is with the generic classes and interfaces. These are the observable wrappers that you can use around any other class of object.\nMore importantly, the hierarchy here is exactly the same as the hierarchy that is used for all of the typed observables. If you can understand how generic observable classes and interfaces relate to each other, without worrying about what kind of data is inside them, and special classes added to handle those types, then you’ll automatically be able to understand how they work when they are typed.\n","title":"Guide To the Observable Classes - Part I","type":"javafx"},{"content":" Introduction # The more that I work with JavaFX, the more that I am convinced that it is one of the best frameworks for building \u0026ldquo;Reactive User Interfaces\u0026rdquo; that\u0026rsquo;s out there. But what is a \u0026ldquo;Reactive User Interface\u0026rdquo;?\nA reactive user interface is one where there is a data representation of the state of the UI (the \u0026ldquo;Presentation Model\u0026rdquo;) which is synchronized with the behaviour and appearance of the UI. This data acts as a pipeline to connect the UI and the business logic without either having any knowledge of the other. The goal is to reduce coupling between the business logic and the implementation of the UI, while maintaining functionality.\nLet\u0026rsquo;s look at how this might work:\nLet\u0026rsquo;s say that in our Presentation Model we have a Boolean element called okToSave. First, notice that it\u0026rsquo;s not called saveButtonEnabled, or nameFieldCompleted. It needs to have a name which is implementation agnostic from either end of the pipeline. saveButtonEnabled implies the implementation of a \u0026ldquo;Save\u0026rdquo; button, while nameFieldCompleted implies that it\u0026rsquo;s dependent on some business rule involving some \u0026ldquo;Name\u0026rdquo; field having a value.\nThe okToSave element implies a \u0026ldquo;contract\u0026rdquo; in the system that the value in okToSave will, at any given time, represent whether or not the UI is in a state where \u0026ldquo;Save\u0026rdquo; is a valid operation. This determination is probably going to be made by the business logic. If I was inclined to use comments - which I generally avoid - this is one of the places where I would use them, and probably in the form of JavaDocs for the getter/setter functions for this element of the Presentation Model: indicating that the business logic is expected to somehow keep this value current and valid at all times.\nIn a reactive framework, okToSave needs to implement the \u0026ldquo;Observer Pattern\u0026rdquo; in some fashion. In JavaFX, we\u0026rsquo;d make it a BooleanProperty. At the GUI end of the pipeline, we treat it like an Observable, meaning that we never deal directly with its value but we connect it as an Observable to some element(s) of the GUI. At the business logic end of the pipeline, we are more concerned with okToSave as a discrete data element. It\u0026rsquo;s possible that we may want to monitor it for changes, in order to trigger some business logic, but it\u0026rsquo;s far more likely that we\u0026rsquo;re going to update its value directly, as part of some business logic.\nThe most important point is that both ends of the pipeline - the UI and the business logic - have independent views of the same data and deal with it in different ways. And both ends of the pipeline have no knowledge of how it\u0026rsquo;s used or maintained at the other end. The UI could simply connect it to the disabled property of the \u0026ldquo;Save\u0026rdquo; button, while the business logic might set it up by connecting it to the values of several other elements in the Presentation Model using some kind of mapping. But both ends can do their job without any knowledge what-so-ever about what\u0026rsquo;s going on at the other end.\nThe opposite technique of \u0026ldquo;reactive\u0026rdquo; is \u0026ldquo;imperative\u0026rdquo; programming. In this kind of programming, you pass a piece of information to the UI and say, \u0026ldquo;Now do this with it\u0026rdquo;, or the UI passes back some information and says, \u0026ldquo;Take this info and check it for me\u0026rdquo;, or something like that. You certainly can write applications this way, and in JavaFX, but it\u0026rsquo;s much, much harder to keep your business logic decoupled from the UI without a lot of really fussy code.\nReactive programming feels more like setting up dominoes in elaborate patterns so that they\u0026rsquo;ll interact with each other in a certain way when you knock one over. In other words, you\u0026rsquo;re telling your layout how to react to changes in the state of the application.\nOkay, there\u0026rsquo;s the background and the reason why we might want to design our application in a reactive fashion. Now, let\u0026rsquo;s talk about how to do that. One issue that seems to come up quite often is how to connect pieces of your application through JavaFX Observable classes. That\u0026rsquo;s the subject of this article.\nState vs Events # Being very, very, very general - and not even directly related to JavaFX - there are two approaches to connecting changes between applications or parts of an application. One is to track things that happen - events or messages - and the other is to connect through State.\nAlso being very, very general - and still not directly related to JavaFX - it\u0026rsquo;s usually safer to connect via State.\nWhy?\nBecause Events are, by their nature, ephemeral. They happen and they are gone, and if you don\u0026rsquo;t capture them when they happen then you\u0026rsquo;re just out of luck. All events need some sort of communication structure, or bus, to travel on, and every system that needs to track events needs to be connected to that bus at the time that the events happen or they won\u0026rsquo;t be aware of the events. There are lots of libraries and systems out there designed to improve message delivery and to make it \u0026ldquo;guaranteed\u0026rdquo;, and there\u0026rsquo;s a reason for this - event/message handling is intrinsically fraught with difficulties.\nOn the other hand, State can be checked at any time and it will always give the correct answer. This means that a system (or subsystem) can query another system about State at the time that it needs the data and can get the current answer. This is independent of whether changes to that State had triggered an event, or whether that event is still sitting on a bus waiting to be captured, or even if there are many events sitting on the bus related to this data.\nWhat this means is that, generally speaking, you should prefer to wire your system together through state when possible. There are occasions when events are the way to go, and we\u0026rsquo;ll talk about that later.\nFor JavaFX programmers, the good news is that JavaFX provides an amazing \u0026ldquo;State Machine\u0026rdquo; with all the features that you need to connect parts of your application via State.\nLet\u0026rsquo;s Get One \u0026ldquo;Issue\u0026rdquo; Out of the Way # Yes, JavaFX is essentially \u0026ldquo;Event Driven\u0026rdquo;, and \u0026ldquo;State\u0026rdquo; is just an illusion in JavaFX.\nDeep, deep \u0026ldquo;under-the-hood\u0026rdquo;, JavaFX is using Event methodology to provide the actions that enable a State Machine. Look down far enough and you\u0026rsquo;ll find InvalidationListeners running everything State related.\nBut, it\u0026rsquo;s probably safe to assume that the core JavaFX libraries are robust, well designed and thoroughly tested and can be treated as essentially foolproof. We can ignore the underlying Event Driven nature of JavaFX and treat its State features as State - and think of them that way.\n\u0026ldquo;State\u0026rdquo; in JavaFX = Bindings # When we think about connecting things via \u0026ldquo;State\u0026rdquo; in JavaFX, that almost exclusively means employing Bindings as the mechanism. There are some edge cases that the standard binding tools cannot handle, and in those cases it may be necessary to use a Listener or a Subscription. But, for the most part, we\u0026rsquo;re talking about Bindings.\nWhen you bind a Property to another ObservableValue, JavaFX will initialize the bound Property to the same value as that of the ObservableProperty it\u0026rsquo;s been bound to, and from that point on it will remain synchronized. No matter what. You can count on this.\nProperties are all over the JavaFX Node classes. Labels, Texts and TextFields all have TextProperty which holds the value that\u0026rsquo;s displayed on the screen. Many of the other input controls have a ValueProperty which serves a similar function. Regions have HeightProperty, WidthProperty, MinHeightProperty, MaxWidthProperty and PaddingProperty amongst many others. These all control how these Nodes are displayed.\nAnd it goes on. Any subclass of Pane has Children, which is an ObservableList of all of the Nodes directly contained inside of it. TableView and ListView have Items, which is an ObservableList of the data displayed in them.\nAll of these Properties and ObservableLists that aren\u0026rsquo;t read-only can be bound to other Properties in your Presentation Model or in other Nodes. The read-only Properties can be bound from by Properties in your Presentation Model or other Nodes.\nAll of Your JavaFX Data Should be Encapsulated in Observables # In order to leverage all of these Properties in the JavaFX Nodes, you need to wrap all of the data in your Presentation Model inside Properties, ObservableValues and ObservableLists. When you\u0026rsquo;ve done this, you can connect these elements of your Presentation Model to the GUI through bindings. Now your Presentation Model truly becomes a data representation of the \u0026ldquo;State\u0026rdquo; of your GUI. You can read it from outside the GUI, and you can alter it from outside the GUI to change the GUI - without knowing about the GUI implementation.\nThe most effective way to do this is to bundle all of your Presentation Model elements into a single class which is just a POJO with JavaFX Observable types as the fields. Don\u0026rsquo;t put any logic or linkages in there, just the fields.\nBindings Should be Your \u0026ldquo;Go To\u0026rdquo; Solution # When designing reactive JavaFX applications, your first approach to any issues should be, \u0026ldquo;How can I do this with Bindings?\u0026rdquo;. This may require that you reorganize your data models or your GUI Nodes. That\u0026rsquo;s fine, it probably will result in a better, more logical data structure anyway.\nYou need to understand how Bindings work and all of the techniques related to Bindings. You can learn a lot of what you need to know from the articles listed on this page.\nSometimes, usually related to ObservableLists, you will come across a situation where a Binding won\u0026rsquo;t work, or you cannot figure out how to write one that will work. In those instances you may have to use a Subscription or a Listener of some sort.\nListeners Convert State Changes to Actions # There are times when it is necessary to perform an action in response to a change in State. This is what Listeners are primarily intended to do. There are some edge cases where Bindings cannot do what you want to do, and you may need to use a Listener to manually update a value based on an ObservableValue. These edge cases usually seem to involve ObservableLists.\nAt times it may not be clear whether the situation calls for a Listener or an Event. Imagine that you have a View that has a Button in it. That Button needs to trigger some activity all the way down in the business logic - which certainly isn\u0026rsquo;t going to be in the View itself. You might be able to trigger that activity by updating some value in the Model, and then put a Listener on that Model element in the Controller or the Interactor. But it\u0026rsquo;s also possible for the Controller to supply the View with a Consumer that it can invoke to explicitly trigger the activity in the business logic.\nMy personal inclination is to go with the second approach, and invoke a Consumer to trigger the activity. There are two reasons for this: First, the Button is going to trigger an action through an EventHandler in any case. Converting that action into a state change in order to detect the state change and trigger another action seems, to me, to be the long way around the square. Secondly, that state change could possibly happen from any number of places other than the Button click, and how is the business logic to know that the action is required?\nAnother use of Listeners is to block changes. Simply create a ChangeListener and in its code have it check conditions to see if the change is allowed. If it isn\u0026rsquo;t, then change it back to the old value. The big thing here is that you might need to add a flag that says, \u0026ldquo;I\u0026rsquo;m putting it back\u0026rdquo;, so that your ChangeListener won\u0026rsquo;t detect this change back as a change it needs to block, creating an infinite loop.\nListeners Don\u0026rsquo;t Launch Event Handlers # An important consideration when using Listeners is to understand how JavaFX executes them. The way that you define them looks very, very similar to the way that you attach an EventHandler to a Node event. But they execute very differently.\nConsider the following code:\nval test = SimpleStringProperty(\u0026#34;abc\u0026#34;) test.addListener { observable, oldValue, newValue -\u0026gt; println(\u0026#34;Old value was: $oldValue, New value is: $newValue\u0026#34;) } println(\u0026#34;Before the change\u0026#34;) test.value = \u0026#34;XYZ\u0026#34; println(\u0026#34;After the change\u0026#34;) Which (perhaps surprisingly) will generate the following output:\nBefore the change Old value was: abc, New value is: XYZ After the change If the code inside the ChangeListener was to be run the same as an EventHandler, submitted to the FXAT as a job, then we would expect to see the following output:\nBefore the change After the change Old value was: abc, New value is: XYZ But it doesn\u0026rsquo;t, and this means that the ChangeListener runs in-line with the code that invokes it. This holds for Bindings as well.\n99% of the time, this is completely irrelevant to your design. But once in a while you\u0026rsquo;ll have a case where you actually need the Listener to execute after everything else, the same way that the screen updates are done after you\u0026rsquo;re code completes. In those cases, put the body of your Listener code in a Platform.runLater() call.\nSubscriptions Simplify Listeners # As of JavaFX 21 we now have a new alternative to Listeners: Subscriptions.\nSubscriptions are essentially wrappers around Listeners but they have two advantages:\nInvoking the subscribe() function on an ObservableValue or Observable will return a reference to the Subscription which can be used to unsubscribe. The subscribe() functions are easier to use as they require less boilerplate code and fewer passed parameters. If you are using JFX or later, you should probably use Subscriptions instead of Listeners 95%+ of the time. There is virtually no advantage in using a Listener over a Subscription, and Subscriptions will give you simpler, cleaner code.\nTo learn more about Subscriptions you can read my article here.\nEventHandlers Describe Actions Triggered by Events # Events are the \u0026ldquo;messages\u0026rdquo; that are passed around your JavaFX application. At any given time there could be hundreds or even thousands of Events active in your JavaFX application.\nIt\u0026rsquo;s easy to get the impression that an EventHandler is \u0026ldquo;triggered\u0026rdquo; by the JavaFX object where it is defined, but this isn\u0026rsquo;t technically correct. A JavaFX object will \u0026ldquo;fire\u0026rdquo; an Event and that Event is passed around through the JavaFX components of the application according to some specific rules. In most cases, it will land back at the JavaFX object that fired the Event (also known as the \u0026ldquo;source\u0026rdquo;) and if it has an EventHandler of the correct type defined on it, that EventHandler will be invoked.\nThis may sound like I\u0026rsquo;m splitting hairs, but this can be an important concept to understand. JavaFX objects will often be passed Events that they did not trigger. JavaFX objects can, therefore, act on Events that they did not trigger. JavaFX objects can also intercept Events that they did not trigger and prevent them from reaching the JavaFX object that fired the Event.\nYou\u0026rsquo;ll see a lot of example code that calls Event.getSource() to determine the Node that called the EventHandler. Most of the time, this will be correct, but depending on the manner in which the EventHandler is called, it\u0026rsquo;s possible that the source of the Event was some other Node.\nOne of the main places that you\u0026rsquo;ll see this used is in the \u0026ldquo;Dispatch EventHandler\u0026rdquo; pattern. This is where a single EventHandler is written, and every Node in the layout invokes that same EventHandler. Inside the EventHandler there is some code that checks Event.getSource() and then branches based on which Node generated the Event.\nMost of this is a waste of time and code. With lambda expressions you can write a one line EventHandler that calls a specific method that does what you want. Something like this:\nbutton.setOnAction{ event -\u0026gt; someMethod() } in Kotlin, we can ditch the event -\u0026gt; if we are not using it (or just let it be it if we are using it), so the code becomes:\nbutton.setOnAction{ someMethod() } There\u0026rsquo;s something about FXML that makes the \u0026ldquo;Dispatch EventHandler\u0026rdquo; attractive to programmers. It\u0026rsquo;s probably just copypasta from on-line examples that define a single \u0026ldquo;#handleClick\u0026rdquo; in the FXML which corresponds to a dispatch method in their FXML Controller.\nIf you are writing an EventFilter then you are probably calling it from the Node that is neither the source nor the target of the Event. In that case, you might want to know which Node is the source or the target. This is one of the few situations where you should be checking the source or the target.\nUsually Events are used when something \u0026ldquo;happens\u0026rdquo; in your GUI or your JavaFX environment. This is most often when the user takes some action, like moving the mouse or clicking a mouse button. But Events can also be fired when the GUI does something like showing the pop-up of a ComboBox or when a Task completes. Note, however, that these really do represent an occurrence of something happening - a moment in time.\nPresumably, these things that happen are things that you want to capture and act upon - otherwise why would you create an EventHandler for them?\nFinally, you can manually fire Events. You can use this to trigger already defined EventHandlers on other Nodes. For instance, you could have a layout with several Buttons, each of which has its own function defined in it ActionEvent handler. One of those Buttons could be a \u0026ldquo;do everything\u0026rdquo; Button, and its EventHandler fires ActionEvents that target the other Buttons, causing their ActionEvent EventHandlers to execute. You could also define your own Event types, and use the Event transport system to pass messages around your layout.\nTask Events # The last kind of Event to think about are those that are generated by Tasks, especially the OnSucceeded Event.\nThis Event is the standard way to get back on the FXAT after running a job on a background thread. The important thing to remember is that this Event can, like any other Event happen at any time. This means that you cannot assume any temporal relationship between launching a Task on a background thread and the OnSucceeded Event being fired. It might happen almost immediately. It might take seconds, or minutes. It might never fire.\nAnd in between; dozens or hundreds or even thousands of other Events might have fired in your GUI. Or not.\nWhat you have to think about, in terms of your GUI, is what \u0026ldquo;happenings\u0026rdquo; are not going to be allowed whilst your Task is running, and how are you going to stop them? Most often, when you define and execute a Task you\u0026rsquo;re already in \u0026ldquo;Action Mode\u0026rdquo;; either by calling an EventHandler or code inside a Listener. Whatever code is closest to that trigger is probably the correct place to deal with disabling the GUI elements that you don\u0026rsquo;t want active while the Task is running. This is probably the EventHandler or Listener code itself.\nBut on the other hand, Tasks are usually created to deal with stuff that needs to run on a background thread because they are likely to block, or might just take a long time to complete. This is usually because you\u0026rsquo;re invoking something that\u0026rsquo;s outside of your GUI like calling an external API.\nAnd that stuff doesn\u0026rsquo;t belong anywhere near your layout code.\nWhich means that your Tasks are probably going to be defined outside of your View, and won\u0026rsquo;t have access to the inside elements of the View. As a matter of fact, that OnSucceeded Event is probably the only Event type that you\u0026rsquo;ll use that occurs outside of your View.\nIt will probably need to do two things: Update your Presentation Model according to business logic, and re-enable the GUI elements that were disabled when the Task was launched.\nThe first is fairly easy, the second requires some finesse. Probably the most direct method is to provide a Callback of some sort for the Task to invoke from the OnSucceeded EventHandler. This Callback is defined in the EventHandler or Listener code that invokes the Task creation outside the View, and passed along to it.\nWhen To Use What # OK, and this is going to sound obvious and banal, but: Actions are actions and state changes are state changes.\nYou need to keep this in mind all the time. Things that are \u0026ldquo;happenings\u0026rdquo; in your GUI, like mouse clicks or Node lifecycle events, pretty much have to result in code defined inside an EventHandler executing. Even if all you are going to do in that code is to change a value in your GUI State. But things that aren\u0026rsquo;t \u0026ldquo;happenings\u0026rdquo; shouldn\u0026rsquo;t generally result in imperative action-style code running.\nThere are two main considerations:\nMost importantly, if your GUI has put you into \u0026ldquo;Action Mode\u0026rdquo;, then respond with an action. Write imperative code that does whatever you need your GUI or business logic to do in a way that you can invoke it directly.\nOn the other hand, state changes are usually best dealt with by setting up Bindings so that you can forget all about them and let them work automatically. If you cannot figure out how to do it with a Binding, then use a Listener or a Subscription so that you can write the imperative code that maintains the connections you want between the data elements.\nIf what you\u0026rsquo;re doing falls outside these two situations, then need to have a good reason to do it that way. Specifically, this means turning an action into a state change, or a state change into an action. Both are possible, even easy to do. But should you?\nSome elements of JavaFX will only respond to actions. For instance, invoking a PseudoCLass state change has to be done via a method call - that\u0026rsquo;s an action, and you can\u0026rsquo;t use Bindings to make it happen. Likewise, programmatically showing the pop-up of a ComboBox requires a method call - so that\u0026rsquo;s an action too.\nSometimes all that an action needs to do is to update State. Something like a Button click that just increments a counter in your Presentation Model - that\u0026rsquo;s defined in the OnAction EventHandler of your Button and would essentially turn an action into a state change.\nKeep in mind that these considerations really apply to your application code, meaning code that is specific to your application or instance of a framework. If you can extrapolate some case of using a state change to trigger an action (or the reverse) into some generic utility code, then you ignore the transition from one type to another.\nConsider the example of how a PseudoClass requires an action to make the method call to transition the state of a PseudoClass on a Node. We can wrap that action up into a custom class extending BooleanProperty (well, BooleanPropertyBase actually) like this:\nclass PseudoClassProperty(private val node: Node, private val pseudoClass: PseudoClass) : BooleanPropertyBase() { override fun getBean() = node override fun getName(): String = pseudoClass.pseudoClassName override fun invalidated() { super.invalidated() node.pseudoClassStateChanged(pseudoClass, value) } } So now we can simply bind an instance of this PseudoClassProperty to whatever Properties in our Presentation Model that we want to, and we don\u0026rsquo;t have to worry about the action nature of the state transition handling because it\u0026rsquo;s not part of our application code any more, it\u0026rsquo;s in some utility library. In our application code, it\u0026rsquo;s just a Binding.\nConclusion # Programmers are used to linear imperative programming. That means, call a method and get an answer. Use that answer to do some more work, maybe call some more methods and get some more answers. Interact with a persistance layer to get and save data. That kind of thing.\nThis approach fails badly when it meets a GUI framework. Mostly because GUI\u0026rsquo;s do not work in a linear imperative manner. Any number of \u0026ldquo;things\u0026rdquo; can happen at any given time, and your GUI needs to react to these \u0026ldquo;things\u0026rdquo; in real time. This means that you generally cannot wait for an answer. So if you\u0026rsquo;re going to go off to an external API to fetch some data, you cannot write that code like a Function where you call it and then you recieve the answer as a return result.\nIt\u0026rsquo;s the \u0026ldquo;linear\u0026rdquo; part that causes the problem. Everyone eventually experiences, \u0026ldquo;I wrote my process with update messages for the GUI but nothing happens. Then the process ends and all of the messages appear at once at the end!\u0026rdquo;. That\u0026rsquo;s because you were thinking linearly and wrote your process to return an answer to the calling code, like a Function.\nProgramming with a GUI requires a paradigm shift.\nIn JavaFX, that shift should result in you approaching your application as a reactive framework.\nWriting your application becomes first about defining the relationships between the data and the Nodes in the GUI. Or between the data in the Nodes and the data in other Nodes. Secondly, it becomes about defining how the application will react when certain things \u0026ldquo;happen\u0026rdquo; in the application. Usually, those \u0026ldquo;happenings\u0026rdquo; are Events, but sometimes they are changes in the data.\nBasically, you are defining how your application will react to changes and events that can happen, literally, at any time and in any order. You cannot get around this, because this is the way that JavaFX works. So embrace the reactive tools that JavaFX gives you and think in a reactive way.\n","date":"1 April 2024","externalUrl":null,"permalink":"/javafx/elements/events_and_listeners/","section":"JavaFX","summary":"Taking a look at how EventHandlers, Listeners, Subscriptions and Bindings are different, and how they should be used in a JavaFX application.","title":"EventHandlers, Listeners and Bindings - What to Use Where","type":"javafx"},{"content":" Introduction # The more that I work with JavaFX, the more that I am convinced that it is one of the best frameworks for building \u0026ldquo;Reactive User Interfaces\u0026rdquo; that\u0026rsquo;s out there. But what is a \u0026ldquo;Reactive User Interface\u0026rdquo;?\nA reactive user interface is one where there is a data representation of the state of the UI (the \u0026ldquo;Presentation Model\u0026rdquo;) which is synchronized with the behaviour and appearance of the UI. This data acts as a pipeline to connect the UI and the business logic without either having any knowledge of the other. The goal is to reduce coupling between the business logic and the implementation of the UI, while maintaining functionality.\nLet\u0026rsquo;s look at how this might work:\nLet\u0026rsquo;s say that in our Presentation Model we have a Boolean element called okToSave. First, notice that it\u0026rsquo;s not called saveButtonEnabled, or nameFieldCompleted. It needs to have a name which is implementation agnostic from either end of the pipeline. saveButtonEnabled implies the implementation of a \u0026ldquo;Save\u0026rdquo; button, while nameFieldCompleted implies that it\u0026rsquo;s dependent on some business rule involving some \u0026ldquo;Name\u0026rdquo; field having a value.\nThe okToSave element implies a \u0026ldquo;contract\u0026rdquo; in the system that the value in okToSave will, at any given time, represent whether or not the UI is in a state where \u0026ldquo;Save\u0026rdquo; is a valid operation. This determination is probably going to be made by the business logic. If I was inclined to use comments - which I generally avoid - this is one of the places where I would use them, and probably in the form of JavaDocs for the getter/setter functions for this element of the Presentation Model: indicating that the business logic is expected to somehow keep this value current and valid at all times.\nIn a reactive framework, okToSave needs to implement the \u0026ldquo;Observer Pattern\u0026rdquo; in some fashion. In JavaFX, we\u0026rsquo;d make it a BooleanProperty. At the GUI end of the pipeline, we treat it like an Observable, meaning that we never deal directly with its value but we connect it as an Observable to some element(s) of the GUI. At the business logic end of the pipeline, we are more concerned with okToSave as a discrete data element. It\u0026rsquo;s possible that we may want to monitor it for changes, in order to trigger some business logic, but it\u0026rsquo;s far more likely that we\u0026rsquo;re going to update its value directly, as part of some business logic.\nThe most important point is that both ends of the pipeline - the UI and the business logic - have independent views of the same data and deal with it in different ways. And both ends of the pipeline have no knowledge of how it\u0026rsquo;s used or maintained at the other end. The UI could simply connect it to the disabled property of the \u0026ldquo;Save\u0026rdquo; button, while the business logic might set it up by connecting it to the values of several other elements in the Presentation Model using some kind of mapping. But both ends can do their job without any knowledge what-so-ever about what\u0026rsquo;s going on at the other end.\nThe opposite technique of \u0026ldquo;reactive\u0026rdquo; is \u0026ldquo;imperative\u0026rdquo; programming. In this kind of programming, you pass a piece of information to the UI and say, \u0026ldquo;Now do this with it\u0026rdquo;, or the UI passes back some information and says, \u0026ldquo;Take this info and check it for me\u0026rdquo;, or something like that. You certainly can write applications this way, and in JavaFX, but it\u0026rsquo;s much, much harder to keep your business logic decoupled from the UI without a lot of really fussy code.\nReactive programming feels more like setting up dominoes in elaborate patterns so that they\u0026rsquo;ll interact with each other in a certain way when you knock one over. In other words, you\u0026rsquo;re telling your layout how to react to changes in the state of the application.\nOkay, there\u0026rsquo;s the background and the reason why we might want to design our application in a reactive fashion. Now, let\u0026rsquo;s talk about how to do that. One issue that seems to come up quite often is how to connect pieces of your application through JavaFX Observable classes. That\u0026rsquo;s the subject of this article.\nState vs Events # Being very, very, very general - and not even directly related to JavaFX - there are two approaches to connecting changes between applications or parts of an application. One is to track things that happen - events or messages - and the other is to connect through State.\nAlso being very, very general - and still not directly related to JavaFX - it\u0026rsquo;s usually safer to connect via State.\nWhy?\nBecause Events are, by their nature, ephemeral. They happen and they are gone, and if you don\u0026rsquo;t capture them when they happen then you\u0026rsquo;re just out of luck. All events need some sort of communication structure, or bus, to travel on, and every system that needs to track events needs to be connected to that bus at the time that the events happen or they won\u0026rsquo;t be aware of the events. There are lots of libraries and systems out there designed to improve message delivery and to make it \u0026ldquo;guaranteed\u0026rdquo;, and there\u0026rsquo;s a reason for this - event/message handling is intrinsically fraught with difficulties.\nOn the other hand, State can be checked at any time and it will always give the correct answer. This means that a system (or subsystem) can query another system about State at the time that it needs the data and can get the current answer. This is independent of whether changes to that State had triggered an event, or whether that event is still sitting on a bus waiting to be captured, or even if there are many events sitting on the bus related to this data.\nWhat this means is that, generally speaking, you should prefer to wire your system together through state when possible. There are occasions when events are the way to go, and we\u0026rsquo;ll talk about that later.\nFor JavaFX programmers, the good news is that JavaFX provides an amazing \u0026ldquo;State Machine\u0026rdquo; with all the features that you need to connect parts of your application via State.\nLet\u0026rsquo;s Get One \u0026ldquo;Issue\u0026rdquo; Out of the Way # Note Yes, JavaFX is essentially \u0026ldquo;Event Driven\u0026rdquo;, and \u0026ldquo;State\u0026rdquo; is just an illusion in JavaFX.\nDeep, deep \u0026ldquo;under-the-hood\u0026rdquo;, JavaFX is using Event methodology to provide the actions that enable a State Machine. Look down far enough and you\u0026rsquo;ll find InvalidationListeners running everything State related.\nBut, it\u0026rsquo;s probably safe to assume that the core JavaFX libraries are robust, well designed and thoroughly tested and can be treated as essentially foolproof. We can ignore the underlying Event Driven nature of JavaFX and treat its State features as State - and think of them that way.\n\u0026ldquo;State\u0026rdquo; in JavaFX = Bindings # When we think about connecting things via \u0026ldquo;State\u0026rdquo; in JavaFX, that almost exclusively means employing Bindings as the mechanism. There are some edge cases that the standard binding tools cannot handle, and in those cases it may be necessary to use a Listener or a Subscription. But, for the most part, we\u0026rsquo;re talking about Bindings.\nWhen you bind a Property to another ObservableValue, JavaFX will initialize the bound Property to the same value as that of the ObservableProperty it\u0026rsquo;s been bound to, and from that point on it will remain synchronized. No matter what. You can count on this.\nProperties are all over the JavaFX Node classes. Labels, Texts and TextFields all have TextProperty which holds the value that\u0026rsquo;s displayed on the screen. Many of the other input controls have a ValueProperty which serves a similar function. Regions have HeightProperty, WidthProperty, MinHeightProperty, MaxWidthProperty and PaddingProperty amongst many others. These all control how these Nodes are displayed.\nAnd it goes on. Any subclass of Pane has Children, which is an ObservableList of all of the Nodes directly contained inside of it. TableView and ListView have Items, which is an ObservableList of the data displayed in them.\nAll of these Properties and ObservableLists that aren\u0026rsquo;t read-only can be bound to other Properties in your Presentation Model or in other Nodes. The read-only Properties can be bound from by Properties in your Presentation Model or other Nodes.\nAll of Your JavaFX Data Should be Encapsulated in Observables # In order to leverage all of these Properties in the JavaFX Nodes, you need to wrap all of the data in your Presentation Model inside Properties, ObservableValues and ObservableLists. When you\u0026rsquo;ve done this, you can connect these elements of your Presentation Model to the GUI through bindings. Now your Presentation Model truly becomes a data representation of the \u0026ldquo;State\u0026rdquo; of your GUI. You can read it from outside the GUI, and you can alter it from outside the GUI to change the GUI - without knowing about the GUI implementation.\nThe most effective way to do this is to bundle all of your Presentation Model elements into a single class which is just a POJO with JavaFX Observable types as the fields. Don\u0026rsquo;t put any logic or linkages in there, just the fields.\nBindings Should be Your \u0026ldquo;Go To\u0026rdquo; Solution # When designing reactive JavaFX applications, your first approach to any issues should be, \u0026ldquo;How can I do this with Bindings?\u0026rdquo;. This may require that you reorganize your data models or your GUI Nodes. That\u0026rsquo;s fine, it probably will result in a better, more logical data structure anyway.\nYou need to understand how Bindings work and all of the techniques related to Bindings. You can learn a lot of what you need to know from the articles listed on this page.\nSometimes, usually related to ObservableLists, you will come across a situation where a Binding won\u0026rsquo;t work, or you cannot figure out how to write one that will work. In those instances you may have to use a Subscription or a Listener of some sort.\nListeners Convert State Changes to Actions # There are times when it is necessary to perform an action in response to a change in State. This is what Listeners are primarily intended to do. There are some edge cases where Bindings cannot do what you want to do, and you may need to use a Listener to manually update a value based on an ObservableValue. These edge cases usually seem to involve ObservableLists.\nAt times it may not be clear whether the situation calls for a Listener or an Event. Imagine that you have a View that has a Button in it. That Button needs to trigger some activity all the way down in the business logic - which certainly isn\u0026rsquo;t going to be in the View itself. You might be able to trigger that activity by updating some value in the Model, and then put a Listener on that Model element in the Controller or the Interactor. But it\u0026rsquo;s also possible for the Controller to supply the View with a Consumer that it can invoke to explicitly trigger the activity in the business logic.\nMy personal inclination is to go with the second approach, and invoke a Consumer to trigger the activity. There are two reasons for this: First, the Button is going to trigger an action through an EventHandler in any case. Converting that action into a state change in order to detect the state change and trigger another action seems, to me, to be the long way around the square. Secondly, that state change could possibly happen from any number of places other than the Button click, and how is the business logic to know that the action is required?\nAnother use of Listeners is to block changes. Simply create a ChangeListener and in its code have it check conditions to see if the change is allowed. If it isn\u0026rsquo;t, then change it back to the old value. The big thing here is that you might need to add a flag that says, \u0026ldquo;I\u0026rsquo;m putting it back\u0026rdquo;, so that your ChangeListener won\u0026rsquo;t detect this change back as a change it needs to block, creating an infinite loop.\nListeners Don\u0026rsquo;t Launch Event Handlers # An important consideration when using Listeners is to understand how JavaFX executes them. The way that you define them looks very, very similar to the way that you attach an EventHandler to a Node event. But they execute very differently.\nConsider the following code:\nval test = SimpleStringProperty(\u0026#34;abc\u0026#34;) test.addListener { observable, oldValue, newValue -\u0026gt; println(\u0026#34;Old value was: $oldValue, New value is: $newValue\u0026#34;) } println(\u0026#34;Before the change\u0026#34;) test.value = \u0026#34;XYZ\u0026#34; println(\u0026#34;After the change\u0026#34;) Which (perhaps surprisingly) will generate the following output:\nBefore the change Old value was: abc, New value is: XYZ After the change If the code inside the ChangeListener was to be run the same as an EventHandler, submitted to the FXAT as a job, then we would expect to see the following output:\nBefore the change After the change Old value was: abc, New value is: XYZ But it doesn\u0026rsquo;t, and this means that the ChangeListener runs in-line with the code that invokes it. This holds for Bindings as well.\n99% of the time, this is completely irrelevant to your design. But once in a while you\u0026rsquo;ll have a case where you actually need the Listener to execute after everything else, the same way that the screen updates are done after you\u0026rsquo;re code completes. In those cases, put the body of your Listener code in a Platform.runLater() call.\nSubscriptions Simplify Listeners # As of JavaFX 21 we now have a new alternative to Listeners: Subscriptions.\nSubscriptions are essentially wrappers around Listeners but they have two advantages:\nInvoking the subscribe() function on an ObservableValue or Observable will return a reference to the Subscription which can be used to unsubscribe. The subscribe() functions are easier to use as they require less boilerplate code and fewer passed parameters. If you are using JFX or later, you should probably use Subscriptions instead of Listeners 95%+ of the time. There is virtually no advantage in using a Listener over a Subscription, and Subscriptions will give you simpler, cleaner code.\nTo learn more about Subscriptions you can read my article here.\nEventHandlers Describe Actions Triggered by Events # Events are the \u0026ldquo;messages\u0026rdquo; that are passed around your JavaFX application. At any given time there could be hundreds or even thousands of Events active in your JavaFX application.\nIt\u0026rsquo;s easy to get the impression that an EventHandler is \u0026ldquo;triggered\u0026rdquo; by the JavaFX object where it is defined, but this isn\u0026rsquo;t technically correct. A JavaFX object will \u0026ldquo;fire\u0026rdquo; an Event and that Event is passed around through the JavaFX components of the application according to some specific rules. In most cases, it will land back at the JavaFX object that fired the Event (also known as the \u0026ldquo;source\u0026rdquo;) and if it has an EventHandler of the correct type defined on it, that EventHandler will be invoked.\nThis may sound like I\u0026rsquo;m splitting hairs, but this can be an important concept to understand. JavaFX objects will often be passed Events that they did not trigger. JavaFX objects can, therefore, act on Events that they did not trigger. JavaFX objects can also intercept Events that they did not trigger and prevent them from reaching the JavaFX object that fired the Event.\nYou\u0026rsquo;ll see a lot of example code that calls Event.getSource() to determine the Node that called the EventHandler. Most of the time, this will be correct, but depending on the manner in which the EventHandler is called, it\u0026rsquo;s possible that the source of the Event was some other Node.\nOne of the main places that you\u0026rsquo;ll see this used is in the \u0026ldquo;Dispatch EventHandler\u0026rdquo; pattern. This is where a single EventHandler is written, and every Node in the layout invokes that same EventHandler. Inside the EventHandler there is some code that checks Event.getSource() and then branches based on which Node generated the Event.\nMost of this is a waste of time and code. With lambda expressions you can write a one line EventHandler that calls a specific method that does what you want. Something like this:\nbutton.setOnAction{ event -\u0026gt; someMethod() } in Kotlin, we can ditch the event -\u0026gt; if we are not using it (or just let it be it if we are using it), so the code becomes:\nbutton.setOnAction{ someMethod() } There\u0026rsquo;s something about FXML that makes the \u0026ldquo;Dispatch EventHandler\u0026rdquo; attractive to programmers. It\u0026rsquo;s probably just copypasta from on-line examples that define a single \u0026ldquo;#handleClick\u0026rdquo; in the FXML which corresponds to a dispatch method in their FXML Controller.\nIf you are writing an EventFilter then you are probably calling it from the Node that is neither the source nor the target of the Event. In that case, you might want to know which Node is the source or the target. This is one of the few situations where you should be checking the source or the target.\nUsually Events are used when something \u0026ldquo;happens\u0026rdquo; in your GUI or your JavaFX environment. This is most often when the user takes some action, like moving the mouse or clicking a mouse button. But Events can also be fired when the GUI does something like showing the pop-up of a ComboBox or when a Task completes. Note, however, that these really do represent an occurrence of something happening - a moment in time.\nPresumably, these things that happen are things that you want to capture and act upon - otherwise why would you create an EventHandler for them?\nFinally, you can manually fire Events. You can use this to trigger already defined EventHandlers on other Nodes. For instance, you could have a layout with several Buttons, each of which has its own function defined in it ActionEvent handler. One of those Buttons could be a \u0026ldquo;do everything\u0026rdquo; Button, and its EventHandler fires ActionEvents that target the other Buttons, causing their ActionEvent EventHandlers to execute. You could also define your own Event types, and use the Event transport system to pass messages around your layout.\nTask Events # The last kind of Event to think about are those that are generated by Tasks, especially the OnSucceeded Event.\nThis Event is the standard way to get back on the FXAT after running a job on a background thread. The important thing to remember is that this Event can, like any other Event happen at any time. This means that you cannot assume any temporal relationship between launching a Task on a background thread and the OnSucceeded Event being fired. It might happen almost immediately. It might take seconds, or minutes. It might never fire.\nAnd in between; dozens or hundreds or even thousands of other Events might have fired in your GUI. Or not.\nWhat you have to think about, in terms of your GUI, is what \u0026ldquo;happenings\u0026rdquo; are not going to be allowed whilst your Task is running, and how are you going to stop them? Most often, when you define and execute a Task you\u0026rsquo;re already in \u0026ldquo;Action Mode\u0026rdquo;; either by calling an EventHandler or code inside a Listener. Whatever code is closest to that trigger is probably the correct place to deal with disabling the GUI elements that you don\u0026rsquo;t want active while the Task is running. This is probably the EventHandler or Listener code itself.\nBut on the other hand, Tasks are usually created to deal with stuff that needs to run on a background thread because they are likely to block, or might just take a long time to complete. This is usually because you\u0026rsquo;re invoking something that\u0026rsquo;s outside of your GUI like calling an external API.\nAnd that stuff doesn\u0026rsquo;t belong anywhere near your layout code.\nWhich means that your Tasks are probably going to be defined outside of your View, and won\u0026rsquo;t have access to the inside elements of the View. As a matter of fact, that OnSucceeded Event is probably the only Event type that you\u0026rsquo;ll use that occurs outside of your View.\nIt will probably need to do two things: Update your Presentation Model according to business logic, and re-enable the GUI elements that were disabled when the Task was launched.\nThe first is fairly easy, the second requires some finesse. Probably the most direct method is to provide a Callback of some sort for the Task to invoke from the OnSucceeded EventHandler. This Callback is defined in the EventHandler or Listener code that invokes the Task creation outside the View, and passed along to it.\nWhen To Use What # OK, and this is going to sound obvious and banal, but: Actions are actions and state changes are state changes.\nYou need to keep this in mind all the time. Things that are \u0026ldquo;happenings\u0026rdquo; in your GUI, like mouse clicks or Node lifecycle events, pretty much have to result in code defined inside an EventHandler executing. Even if all you are going to do in that code is to change a value in your GUI State. But things that aren\u0026rsquo;t \u0026ldquo;happenings\u0026rdquo; shouldn\u0026rsquo;t generally result in imperative action-style code running.\nThere are two main considerations:\nMost importantly, if your GUI has put you into \u0026ldquo;Action Mode\u0026rdquo;, then respond with an action. Write imperative code that does whatever you need your GUI or business logic to do in a way that you can invoke it directly.\nOn the other hand, state changes are usually best dealt with by setting up Bindings so that you can forget all about them and let them work automatically. If you cannot figure out how to do it with a Binding, then use a Listener or a Subscription so that you can write the imperative code that maintains the connections you want between the data elements.\nIf what you\u0026rsquo;re doing falls outside these two situations, then need to have a good reason to do it that way. Specifically, this means turning an action into a state change, or a state change into an action. Both are possible, even easy to do. But should you?\nSome elements of JavaFX will only respond to actions. For instance, invoking a PseudoCLass state change has to be done via a method call - that\u0026rsquo;s an action, and you can\u0026rsquo;t use Bindings to make it happen. Likewise, programmatically showing the pop-up of a ComboBox requires a method call - so that\u0026rsquo;s an action too.\nSometimes all that an action needs to do is to update State. Something like a Button click that just increments a counter in your Presentation Model - that\u0026rsquo;s defined in the OnAction EventHandler of your Button and would essentially turn an action into a state change.\nKeep in mind that these considerations really apply to your application code, meaning code that is specific to your application or instance of a framework. If you can extrapolate some case of using a state change to trigger an action (or the reverse) into some generic utility code, then you ignore the transition from one type to another.\nConsider the example of how a PseudoClass requires an action to make the method call to transition the state of a PseudoClass on a Node. We can wrap that action up into a custom class extending BooleanProperty (well, BooleanPropertyBase actually) like this:\nclass PseudoClassProperty(private val node: Node, private val pseudoClass: PseudoClass) : BooleanPropertyBase() { override fun getBean() = node override fun getName(): String = pseudoClass.pseudoClassName override fun invalidated() { super.invalidated() node.pseudoClassStateChanged(pseudoClass, value) } } So now we can simply bind an instance of this PseudoClassProperty to whatever Properties in our Presentation Model that we want to, and we don\u0026rsquo;t have to worry about the action nature of the state transition handling because it\u0026rsquo;s not part of our application code any more, it\u0026rsquo;s in some utility library. In our application code, it\u0026rsquo;s just a Binding.\nConclusion # Programmers are used to linear imperative programming. That means, call a method and get an answer. Use that answer to do some more work, maybe call some more methods and get some more answers. Interact with a persistance layer to get and save data. That kind of thing.\nThis approach fails badly when it meets a GUI framework. Mostly because GUI\u0026rsquo;s do not work in a linear imperative manner. Any number of \u0026ldquo;things\u0026rdquo; can happen at any given time, and your GUI needs to react to these \u0026ldquo;things\u0026rdquo; in real time. This means that you generally cannot wait for an answer. So if you\u0026rsquo;re going to go off to an external API to fetch some data, you cannot write that code like a Function where you call it and then you recieve the answer as a return result.\nIt\u0026rsquo;s the \u0026ldquo;linear\u0026rdquo; part that causes the problem. Everyone eventually experiences, \u0026ldquo;I wrote my process with update messages for the GUI but nothing happens. Then the process ends and all of the messages appear at once at the end!\u0026rdquo;. That\u0026rsquo;s because you were thinking linearly and wrote your process to return an answer to the calling code, like a Function.\nProgramming with a GUI requires a paradigm shift.\nIn JavaFX, that shift should result in you approaching your application as a reactive framework.\nWriting your application becomes first about defining the relationships between the data and the Nodes in the GUI. Or between the data in the Nodes and the data in other Nodes. Secondly, it becomes about defining how the application will react when certain things \u0026ldquo;happen\u0026rdquo; in the application. Usually, those \u0026ldquo;happenings\u0026rdquo; are Events, but sometimes they are changes in the data.\nBasically, you are defining how your application will react to changes and events that can happen, literally, at any time and in any order. You cannot get around this, because this is the way that JavaFX works. So embrace the reactive tools that JavaFX gives you and think in a reactive way.\n","date":"1 April 2024","externalUrl":null,"permalink":"/javafx/elements/events_and_listeners/","section":"JavaFX","summary":"Taking a look at how EventHandlers, Listeners, Subscriptions and Bindings are different, and how they should be used in a JavaFX application.","title":"EventHandlers, Listeners and Bindings - What to Use Where","type":"javafx"},{"content":" Introduction # I was working on a JFX 21 project last week and IntelliJ came up with ObservableValue.map() as a suggestion. I looked it up in the JavaDocs, I tried it, it worked, and I moved on with what I was doing.\nFor the next few days, however, some thoughts gnawed at my brain: \u0026ldquo;How had I missed this?\u0026rdquo;, and \u0026ldquo;Isn\u0026rsquo;t this is a game-changer?\u0026rdquo;.\nHow had I missed this?\nI went back and looked, and right at the bottom of the entry\u0026hellip; \u0026ldquo;Since 19\u0026rdquo;.\nThen I looked at the JavaDocs page for ObservableValue in both JFX 16 and 21. There are just 3 methods in JFX 16: addListener(), getValue(), and removeListener(). There are 9 methods in the JFX 21 version. Those 6 new methods (plus one in Observable) are what we are going to look at in this article.\nAnd yes, these are game changers!\nObservableValue.map() and ObservableValue.orElse() # Let\u0026rsquo;s start by looking at the JavaDocs for ObservableValue.map():\nReturns an ObservableValue that holds the result of applying the given mapping function on this value. The result is updated when this ObservableValue changes. If this value is null, no mapping is applied and the resulting value is also null.\nHang on! This pretty much describes, in a general way, what every function in the Bindings library does - at least when they are dependant on only one Observable.\nLet\u0026rsquo;s look at an example. First, the old Bindings library way:\nregion.minWidthProperty().bind(Bindings.createDoubleBinding({if (someBooleanProperty.get()) 50.0 else 75.0}, someBooleanProperty)) Here, we are just using a BooleanProperty to control the minWidth of some region in our layout. If the BooleanProperty holds true then we\u0026rsquo;ll set the minWidth to 50.0 otherwise it will be 75.0. Whenever the value in someBooleanProperty changes, then region.minWidthProperty() will change in lock-step.\nNow, with ObservableValue.map():\nregion.minWidthProperty().bind(someBooleanProperty.map{if (it) 50.0 else 75.0}) This does exactly the same thing. Note that ObservableValue.map() returns an ObservableValue wrapping the type defined by the Function passed to it. However, just like with the Bindings library methods, that Function deals with the value inside the ObservableValue and returns a non-observable type. In this example, it is Boolean and the Function returns Double. The function is Function\u0026lt;Boolean, Double\u0026gt;.\nClearly this new method is less code, and easier to understand. It\u0026rsquo;s also easy to view it as an extension of the Fluent API. You can also chain Observable.map() calls together if it makes it more clear.\nAnother benefit is that it\u0026rsquo;s the same function and format for every kind of conversion that you might want to do. Instead of having to pick the correct method from Bindings, just use map and let the compiler infer the types and off you go.\nThere\u0026rsquo;s one hitch\u0026hellip;\nRemember this from the JavaDocs, quoted above?\nIf this value is null, no mapping is applied and the resulting value is also null.\nUh oh! Any ObservableValue can potentially hold a Null value, in which case we\u0026rsquo;ll get a Null back out if it. In our example, do we really want Null if someBooleanProperty holds a Null value? Probably not.\nHere comes orElse() to the rescue:\nregion.minWidthProperty().bind(someBooleanProperty.map{if (it) 50.0 else 75.0}.orElse(60.0)) Once again, this will return an ObservableValue of the right type, but the parameter passed to orElse() is a value of that type, not an ObservableValue.\nYou\u0026rsquo;ll need to keep in mind that since ObservableValue.map() returns an ObservableValue and not a Property of some kind, it cannot be used for a bidirectional binding.\nObservableValue.flatMap() # My first reaction when seeing this was, \u0026ldquo;Ho hum, just another map function for completeness.\u0026rdquo;\nBut then I read the example in the JavaDocs, which starts with this:\nObservableValue\u0026lt;Boolean\u0026gt; isShowing = listView.sceneProperty() .flatMap(Scene::windowProperty) .flatMap(Window::showingProperty) .orElse(false); and the explanation:\nChanges in any of the values of: the scene of listView, the window of that scene, or the showing of that window, will update the boolean value isShowing.\nThis is big!\nOne of the biggest headaches in JavaFX is handling nested Properties, or Properties of Properties. This pops most often when you\u0026rsquo;re dealing with TableView and ListView, or really any other container based on VirtualFlow. That\u0026rsquo;s because the cells have a Property called item and it contains the value currently held in the cell. When that value is a simple object or primitive, like a String then it\u0026rsquo;s easy simply bind some Node in the cell layout to item and it will work.\nBut when item is an object composed of other Observables then it becomes harder to do this. Binding, say, label.textProperty() to item.nameProperty() is problematic because of the reach-through to the component field. When item changes, the binding goes with it, and now label.textProperty() is bound to the nameProperty() of some prior value of item and not the current one.\nThis was particularly irksome with ListView where the items passed to the ListCells are often complex objects composed of Observable fields.\nThere was a way around this, and that was Bindings.select(). It was problematic, though, because it used reflection and depended on the names used for the component fields following a pattern.\nObservableValue.flatMap() fixes that. As a matter of fact, the current JavaDocs for Bindings.select() tell you to use Observable.flatMap(). This is somewhat analogous to how PropertyValueFactory should be replaced with lambda calls after Java 8.\nObservableValue.flatMap() Example # Let\u0026rsquo;s take a look at how this works. One of the most common uses for nested Properties is to display information from the selected item in a TableView. This is usually done by linking through the SelectedItemProperty of a TableView, which, in turn, is normally composed of other Properties.\nHere\u0026rsquo;s a simple example of how this works with ObservableValue.flatMap()\nclass FlatMap : Application() { val selectedItem: ObjectProperty\u0026lt;TableData\u0026gt; = SimpleObjectProperty() override fun start(stage: Stage) { val scene = Scene(createContent(), 620.0, 300.0).apply { addStyleSheet(\u0026#34;/ca/pragmaticcoding/widgetsfx/css/LabelBox.css\u0026#34;) addWidgetStyles() } stage.title = \u0026#34;LabelBox Demo\u0026#34; stage.scene = scene stage.show() } fun createContent() = BorderPane().apply { padding = Insets(20.0, 20.0, 20.0, 20.0) styleClass += \u0026#34;wrapper-region\u0026#34; center = createTable() right = createPane() } private fun createTable(): Region = TableView\u0026lt;TableData\u0026gt;().apply { columns += TableColumn\u0026lt;TableData, String\u0026gt;(\u0026#34;Name\u0026#34;).apply { cellValueFactory = Callback { it.value.nameProperty } } columns += TableColumn\u0026lt;TableData, String\u0026gt;(\u0026#34;Address\u0026#34;).apply { cellValueFactory = Callback { it.value.addressProperty } } items += listOf( TableData(\u0026#34;Wizard of Oz\u0026#34;, \u0026#34;1 Yellow Brick Road\u0026#34;), TableData(\u0026#34;Herman Munster\u0026#34;, \u0026#34;1313 Mockingbird Lane\u0026#34;), TableData(\u0026#34;Norman Bates\u0026#34;, \u0026#34;Bates Motel\u0026#34;) ) selectedItem.bind(selectionModel.selectedItemProperty()) columnResizePolicy = TableView.CONSTRAINED_RESIZE_POLICY_LAST_COLUMN maxWidth = 300.0 } private fun createPane(): Region = VBox(10.0).apply { children += HBox(10.0, Label(\u0026#34;Name: \u0026#34;), Label().apply { textProperty().bind(selectedItem.flatMap { it.nameProperty }) }) children += HBox(10.0, Label(\u0026#34;Address: \u0026#34;), Label().apply { textProperty().bind(selectedItem.flatMap { it.addressProperty }) }) minWidth = 300.0 padding = Insets(15.0) } } class TableData(val name: String, val address: String) { val nameProperty: StringProperty = SimpleStringProperty(name) val addressProperty: StringProperty = SimpleStringProperty(address) } fun main() { Application.launch(MapsAndSubscriptions::class.java) } Our Model for the TableView is TableData, which is a object composed of two StringPropertie, nameProperty and addressProperty. The TableView just has two columns, one for \u0026ldquo;Name\u0026rdquo; and one for \u0026ldquo;Address\u0026rdquo;. Off to the right of the TableView we have a VBox with two Label pairs in HBoxes. One for the name, and one for the address.\nThere\u0026rsquo;s an ObjectProperty\u0026lt;TableData\u0026gt; field in our application. It\u0026rsquo;s bound to the TableView's selectedItemProperty, so it will always reflect the currently selected item in the TableView.\nTwo of the Labels on the right side need to bound to the corresponding Properties in value held in selectedItem, and this is done via flatMap():\ntextProperty().bind(selectedItem.flatMap { it.nameProperty }) and:\ntextProperty().bind(selectedItem.flatMap { it.addressProperty }) Now, when either selectedItem changes, or either of the two Properties that make up that current value changes, the Labels will also change.\nWhen we run this, we first see:\nThen, when we select an item:\nAnd, then another:\nThe one thing that you cannot do with this is to change those data Labels to TextField and bidirectionally bind their TextProperty to the flatMap() value, because it returns ObservableValue and not a Property of some sort.\nObservableValue.subscribe() and Observable.subscribe() # Subscription is another neat new feature, this one added in JFX 21. From the description in the issue, I can paraphrase the situation that they were trying to solve:\nListeners are heavyweight and contain features that application programmers seldom use. ChangeListener is often used when InvalidationListener would suffice, and the references to ObservableValue and the oldValue are seldom seen in application code. Consequently, Listeners are cumbersome to implement in application code. The inclusion of Lambdas and Method references to the language make it increasingly difficult to remove Listeners from Observables.\nThis has been my observation when looking at other programmers\u0026rsquo; projects also. ChangeListener is used almost exclusively, even though InvalidationListener is more appropriate in many cases. At times, the Listener is only used to trigger something which doesn\u0026rsquo;t even need the new value of the Observable.\nPersonally, I now rarely work on the kind of projects where garbage collection is an issue, and defunct Listener references to Observables prevent those Observables from being garbage collected, but I can see the need to be able to remove Listeners without bloating the code base with boilerplate to achieve this.\nThe Differences between InvalidationListener and ChangeListener # Before we go on, it\u0026rsquo;s best to have a quick review about how the two Listener types in JavaFX work.\nThe first concept to understand is \u0026ldquo;invalidation\u0026rdquo;. An Observable is considered to have a \u0026ldquo;valid\u0026rdquo; value when it\u0026rsquo;s, well, valid - or known to be correct. If an Observable is bound to another Observable in some way, and that other Observable changes, then it\u0026rsquo;s possible that the value of the bound Observable has also changed. Under the hood, whenever an Observable is changed, JavaFX gets a list of all the other Observables that are bound to it and changes their status from \u0026ldquo;valid\u0026rdquo;, to \u0026ldquo;invalid\u0026rdquo; (a process called \u0026ldquo;invalidation\u0026rdquo;). Additionally, when an Observable is invalidated, JavaFX gets a list of all of the other Observables that are bound to it and invalidates them, too.\nFinally, Observables are flipped back to \u0026ldquo;valid\u0026rdquo; when get() or getValue() are called on them.\nAn InvalidationListener triggers when an Observable has been invalidated. It doesn\u0026rsquo;t mean that the value has changed, just that it might have changed. Consider the following code:\nval a : IntegerProperty = SimpleIntegerProperty(10) val b : BooleanProperty = a.map{if (it \u0026gt; 20) true else false} b.addListener(InvalidationListener{println(\u0026#34;I was triggered ${b.value}\u0026#34;)}) a.value = a.value + 1 a.value = a.value + 20 After the second line, the value of a was 10 and b was false, b was valid. After the first increment of a, the value of a was 11, and b was invalidated. This would trigger the Listener and, \u0026ldquo;I was triggered false\u0026rdquo; would print. After this printed, b would be valid again. After the last line a was 31 and b was invalidated again. This would trigger the Listener and \u0026ldquo;I was triggered true\u0026rdquo; would print. And then, once again, b would be valid.\nHowever, if we take out the ${b.value} from the println, so that the code looks like this:\nval a : IntegerProperty = SimpleIntegerProperty(10) val b : BooleanProperty = a.map{if (it \u0026gt; 20) true else false} b.addListener(InvalidationListener{println(\u0026#34;I was triggered\u0026#34;)}) a.value = a.value + 1 a.value = a.value + 20 Things are different.\nAfter the second line, the value of a was 10 and b was false, b was valid. After the first increment of a, the value of a was 11, and b was invalidated. This would trigger the Listener and, \u0026ldquo;I was triggered\u0026rdquo; would print. After this printed, b would still be invalid. After the last line a was 31 and b was already invalid. This would not trigger the Listener and nothing would print. And then, at the end, b would still be invalid.\nThe best mental model of a ChangeListener is to think of it as a wrapper around an InvalidationListener, with a place to hold the last value stored in the Observable. When the InvalidationListener triggers, it invokes the Observable.get() method, revalidating the Observable, and comparing the results to the previous value that it has stored. If these two values are different, then it triggers the ChangeListener action.\nThe key differences between InvalidationListener and ChangeListener are that ChangeListener only triggers the action on an actual change of the value, and it always re-validates the Observable automatically, even if the invalidation didn\u0026rsquo;t result in a value change. Beyond that, ChangeListener gives you old and new values, if your logic needs to compare them.\nWith that in mind, let\u0026rsquo;s look at the new subscription facility in JavaFX\u0026hellip;\nUsing Subscriptions # This feature introduces a new Interface called Subscription which allows you to track and combine the subscriptions you\u0026rsquo;ve added to your Observables. Subscriptions are returned from the subscribe() methods. The Subscription interface has methods that allow you to combine Subscriptions and to unsubscribe Subscriptions.\nAll of that sounds very circular. Subscription doesn\u0026rsquo;t really talk about what they do, just how to combine them and get rid of them. The idea is that you can create a compound Subscription that acts as a container for all of your individual Subscriptions so that when you are done with them you can unsubscribe them all at once. And it\u0026rsquo;s all easy, easy, easy to do.\nThere are three subscription() methods; two in ObservableValue and one in Observable. They differ in only the argument that they take. Here\u0026rsquo;s what they do when triggered:\nObservableValue.subscribe(Consumer)\nWill invoke a Consumer that accepts the new value of the ObservableValue. ObservableValue.subscribe(BiConsumer)\nWill invoke a BiConsumer that accepts the old value and the new value of the ObservableValue. Observable.subscribe(Runnable)\nWill invoke a Runnable that does accept not any values. You have to read the JavaDocs fairly carefully to get all the nuances in here. Let\u0026rsquo;s experiment\u0026hellip;\nUsing Observable.subscribe() # Let\u0026rsquo;s look at the first version, which just takes a runnable as its argument. How does this interact with the Invalidation status of the Observable.\nHere\u0026rsquo;s some test code:\nclass MapsAndSubscriptions : Application() { override fun start(stage: Stage) { val scene = Scene(createContent(), 420.0, 300.0).apply { addStyleSheet(\u0026#34;/ca/pragmaticcoding/widgetsfx/css/LabelBox.css\u0026#34;) addWidgetStyles() } stage.title = \u0026#34;LabelBox Demo\u0026#34; stage.scene = scene stage.show() } fun createContent() = BorderPane().apply { padding = Insets(20.0, 20.0, 20.0, 20.0) styleClass += \u0026#34;wrapper-region\u0026#34; center = VBox(20.0, createPane()) } private fun createPane(): Region = VBox(10.0).apply { val counter: IntegerProperty = SimpleIntegerProperty(0) val textArea = TextArea() counter.addListener(InvalidationListener { textArea.text += \u0026#34;Invalidation Listener triggered\\n\u0026#34; }) counter.subscribe(Runnable { textArea.text += \u0026#34;Subscription triggered\\n\u0026#34; }) children += Button(\u0026#34;Click Me\u0026#34;).apply { val buttonCounter: IntegerProperty = SimpleIntegerProperty(0) textProperty().bind(buttonCounter.map { \u0026#34;Click Me $it\u0026#34; }) onAction = EventHandler { textArea.text += \u0026#34;Button clicked ${buttonCounter.value}\\n\u0026#34; counter.value = buttonCounter.value++ } } children += textArea } } fun main() { Application.launch(MapsAndSubscriptions::class.java) } This is going to give us a window with a Button and a TextArea. Every time the Button is clicked, it\u0026rsquo;s going to add some text to the TextArea and update an IntegerProperty called counter. In addition, it will increment another IntegerProperty called buttonCounter which is displayed on the Button through a map().\nAdditionally, there is an InvalidationListener on counter that does not revalidate counter by calling its get() method. Nor does the Subscription.\nLet\u0026rsquo;s see what happens when you click the Button a few times:\nOn the first click, buttonCounter is 0 and it sets counter to 0, which is what it already was. So this does not invalidate counter.\nOn the second click, buttonCounter is 1 and it sets counter to 1, which then invalidates counter. Both the InvalidationListener and the Subscription are triggered.\nOn the subsequent clicks, counter is incremented, but it\u0026rsquo;s already been invalidated so neither the InvalidationListener nor the Subscription are triggered again.\nNow, let\u0026rsquo;s change one line of code; the line that creates the InvalidationListener:\ncounter.addListener(InvalidationListener { textArea.text += \u0026#34;Invalidation Listener triggered ${counter.value}\\n\u0026#34; }) This calls counter.get() through the Kotlin property accessor value. So this will revalidate counter. Here\u0026rsquo;s the output:\nFinally, we put the InvalidationListener code back the way it started, and change the Subscription line so that it will revalidate counter:\ncounter.subscribe(Runnable { textArea.text += \u0026#34;Subscription triggered ${counter.value}\\n\u0026#34; }) From this, I think it\u0026rsquo;s safe to assume that Observable.subscribe() is analogous to adding an InvalidationListener to your Observable. This is in agreement with the JavaDocs, which state:\nThe provided subscriber is akin to an InvalidationListener without the Observable parameter.\nObservableValue.subscribe() # Now let\u0026rsquo;s look at the two methods defined in ObservableValue. These are much more like ChangeListener.\nWith a Consumer # The next version of subcribe() that we\u0026rsquo;ll look at is the one that takes a Consumer. We\u0026rsquo;ll leave all the rest of the example code alone, including the InvalidationListener, but change the subscription:\ncounter.subscribe(Consumer { textArea.text += \u0026#34;Subscription triggered \\n\u0026#34; }) Note that we\u0026rsquo;re not accessing the value passed to the Consumer here. This is what we get:\nThis is almost what you\u0026rsquo;d expect. Nothing happens after the first Button click because the value doesn\u0026rsquo;t change, it stays 0. After that, the Subscription, in getting the value to pass to the Consumer is revalidating our Property. So we expect to see both the Subscription and the InvalidationListener trigger on each subsequent Button click.\nBut the very first \u0026ldquo;Subscription Triggered\u0026rdquo; message is interesting. It clearly happens when the Subscription is created. Let\u0026rsquo;s check the JavaDocs on that:\nCreates a Subscription on this ObservableValue which immediately provides the current value to the given valueSubscriber, followed by any subsequent values whenever its value changes. The valueSubscriber is called immediately for convenience, since usually the user will want to initialize a value and then update on changes.\nAh! When I first read this it wasn\u0026rsquo;t clear to me what \u0026ldquo;immediately provides\u0026rdquo; meant. Now it\u0026rsquo;s clear, as is the explanation about initializing a value as a use case.\nAside from this, it\u0026rsquo;s safe to consider this version of subscribe() to be about the same as a ChangeListener that only passes the newVal to its Handler.\nWith a BiConsumer # Now we\u0026rsquo;ll swap out the Subscription to use the last version, the one that passes both the old and the new values to a BiConsumer:\ncounter.subscribe { _: Number, _: Number -\u0026gt; textArea.text += \u0026#34;Subscription triggered \\n\u0026#34; } This is the Kotlin version of a BiConsumer. We\u0026rsquo;re not going to use the values passed in, so _ is used a a placeholder.\nThe output looks like this: This is also pretty much what you\u0026rsquo;d expect, except that we don\u0026rsquo;t get the initial firing of the Subscription when it\u0026rsquo;s created. Check the JavaDocs and you\u0026rsquo;ll see that there is no mention of \u0026ldquo;immediately provides\u0026rdquo; for this version of subscribe().\nInvalidation Without a Value Change # We know from the discussion above, that an Observable can invalidate because something that it\u0026rsquo;s bound to has been invalidated, while its own value doesn\u0026rsquo;t actually change. Let\u0026rsquo;s see how that works with Subscriptions. We\u0026rsquo;re going to add a new Observable to our code, that is bound to counter through a map that means it wont always change when counter` changes.\nval counter: IntegerProperty = SimpleIntegerProperty(0) val boundCounter = counter.map { if ((it as Int) \u0026lt; 3) 10 else 20 } boundCounter.addListener(InvalidationListener { textArea.text += \u0026#34;Invalidation Listener triggered\\n\u0026#34; }) boundCounter.subscribe { _: Number, _: Number -\u0026gt; textArea.text += \u0026#34;Subscription triggered \\n\u0026#34; } All the rest of the code is the same. Here\u0026rsquo;s the output: This is what I would have expected (hoped?) would happen. For the second and third button clicks, counter changes and invalidates which, in turn, invalidates boundCounter. At the same time, the Subscription, which doesn\u0026rsquo;t trigger the BiConsumer, does re-validate the Observables because it needs to call get() on boundCounter in order to decide if it has changed.\nWe\u0026rsquo;ll try the same thing, but with the Consumer version: This is exactly the same, except that we have that initial triggering when the Subscription is created.\nWhich to Use? # I think that for 95% of use cases - which is when you really want to trigger when the value actually changes, but you don\u0026rsquo;t care what the previous value was - then you should just use the ObservableValue.subscribe(Consumer) version. Use this even if you don\u0026rsquo;t want to use the new value in anything directly. This is going to free you up from having to worry about invalidation issues. On those few cases that you do need the previous value, then use the BiConsumer version.\nI would reserve the Runnable version for those rare cases when you really do care about the difference between invalidation and change, and you want to fire whenever anything in your chain of bound Properties changes.\nThe other big thing to think about is this initial firing of the Consumer version when it\u0026rsquo;s initiated. I\u0026rsquo;ve seen applications where a Property is used to connect to modules of a together, and it\u0026rsquo;s passed from the first module to the second via a constructor parameter. Then the dependent module uses a ChangeListener to do something when the controlling module changes the value of the Property. In that situation, you usually want to run whatever code executes when the value changes when you first get the property, because often it already has a valid value in it. So you\u0026rsquo;re forced to put the ChangeListener code into a method so that you can manually force it to run from the constructor. With this version of Subscription, you don\u0026rsquo;t need to do that.\nCombining and Unsubscribing # One of the big advantages to Subscriptions is that it\u0026rsquo;s easier to remove them than the corresponding Listener type, especially when the Listener was declared via a lambda or method reference. This is because the removeListener() functions take a reference to Listener to be removed. For instance, in our example code above, we have this:\nboundCounter.addListener(InvalidationListener { textArea.text += \u0026#34;Invalidation Listener triggered\\n\u0026#34; }) If we want to be able to remove this listener, then we have to do this:\nval listener = InvalidationListener { textArea.text += \u0026#34;Invalidation Listener triggered\\n\u0026#34; } boundCounter.addListener(listener) and then, later on we can do:\nboundCounter.removeListener(listener) While addListener() has a return type of void, subscribe() has a return type of Subscription. And the Subscription interface has a method called unsubscribe. Back to our example code, we can do this:\nval subscription = boundCounter.subscribe { _: Number, _: Number -\u0026gt; textArea.text += \u0026#34;Subscription triggered \\n\u0026#34; } Which means that later we can do this:\nsubscription.unsubscribe() Now, if you have a number of Subscriptions and you don\u0026rsquo;t want to keep track of them individually throughout your code, you can do something like this:\nval subscriptions = Subscription.EMPTY . . subscriptions.add(boundCounter.subscribe { _: Number, _: Number -\u0026gt; textArea.text += \u0026#34;Subscription triggered \\n\u0026#34; }) . . subscriptions.unsubscribe() alternatively, you can do something like this:\nval subscriptions = Subscription.combine(subscription1, subscription2, subscription3) . . subscriptions.unsubscribe() ObservableValue.when() # Observable.when() is described primarily as a means to avoid having defunct references which prevent garbage collection of otherwise unused Properties. But how does it work, and what can we expect to see when we use when()\nObservableValue.when() Example # class WhenApplication : Application() { override fun start(stage: Stage) { val scene = Scene(createContent(), 420.0, 300.0).apply { addStyleSheet(\u0026#34;/ca/pragmaticcoding/widgetsfx/css/LabelBox.css\u0026#34;) addWidgetStyles() } stage.title = \u0026#34;LabelBox Demo\u0026#34; stage.scene = scene stage.show() } fun createContent() = BorderPane().apply { padding = Insets(20.0, 20.0, 20.0, 20.0) styleClass += \u0026#34;wrapper-region\u0026#34; center = VBox(20.0, createPane()) } private fun createPane(): Region = VBox(10.0).apply { val textArea = TextArea() val bindingEnabled: BooleanProperty = SimpleBooleanProperty(false) val counter: IntegerProperty = SimpleIntegerProperty(0) val boundCounter: IntegerProperty = SimpleIntegerProperty(25) boundCounter.bind(counter.`when`(bindingEnabled).map { it.toInt() + 17 }) boundCounter.subscribe { it: Number -\u0026gt; textArea.text += \u0026#34;Subscription triggered $it\\n\u0026#34; } bindingEnabled.subscribe { it: Boolean -\u0026gt; textArea.text += \u0026#34;Binding enabled: $it\\n\u0026#34; } children += Button(\u0026#34;Click Me\u0026#34;).apply { val buttonCounter: IntegerProperty = SimpleIntegerProperty(0) textProperty().bind(buttonCounter.map { \u0026#34;Click Me $it\u0026#34; }) onAction = EventHandler { textArea.text += \u0026#34;Button clicked ${buttonCounter.value}\\n\u0026#34; counter.value = buttonCounter.value++ } } children += CheckBox(\u0026#34;Binding On\u0026#34;).apply { selectedProperty().bindBidirectional(bindingEnabled) } children += HBox(10.0, Label(\u0026#34;Bound Counter:\u0026#34;), Label().apply { textProperty().bind(boundCounter.asString()) }) children += textArea } } A quick Kotlin note: when is a reserved keyword in Kotlin, so to use when() we need put the \u0026ldquo;when\u0026rdquo; in backticks.\nHere we have a couple if IntegerProperties, one called boundCounter which is bound to the other, called counter through a map() and a when(). The map() just adds 17 to the value in counter. There is a BooleanProperty called bindingEnabled which is bound bidirectionally to a CheckBox.\nWith this we can control the when() with the CheckBox and increment counter via the Button. Then we can see how it works.\nLet\u0026rsquo;s look at the output from this. First, once the application has been started and the Button has been clicked twice:\nWe see the output from creating the two Subscriptions, as they fire immediately. Interestingly, even though the value in bindingEnabled is false, and boundCounter was initialised to 25, it\u0026rsquo;s shows as 17 when the Subscription was added. There\u0026rsquo;s evidence of two Button clicks, and counter now has a value of 1 (remember, it\u0026rsquo;s one behind the number shown on the Button). Yet the value in boundCounter remains at 17.\nNext, we\u0026rsquo;ll click on the CheckBox to active the when() and click the Button again. After that, we\u0026rsquo;ll unselect the CheckBox:\nWe see the output from the two subscriptions when the when() is activated. One is for bindingEnabled changing to true, and the other is when boundCounter is updated because the when() is activated. Then the Button is clicked, and we see the subscription on boundCounter firing, telling us the value in now 19. Finally, we see the bindingEnabled subscription firing when the CheckBox was unselected. The current value of boundCounter remains 19.\nFinally, we\u0026rsquo;ll click the Button again:\nHere we see Button click message, but nothing else. The value of boundCounter remains 19.\nConclusion # In my opinion, ObservableValue.map(), and the Subscription methods are \u0026ldquo;game changers\u0026rdquo; for writing Reactive JavaFX applications. Using these methods will result in cleaner, simpler code with less boilerplate. By themselves, these are reason enough to start using JFX 21 right now.\n","date":"18 February 2024","externalUrl":null,"permalink":"/javafx/elements/subscribe_and_map/","section":"JavaFX","summary":"In JavaFX 19 and 21, new methods have been added to the Observables library that should change the way you write Bindings and Listeners","title":"Use Subscriptions Instead of Listeners","type":"javafx"},{"content":" Introduction # In JavaFX, ListViews are everywhere. They\u0026rsquo;re used as the pop-up in ComboBoxes and some other controls, so many programmers are familiar with them even if they haven\u0026rsquo;t directly used ListView. Most programmers, however, never go very deep into understanding ListView, they\u0026rsquo;ll simply create an ObservableList of String and then stuff it into a vanilla ListView to put a scrolling list on the screen. To do anything more interesting, they\u0026rsquo;ll jump over to TableView instead.\nBut ListView can do so much more! In many cases, a ListView can be a much more appropriate vehicle to display complex data than TableView.\nIn this article, we\u0026rsquo;ll begin by showing the simplest usage of ListView, then we\u0026rsquo;ll take a look at how to do something much more involved.\nThe Basic ListView # The simplest implementation of a ListView is to use it to display a list of Strings or some other object that supports toString() nicely. In this case, you just create the ListView, connect it to your ObservableList and you\u0026rsquo;re done. Let\u0026rsquo;s take a look at that:\npublic class BasicListViewApplication extends Application { @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); } private Parent createContent() { ListView\u0026lt;String\u0026gt; listView = new ListView\u0026lt;\u0026gt;(); ObservableList\u0026lt;String\u0026gt; animals = FXCollections.observableArrayList(\u0026#34;Bear\u0026#34;, \u0026#34;Bird\u0026#34;, \u0026#34;Hedgehog\u0026#34;, \u0026#34;Lobster\u0026#34;, \u0026#34;Owl\u0026#34;, \u0026#34;Shark\u0026#34;); listView.setItems(animals); return listView; } } And it looks like this:\nUsing Non-String Values # Anything that can support Object.toString() can be used as a data type in a ListView. We\u0026rsquo;re going to use a custom Enum type to show some more techniques, so let\u0026rsquo;s introduce it here and show how it works with a basic ListView\npublic enum Animal { BEAR(\u0026#34;Bear\u0026#34;, \u0026#34;bear.png\u0026#34;), BIRD(\u0026#34;Bird\u0026#34;, \u0026#34;bird.png\u0026#34;), HEDGEHOG(\u0026#34;Hedgehog\u0026#34;, \u0026#34;hedgehog.png\u0026#34;), LOBSTER(\u0026#34;Lobster\u0026#34;, \u0026#34;lobster.png\u0026#34;), OWL(\u0026#34;Owl\u0026#34;, \u0026#34;owl.png\u0026#34;), SHARK(\u0026#34;Shark\u0026#34;, \u0026#34;shark.png\u0026#34;); private final ImageView imageView; private final String name; Animal(String name, String imageName) { this.name = name; this.imageView = new ImageView(BasicListViewApplication.class.getResource(\u0026#34;images/\u0026#34; + imageName).toExternalForm()); } public ImageView getImageView() { return imageView; } public String getName() { return name; } @Override public String toString() { return name; } } Using an ImageView right in the Enum isn\u0026rsquo;t really a good idea, but it\u0026rsquo;s a little cheat that will keep the first example that uses it simple. We\u0026rsquo;ll fix this up later.\nand now the application would look like this:\npublic class BasicListViewApplication extends Application { @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); } private Parent createContent() { ListView\u0026lt;Animal\u0026gt; listView = new ListView\u0026lt;\u0026gt;(); ObservableList\u0026lt;Animal\u0026gt; animals = FXCollections.observableArrayList(Animal.BEAR, Animal.BIRD, Animal.HEDGEHOG, Animal.LOBSTER, Animal.OWL, Animal.SHARK); listView.setItems(animals); return listView; } } Which gives exactly the same display as before. We just let the Animal.toString() function do the heavy lifting.\nFor a lot of programmers, that\u0026rsquo;s all they\u0026rsquo;ll ever did with ListView, and that\u0026rsquo;s a shame. ListView can do so much more.\nCustomizing the Display # The basic display unit of a ListView is the ListCell. ListCell is a subclass of Labeled which means that, in many ways, it behaves just like a Label. Most importantly, it has both a Graphic and a Text component, both of which can be displayed together or independantly. Ordinarily, we just see the Text component as the default behaviour of ListCell is just to use toString() on the value and put it in Text component.\nLoading Custom Cells # You can customize the ListCell to make its behaviour change. This involves first telling your ListView that it needs to load a custom ListCell. This is done via the ListView.setCellFactory() method. Let\u0026rsquo;s look at the signature of this:\npublic final void setCellFactory​(Callback\u0026lt;ListView\u0026lt;T\u0026gt;,​ListCell\u0026lt;T\u0026gt;\u0026gt; value) This method takes a Callback. A Callback is basically the same as a Function, but the name implies that it\u0026rsquo;s supposed to be passed as a parameter of a method which will invoke the Function (it will \u0026ldquo;call back\u0026rdquo; to the Function) as part of what it does.\nIn this case, we expect that some piece of internal JavaFX code will take our ListView, pass it to the Callback and get a ListCell back. So we need to write the logic in our Callback which will create our custom ListCell on demand.\nHere\u0026rsquo;s the code that will do that:\npublic class BasicListViewApplication extends Application { @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); } private Parent createContent() { ListView\u0026lt;Animal\u0026gt; listView = new ListView\u0026lt;\u0026gt;(); listView.setCellFactory(lv -\u0026gt; createCell()); ObservableList\u0026lt;Animal\u0026gt; animals = FXCollections.observableArrayList(Animal.BEAR, Animal.BIRD, Animal.HEDGEHOG, Animal.LOBSTER, Animal.OWL, Animal.SHARK); listView.setItems(animals); return listView; } private ListCell\u0026lt;Animal\u0026gt; createCell() { return new ListCell\u0026lt;\u0026gt;() { @Override public void updateItem(Animal item, boolean isEmpty) { super.updateItem(item, isEmpty); if (!isEmpty \u0026amp;\u0026amp; (item != null)) { setGraphic(item.getImageView()); setText(item.getName()); } else { setGraphic(null); setText(null); } } }; } } The line:\nlistView.setCellFactory(lv -\u0026gt; createCell()); sets the Callback. The createCell() method does the work, which in this case is to create an anonymous inner class which extends ListCell and overrides updateItem().\nWhen you run it, it looks like this:\nAbout Cell.updateItem() # Every ListCell has to support ListCell.updateItem() and the super class that this comes from is Cell, not ListCell. Cell.updateItem() is the basic mechanism by which the data is put into your ListCell. It passes two values: the actual data, and a flag that indicates if the ListCell is empty. This second element is used when your ListView's viewport is longer than the number of items in the list, and it needs to leave some of the rows empty.\nIt\u0026rsquo;s important that we clear out all of the visual elements when isEmpty is true. Otherwise we\u0026rsquo;ll get random data in our empty rows (maybe).\nIn our custom ListCell, we\u0026rsquo;re simply putting values into the Graphic and Text elements of the standard ListCell, pulling them from the appropriate parts of item when it\u0026rsquo;s loaded in.\nOne of the most important aspects of updateItem() is that it must call super.updateItem(). We\u0026rsquo;ll see why in a little bit, but for now you should know that if you forget to do this (and you will), then you\u0026rsquo;re likely to get very strange results in your ListView.\nUnderstanding VirtualFlow # ListView is one of the screen elements in JavaFX that uses something called VirtualFlow. This is a class that extends Region and is used in the skins of a number of classes with many elements inside a scrolling region. We\u0026rsquo;ve already seen one aspect of this with our call to setCellFactory(), which is part of the VirtualFlow interface.\nOne of the main characteristics of VirtualFlow is that is a virtualized region of many elements. What this means is that you\u0026rsquo;ll have a limited number of real screen elements, but they are used and re-used over and over to present a potentially unlimited number of elements in whatever list is backing it. In terms of ListView, this means that if the ObservableList that you install using ListView.setItems() is huge, you\u0026rsquo;ll still only have a limited number of ListCells created to display those on the screen. Typically, this amount is only slightly more than the number of rows that can be displayed on the screen at one time. If you do something at runtime to make the viewport of the ListView bigger, then it will create a few more ListCells so that it can fill the viewport.\nWhy do you care about this?\nYou care about this because this radically alters the approach you need to take when designing your ListCells. For the most part, all of the ListCells that you\u0026rsquo;ll have are created when the ListView is first shown on the screen. Then those handful of ListCells are going to be loaded via Cell.updateItem() with the data from the underlying List that are going to be shown in the viewport of the ListView. As the user scrolls through the ListView, some items will scroll out of the viewport and off the screen, the ListCells that held them are now free to be resused, and they will be filled with data from items that are scrolling into the viewport.\nThe implication of this is that Cell.updateItem() is going to get called a lot.\nSeriously\u0026hellip;a lot.\nSo you want it to run lightning fast. And this is why the commentary from the JavaDocs is misleading:\nThe updateItem method should not be called by developers, but it is the best method for developers to override to allow for them to customise the visuals of the cell.\nand then:\nHowever, the purpose of having the updateItem method is so that developers, when specifying custom cell factories (again, like the ListView cell factory), the updateItem method can be overridden to allow for complete customisation of the cell.\nIf you do as this says, and \u0026ldquo;customise the visuals of the cell\u0026rdquo; in Cell.updateItem(), then your application will choke when the users scroll through your ListView with any speed. That\u0026rsquo;s because creating a layout in JavaFX is fairly heavy-weight, and it\u0026rsquo;s slow.\nEverybody knows this.\nEverbody understands that a better approach is to create your layout in your ListCell constructor and then just put the data into it via Cell.updateItem().\nLet\u0026rsquo;s take a look at this with our Animals example:\nCustomizing the Graphic Layout # The first thing we\u0026rsquo;re going to do is clean up Animals and remove the ImageView, which is a screen Node and replace it with Image, which isn\u0026rsquo;t a screen Node:\npublic enum Animal { BEAR(\u0026#34;Bear\u0026#34;, \u0026#34;bear.png\u0026#34;), BIRD(\u0026#34;Bird\u0026#34;, \u0026#34;bird.png\u0026#34;), HEDGEHOG(\u0026#34;Hedgehog\u0026#34;, \u0026#34;hedgehog.png\u0026#34;), LOBSTER(\u0026#34;Lobster\u0026#34;, \u0026#34;lobster.png\u0026#34;), OWL(\u0026#34;Owl\u0026#34;, \u0026#34;owl.png\u0026#34;), SHARK(\u0026#34;Shark\u0026#34;, \u0026#34;shark.png\u0026#34;); private final Image image; private final String name; Animal(String name, String imageName) { this.name = name; this.image = new Image(BasicListViewApplication.class.getResource(\u0026#34;images/\u0026#34; + imageName).toExternalForm()); } public Image getImage() { return image; } public String getName() { return name; } @Override public String toString() { return name; } } Now we can\u0026rsquo;t just take the ImageView and put it into Graphic, we need to create a layout in the ListCell that has an ImageView in it, then stuff the Image into the ImageView through Cell.updateItem():\npublic class BasicListViewApplication extends Application { @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); } private Parent createContent() { ListView\u0026lt;Animal\u0026gt; listView = new ListView\u0026lt;\u0026gt;(); listView.setCellFactory(lv -\u0026gt; createCell()); ObservableList\u0026lt;Animal\u0026gt; animals = FXCollections.observableArrayList(Animal.BEAR, Animal.BIRD, Animal.HEDGEHOG, Animal.LOBSTER, Animal.OWL, Animal.SHARK); listView.setItems(animals); return listView; } private ListCell\u0026lt;Animal\u0026gt; createCell() { return new ListCell\u0026lt;\u0026gt;() { private final ImageView imageView = new ImageView(); @Override public void updateItem(Animal item, boolean isEmpty) { super.updateItem(item, isEmpty); if (!isEmpty \u0026amp;\u0026amp; (item != null)) { imageView.setImage(item.getImage()); setGraphic(imageView); setText(item.getName()); } else { setGraphic(null); setText(null); } } }; } } That\u0026rsquo;s not really a whole lot of change, but it\u0026rsquo;s philosophically huge. Let\u0026rsquo;s go one step further and put the ImageView inside a styled StackPane so that it looks like a layout:\npublic class BasicListViewApplication2 extends Application { @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); } private Parent createContent() { ListView\u0026lt;Animal\u0026gt; listView = new ListView\u0026lt;\u0026gt;(); listView.setCellFactory(lv -\u0026gt; createCell()); ObservableList\u0026lt;Animal\u0026gt; animals = FXCollections.observableArrayList(Animal.BEAR, Animal.BIRD, Animal.HEDGEHOG, Animal.LOBSTER, Animal.OWL, Animal.SHARK); listView.setItems(animals); return listView; } private ListCell\u0026lt;Animal\u0026gt; createCell() { return new ListCell\u0026lt;\u0026gt;() { private final StackPane layout = new StackPane(); private final ImageView imageView = new ImageView(); { layout.getChildren().add(imageView); layout.setPadding(new Insets(3)); layout.setBackground(new Background(new BackgroundFill(Color.CYAN, null, null))); } @Override public void updateItem(Animal item, boolean isEmpty) { super.updateItem(item, isEmpty); if (!isEmpty \u0026amp;\u0026amp; (item != null)) { imageView.setImage(item.getImage()); setGraphic(layout); setText(item.getName()); } else { setGraphic(null); setText(null); } } }; } } And now it will look like:\nOk, that\u0026rsquo;s more like a layout. Let\u0026rsquo;s look at what\u0026rsquo;s happening here\u0026hellip;\nWith an anonymous inner class you use the {} for the constructor, so you can see that we\u0026rsquo;re creating the layout in the constructor. The layout is just a StackPane, and we\u0026rsquo;ve got some padding and a BackgroundFill on it. Once it\u0026rsquo;s done, though, it\u0026rsquo;s done. We\u0026rsquo;re not going to mess with the layout outside that constructor.\nNow updateItem() does two things. First, it stuffs the Image into the ImageView. Then it sets Graphic to our layout, and Text to the name.\nConclusion # Hopefully, you now have a good understanding about how ListView works, and how the data from your underlying List gets into the ListCells. We\u0026rsquo;ve gone from the generic ListView showing a list of Strings in a scrolling pane, to handling the data transfer into each Cell and customizing how it\u0026rsquo;s displayed.\nThis is a big step forward, but we\u0026rsquo;re still treating Cell very much like a Label. In the next article we\u0026rsquo;ll look at how to treat ListView as a vehicle for displaying custom layouts.\n","date":"19 November 2023","externalUrl":null,"permalink":"/javafx/elements/nodes/tableview-listview/listview-basics/","section":"JavaFX","summary":"An introduction to ListView and understanding how to use it to display something more than just lists of Strings.","title":"Understanding ListView","type":"javafx"},{"content":" Introduction # Getting your Nodes on the screen in the places that you want them can be challenging at times. JavaFX has a fairly complete set of layout classes that will organize the contents in some particular fashion. Understanding the options available to you when you are designing a screen can make it much easier to get your screen to behave the way that you want it to.\nThis article isn\u0026rsquo;t going to go into deep detail about how to use any of these classes. It\u0026rsquo;s intended to give you a familiarity with how they work, and to outline some of the key parameters that you can use to change their behaviour.\nYou\u0026rsquo;ll find that there are links to the JavaDocs (version 16) of all of these classes. So if you want to know more about them, it\u0026rsquo;s just a click away.\nThe Layout Classes # There are a variety of layout classes, and they differ mostly in the way that they arrange the items in them in relationship to each other.\nHBox and VBox # These two classes are the basic building blocks of just about any layout. They work very much the same way except for their orientation. HBox places the items in it horizontally, with each new element added to the right of the ones already in the HBox. VBox places the items in it vertically, with each new element added below the ones already in the VBox.\nThat\u0026rsquo;s really all there is to it. Both of these classes will respect the boundaries of the elements inside them (meaning that they won\u0026rsquo;t overlap) unless you force them to do otherwise.\nThere are a number of parameters that you can alter that will impact the manner in which they work:\nPadding Padding is the space inside the container that surrounds the contents. Padding is set using a class called Insets. Spacing Spacing is the space that occurs between each of the elements in the container. Alignment Alignment is how the elements will be place with relation to the boundaries of the container. Both HBox and VBox have TOP_CENTER as the default alignment Margins Margins are spacing around the outside of an element in a container. Margins are imposed by the container on the contained elements. The setMargin() method is static in HBox and VBox FillHeight and FillWidth Properties These two properties control how resizable children will grow to fill the height or width of the container. Note that HBox only has FillHeight and VBox only has FillWidth. StackPane # StackPane is the third layout class that orients the contents based on how they are added to the container. In StackPane all of the elements are placed on top of each other (like they are coming out of the screen towards you) as they are added.\nStackPane doesn\u0026rsquo;t support Spacing since the items are intended to overlap. Additionally StackPane tries to resize its children to fill it completely, and the default alignment is CENTER. Otherwise it supports Margins, Alignment and Padding.\nStackPane can be particularly useful when you want to have the content of some section of your screen contain different Nodes dependant on the state of something in your application. Just put them all in a StackPane and only have the Visible property of one of them true at a time.\nBorderPane # BorderPane is perhaps the most useful of the layouts when designing a complete screen. It\u0026rsquo;s best understood via the diagram from the official JavaDocs:\nYou put things into a BorderPane by calling its methods, setCenter(), setTop() and so on. Note that you can only put one item into each area of a BorderPane, and if you want more than one thing in an area, you\u0026rsquo;ll have to put them into some other layout class.\nBorderPane is especially useful because it follows the same structure that many application screens take. There\u0026rsquo;s some kind of a header section at the top, then the main form or table in the middle, and then a status, message or button area at the bottom of the screen. These all map nicely to the structure of BorderPane.\nBecause each area of BorderPane can only hold one item, it doesn\u0026rsquo;t make any sense to have an overall alignment parameter for the BorderPane. Each of the areas has its own default alignment, and you can change the alignment of any specific contained element using a static method. Margins and Padding are supported by BorderPane.\nGridPane # GridPane is the most complicated of all of the layout classes in it\u0026rsquo;s use and structure. It allows you to carve the pane up into rows and columns and to place Nodes it particular columns and rows. There are constructs to allow you to control the nature of each row and each column, and to control the spacing between rows and columns. Additionally, there are ways to make a Node span several rows or columns.\nGridPane can become complicated to use because, while it has the concepts of row and columns this is only in regards to layout. There really is no construct to hold a row or column of Nodes. This means that, while you can set some parameters around a row, you cannot write a method to create a row of content Nodes and just append it to your GridPane. Each Node needs to be specifically put into a particular row/column position, and it\u0026rsquo;s not possible to have a true builder method for a row that doesn\u0026rsquo;t involve passing a reference to the GridPane as a parameter.\nAlso, if you find yourself putting a GridPane inside a ScrollPane, you\u0026rsquo;re likely to be getting into a situation where a TableView or ListView would be a better approach.\nOn the other hand, any Node that you put into a GridPane can itself be a layout container, which means that you can have fairly complicated constructs inside your GridPane.\nGridPane really shines when you do have Nodes that need to be arranged into a row and column arrangement where columns of Nodes need to be aligned with each other horizontally. If this isn\u0026rsquo;t a requirement, you might be better off by populating a VBox with a series of HBoxes. This can be much less complicated and easier to construct via builder methods.\nThe other concept you\u0026rsquo;ll need to understand about GridPane is the concept of RowConstraints and ColumnConstraints. These are classes that can be added to your GridPane to control the width/height, alignment and the grow priority of items in the rows and columns.\nFlowPane # FlowPane is layout container where the children are laid out in column or rows, with the items wrapping either horizontally or vertically. For instance, with a horizontal FlowPane, each child Node is added to the right of the previous one, until the wrapping width of the FlowPane has been reached, at which point it will start a new row, below the previous one.\nIf the width or the height (for a vertical FlowPane) of the FlowPane is changed, the Nodes move to fill it:\nYou can see how in this horizontal FlowPane there is clearly a concept of rows with a given height, but there is no concept of columns, as each row will simple have as many Nodes in it as will fit with a standard gap between them.\nTilePane # TilePane is very similar to FlowPane but it maintains a sense of both rows and columns regardless of its orientation:\nand it reorganizes itself as the width changes:\nBut you can see that it always keeps the Nodes organized in both rows and columns.\nAnchorPane # AnchorPane is absolutely the most abused layout container in JavaFX. Probably because it\u0026rsquo;s the first alphabetically, it becomes the \u0026ldquo;default\u0026rdquo; layout container programmers use in SceneBuilder. It\u0026rsquo;s almost a certainty, if you see code that uses AnchorPane instead of something more appropriate, it\u0026rsquo;s because someone used SceneBuilder, or they learned layouts by using SceneBuilder.\nWhat AnchorPane is intended to be used for is when you have several contained Nodes that need to be \u0026ldquo;anchored\u0026rdquo; to one or more sides of the Pane. This is generally not useful when you only have one item that needs to be anchored, as setting the alignment on something like an HBox will do the same thing with less code.\nThe Labeled Classes # The classes that inherit Labeled are all technically layout classes also. This includes Cell, Label and Button. These classes have two elements, a graphic and a text value. There are methods to control how these two elements are positioned inside the Node relative to each other, and then other methods that control the presentation of the text value.\nIf you have some layout that boils down to a Node plus a Label, you might be best off putting all of it together in a single Label node. For instance, a TextField with a prompt Label in front of it could be combined into a single Label instead of using an HBox to contain it all.\nThe Wrapper Classes # The next three classes aren\u0026rsquo;t really layout classes, but containers that you can put layout classes into to control their presentation.\nTabPane \u0026amp; Tab # A TabPane is a container that holds Tabs in a presentation that looks like a file cabinet with file folders with tabs that stick up. When you click on a Tab it brings it and its contents to the front of the TabPane, and you can only see the content of one Tab at a time.\nTabs have two regions, the tab itself - which operates a bit like Labeled, and the Content. Like BorderPane you can only put one item into the content of a Tab, which means that you\u0026rsquo;re probably going to be putting some other kind of layout container in there.\nTitledPane # TitledPane works very much the same way that \u0026ldquo;twisty\u0026rsquo;s\u0026rdquo; on web pages work. There is a title section with an arrow, and clicking on this will rotate the arrow and open and close the contents of the panel. Clicking on it again will do the reverse:\nAs with Tabs, you can only put one Node into the contents of a TitledPane, so you\u0026rsquo;ll need to use some other layout class to organize multiple Nodes in a TitledPane.\nScrollPane # ScrollPane is usually used as a wrapper around another layout class to allow it to fit into a area in the screen that would otherwise be too small for it. If the enclosed region is larger than the viewport of the ScrollPane then scroll bars will appear.\nThis image shows a TilePane inside a ScrollPane. It also illustrates the very important point that putting a container inside a ScrollPane may often fundamentally change the way that the enclosed container functions. A ScrollPane functionally has an infinite height and width, and won\u0026rsquo;t cause any resizable elements inside it to automatically expand. In the image above, you can see that the width of the ScrollPane has been increased but the TilePane inside it has not grown wider.\nHere\u0026rsquo;s the same TilePane, but this time the FitToWidth property of the ScrollPane has been set to true. This causes the TilePane to expand to fit the width of the ScrollPane's viewport:\nThe Non-Layout Containers # This next set of container classes don\u0026rsquo;t do any layout for you at all and are, therefore, of limited use.\nPane # Pane is the parent class to most of the layout classes. It extends from Region and only adds the public getChildren() method to Region. Anything that you put into a Pane will be stacked in the top left corner of the Pane. Pane won\u0026rsquo;t auto-resize to accommodate the things you put in it or do anything else particularly useful.\nIt\u0026rsquo;s parent, Region, has all of the methods that control the size of the layouts as well as a number of layout control methods. However the getChildren() in Region is inherited from Parent and is Protected, so you cannot directly put anything into either Region or Parent.\nTo position items in Pane you\u0026rsquo;re going to have to use setLayoutX/Y() or setTranslateX/Y() unless you\u0026rsquo;re happy with them all stacked on top of each other in the top left corner. This can be useful, however, if there\u0026rsquo;s something about the topology of your layout that doesn\u0026rsquo;t work well with the standard layout classes. For instance, the hexmap layouts that I\u0026rsquo;ve talked about in other articles are best done with Pane and manual placement of the hexes.\nGroup # Group is very much like Pane except that it gains its size purely from its contents. So it can\u0026rsquo;t be resized externally by an enclosing container class.\nGroup is supposed to be extremely light-weight and doesn\u0026rsquo;t do CSS styling. So in some extreme cases it might have some value if you were having performance issues. It\u0026rsquo;s also useful as a holder for Canvas so that you can put nothing more than a Canvas into a Scene. Otherwise, just don\u0026rsquo;t use Group.\nCanvas # Canvas isn\u0026rsquo;t really a container class, and it doesn\u0026rsquo;t extend from Parent, but from Node. It\u0026rsquo;s really a wrapper around a GraphicsContext that you can use to draw on.\nSince Canvas doesn\u0026rsquo;t extend from Parent, you can\u0026rsquo;t add it directly to a Scene. If all you want in your Scene is a Canvas, then the best approach is to stuff it into a Group that you use as the root of the Scene.\nTips and Techniques # Getting a layout to work the way that you want or expect can, at times, be a bit frustrating. Small changes to a design cause a ripple effect through your layout and stuff moves around in unexpected ways. Here are some tips for creating you layouts with the least amount of frustration\u0026hellip;\nUnderstand How Items Grow Inside Containers # Many of the Node classes are resizable, but for many of those there are practical constraints which means that they are essentially fixed sizes. For instance, a CheckBox isn\u0026rsquo;t going to ever need to be much bigger than the checkbox itself and the label.\nVery often, the Nodes that you\u0026rsquo;ll need to worry about resizing are the container classes. Most of the time, these way that these Nodes resize is going to be dependant on the class of the parent holding them. Sometimes, you can control how these Nodes grow based on their own methods, other times you need to use methods in the containing class.\nIn order to understand this\u0026hellip;\nUse Borders to Debug # The absolute best way to understand what\u0026rsquo;s using up space in your layouts is to use borders on your layout containers. Borders let you understand how things have grown to fill up containers and to understand why a particular Node isn\u0026rsquo;t appearing where you expect it to. Virtually all of the time, when you cannot understand why your layout is looking goofy, it\u0026rsquo;s because some container isn\u0026rsquo;t occupying the space that you think it should.\nThe easiest way to do this is to just create a few style class selectors in your CSS file, define a border of a particular colour in them, and then add them to the StyleClass list in a container Node. The CSS would look like this:\n.test-border1 { -fx-border-color: red; } .test-border2 { -fx-border-color: blue; } .test-border3 { -fx-border-color: green; } .test-border4 { -fx-border-color: black; } The examples in this article were created using these. I also added background colours to make things a bit clearer.\nDon\u0026rsquo;t Put Stuff Inside of Stuff Unless You Have a Reason # This is something that beginners do all the time. I\u0026rsquo;ve seen countless layouts where GridPane is inside an AnchorPane which is inside an HBox which is inside another AnchorPane and so on. There\u0026rsquo;s no point to it, just having the GridPane by itself would do the trick.\nGenerally speaking, having a layout container with only one child Node in it is probably a JavaFX code smell. This is especially true if that container doesn\u0026rsquo;t have any configuration done to it at all (like setting Padding or Alignment).\nAs a rule, putting your layout inside another layout container won\u0026rsquo;t fix your problem but it will make your layout more complex and harder to debug.\nDon\u0026rsquo;t Worry About \u0026ldquo;Load\u0026rdquo; of Layout Types # Unless you\u0026rsquo;re doing something really graphically complex that might tax your system, just don\u0026rsquo;t worry about whether one layout class is \u0026ldquo;heavier\u0026rdquo; than another. It just won\u0026rsquo;t matter in real life.\nLooking at the source code for JavaFX, you\u0026rsquo;ll find that many of the more complicated controls, like ComboBox or Slider are actually composed of a number of different Nodes. Generally, if one of those internal Nodes needs to be placed into a layout container (or sometimes just is the layout container), JavaFX uses StackPane. StackPane doesn\u0026rsquo;t appear to be any lighter than any other container. If this approach is good enough for the inner workings of the JavaFX classes, it\u0026rsquo;s probably good enough for you.\nBuild Size Constraints from the Inside Out # It\u0026rsquo;s usually true that you\u0026rsquo;ll have better results if you concentrate on limiting the size of your Nodes at the bottom of your hierarchy, not at the top. Leave the layout containers free to resize to the contents as much as possible.\nAt the bottom is where you\u0026rsquo;re likeliest to find Nodes that can be absolutely sized. For instance, you might be able to say that a particular TextField should be restricted to only 60px. It would probably look better than having it 5 times as big as the longest String that\u0026rsquo;s allowed in it.\nDon\u0026rsquo;t Try to Scale to Changing Fonts # One of the most difficult things to do in JavaFX is to try to set limits on a Node based on how much space its text-based contents might take up. Especially when you try to make the calculation independent of the font used for the text.\nThe best approach is probably to just pick a reasonably font and size that works well in your screen and set your height and width values for your controls based on that font. If you need bigger fonts for special situations, use setScaleX/Y() on the entire Node.\nStatic Methods # There are a number of static methods in the layout classes that can be used alter the layout of contained Nodes. Methods like setMargins(). These methods should be applied from outside the builders for the contained Nodes.\nLet\u0026rsquo;s say that you have a Label that you intend to put into an HBox and you want to have a little extra space in between this Label and the next thing to its right. Generally speaking, this is a good use for HBox.setMargin(). If you have a builder for this Label don\u0026rsquo;t include the call to HBox.setMargin() inside of it. This makes your builder assume that the Label is going to be in an HBox, and if you later change that, then you\u0026rsquo;ll have to change the builder too.\nAny code that arranges Nodes in container with regards to each other belongs in the code for the container.\nDifferent Approaches Can Work Fine # There\u0026rsquo;s often not just one way that you can design a layout. In many cases, there\u0026rsquo;s no objectively better way to construct a layout, it just boils down to what makes sense to the programmer.\nLet\u0026rsquo;s look at a simple example. We\u0026rsquo;ll start from the inside, with a Label/TextField combination in a VBox, so the prompt is above the TextField. We\u0026rsquo;ll create a builder for it, parameterize the prompt text and return the result as a very generic Region. This is Kotlin, but it should be pretty clear what\u0026rsquo;s going on:\nprivate fun inputThingy(promptText: String): Region = VBox(4.0).apply { padding = Insets(4.0) isFillWidth = true maxWidth = 230.0 children += Label(\u0026#34;$promptText:\u0026#34;) children += TextField() } We\u0026rsquo;re controlling the width of the TextField by setting setFillWidth(true) and then setting a max width for the VBox. In real life, with a Model, we\u0026rsquo;d also parameterize the StringProperty from the model that we\u0026rsquo;d bind to the TextField. But this is just about layouts, so we don\u0026rsquo;t need to go that far for this example.\nNow we\u0026rsquo;ll look at how you can get a bunch of these inputThingy's onto the screen\u0026hellip;\nWe can lay them out in a set of HBoxes contained in a VBox:\nreturn VBox(2.0).apply { children += listOf(HBox(2.0, inputThingy(\u0026#34;First Name\u0026#34;), inputThingy(\u0026#34;Last Name\u0026#34;)), HBox(2.0, inputThingy(\u0026#34;Street\u0026#34;), inputThingy(\u0026#34;City\u0026#34;)), HBox(2.0, inputThingy(\u0026#34;Province/State\u0026#34;), inputThingy(\u0026#34;Country\u0026#34;)), HBox(2.0, inputThingy(\u0026#34;Phone Number\u0026#34;), inputThingy(\u0026#34;Email Address\u0026#34;))) } or a GridPane:\nreturn GridPane().apply { add(inputThingy(\u0026#34;First Name\u0026#34;), 0, 0) add(inputThingy(\u0026#34;Last Name\u0026#34;), 1, 0) add(inputThingy(\u0026#34;Street\u0026#34;), 0, 1) add(inputThingy(\u0026#34;City\u0026#34;), 1, 1) add(inputThingy(\u0026#34;Province/State\u0026#34;), 0, 2) add(inputThingy(\u0026#34;Country\u0026#34;), 1, 2) add(inputThingy(\u0026#34;Phone Number\u0026#34;), 0, 3) add(inputThingy(\u0026#34;Email Address\u0026#34;), 1, 3) vgap = 2.0 hgap = 2.0 } or in a TilePane:\nreturn TilePane().apply { children += listOf(inputThingy(\u0026#34;First Name\u0026#34;), inputThingy(\u0026#34;Last Name\u0026#34;), inputThingy(\u0026#34;Street\u0026#34;), inputThingy(\u0026#34;City\u0026#34;), inputThingy(\u0026#34;Province/State\u0026#34;), inputThingy(\u0026#34;Country\u0026#34;), inputThingy(\u0026#34;Phone Number\u0026#34;), inputThingy(\u0026#34;Email Address\u0026#34;)) prefColumns = 2 hgap = 2.0 vgap = 2.0 } And they all look the same:\nThe only real difference is if you stretch out the window, the TilePane adjusts to the new space:\nMaybe the TilePane is better because it\u0026rsquo;s more responsive, but who knows? Maybe there is a constraint to have just two columns of inputs. This example is a little bit contrived, because all of the Nodes in the layout are designed to be the same width, which is a bit simpler. But still, this kind of situation comes up all the time.\nProbably more important than the layout class used, is the fact the that Label/TextField pair are set up in their own layout, and then that composite Node is put into the main layout. Many, many programmers would try to put the Labels and the TextFields separately into the main layout, and then it gets overly complicated very quickly.\n","date":"6 January 2023","externalUrl":null,"permalink":"/javafx/elements/layout_classes/","section":"JavaFX","summary":"In order to put anything onto the GUI you’ll need to use one of the layout Nodes to control how the screen is organized. There are a variety of different layout classes, each of which handles the organization of its content differently.\nThis article gives you a summary of how each different layout class works, and explains how each one differs from the others.\n","title":"Layout Classes 101","type":"javafx"},{"content":" Introduction # Most programmers start out with JavaFX by manually applying styling to their Nodes using the Node.setStyle() method. It works, but it\u0026rsquo;s really cumbersome and complicated to read - especially when a Node has extensive styling applied. We\u0026rsquo;re going to look at Cascading Style Sheets and how they have been implemented in JavaFX.\nIf you think about it, you\u0026rsquo;ll realize that, in general, there three elements to the UI of an application:\nThe Layout This is how the various Nodes will be positioned on the screen. How they are aligned, which Nodes contain other Nodes, padding and margins. Things like that. Configuration This defines how the Nodes will behave. It includes things like actions on Button clicks, responses to having focus, whether or not a Node is visible and how this is controlled. It also defines how various Nodes on the screen are related to each other functionally. Also, this includes how the properties of the Nodes are bound to the Model. Styling This is virtually all of the other elements of a Node\u0026rsquo;s appearance that aren\u0026rsquo;t layout. Font sizes, colours, borders and backgrounds, opacity, all that kind of stuff. You\u0026rsquo;ll want to keep these three things separated as much as possible. Having everything jumbled together makes it much, much more difficult to understand how a layout is designed, and makes it difficult to identify where you need to look to understand configuration and styling.\nThis article is about styling, and how you can keep it isolated from the rest of your application by using Cascading Style Sheets. You\u0026rsquo;ll see that it\u0026rsquo;s actually quite easy to do and much more convenient than using Node.setStyle().\nWhy Use stylesheets? # Stylesheets have a number of advantages:\nThey put all of your styling in one place. They \u0026ldquo;cascade\u0026rdquo;. It\u0026rsquo;s much easier to read and understand your styles. It\u0026rsquo;s possible to combine styles together making it easier to do more complicated things. They allow Pseudo Classes. They keep the styling out of your code. They allow \u0026ldquo;theming\u0026rdquo; to be easily implemented. It\u0026rsquo;s consistent with the way that the standard JavaFX Node classes are implemented. Getting Started # There\u0026rsquo;s three steps you need to follow to style your Nodes with a stylesheet:\nCreate a stylesheet. Load the stylesheet into your Scene. Apply the styles to your Nodes. Creating a StyleSheet # A stylesheet is just a file, usually with a \u0026ldquo;.css\u0026rdquo; extension. You need to put it into the resources section of your project so that you can find it with a Class.class.getResource() call. We\u0026rsquo;ll look at the actual structure a little later on.\nImplementing a Stylesheet # The main method for implementing a new stylesheet is the method getStyleSheets(), which returns an ObservableList\u0026lt;String\u0026gt;, where the Strings are URL\u0026rsquo;s to file locations of the stylesheets. This means that there can be many stylesheets implemented on a single entity. In this case, the stylesheets will be applied in order, with later stylesheets overriding earlier ones if they both define the same element. This is important.\nSince this returns a List, once you\u0026rsquo;ve gotten it, you can add your own stylesheets to it. You can also remove stylesheets if you want to. It\u0026rsquo;s just a List, but since it\u0026rsquo;s Observable any changes will instantly be reflected in your UI.\nWhere To Implement the Stylesheet # The method getStyleSheets() is defined for two classes: Scene and Parent. Parent is a direct descendent of Node, and the ancestor of virtually every other JavaFX Node that you\u0026rsquo;re likely to use except ImageView and Canvas. So this means that you can implement a stylesheet for virtually any Node that you might use. The only restriction is that it seems you can only add a stylesheet to a Node that is the \u0026ldquo;root\u0026rdquo; of a Scene.\nThis last restriction doesn\u0026rsquo;t seem to be documented anywhere, and the source code inside JavaFX that deals with this is super complicated. But it appears that the stylesheet is actually pushed up and applied to the Scene, and it does this when the Node is placed into the Scene. And it only looks at the Node directly placed into the Scene in a constructor or with Scene.setRoot().\nThis means that you can apply a stylesheet at either the Scene, or the layout class which is the \u0026ldquo;root\u0026rdquo; element of the Scene. Then every Node in the Scene will get that stylesheet added.\nPersonally, I prefer to apply the stylesheet at the \u0026ldquo;root\u0026rdquo; element of the Scene. This is because the styling seems to me to be part of the content, and it should be included in its definition. Also, if you use the same content in a different Scene, then you don\u0026rsquo;t have to re-specify the stylesheet, since it\u0026rsquo;s included in the content.\nOf course, you might have a case where you define the content independent of the styling, and wish to have the styling change dependent on the context in which it is being used. If that context is defined when the Scene is created, then it might be better to set the stylesheet at the Scene, not the \u0026ldquo;root\u0026rdquo; of the Scene.\nFor what it\u0026rsquo;s worth, almost every example that I\u0026rsquo;ve seen on the web applies the stylesheet to the Scene directly. My guess is that this is mostly just copypasta and the majority of people don\u0026rsquo;t even realize that it can be applied to the \u0026ldquo;root\u0026rdquo; element instead.\nApplying a Style # Stylesheets are divided up into two parts: A \u0026ldquo;root\u0026rdquo; section, and everything else.\nThe \u0026ldquo;root\u0026rdquo; section is for defining global variables, which you can then use in the rest of the stylesheet. Also, since the stylesheets cascade, if you redefine a variable which was defined in an earlier stylesheet, that new definition will be used in its place - even those uses in other stylesheets.\nIt\u0026rsquo;s perfectly reasonable to have a stylesheet that has nothing but a \u0026ldquo;root\u0026rdquo; section, which would have the impact of changing global settings defined and used in other stylesheets. Colours are one of the key global variables which are often used, so you can completely change the colour composition of a screen just by changing a handful of colour definitions.\nThe \u0026ldquo;.root\u0026rdquo; section can also allegedly be used to load fonts, but this seems to be a bit problematic (buggy?). We\u0026rsquo;re not going to look at fonts in this article, though.\nThe rest of the stylesheet is made up of styles to be used in specific cases. These specific cases are identified by something called \u0026ldquo;selectors\u0026rdquo;. There are three kinds of selectors in a JavaFX stylesheet, the \u0026ldquo;.\u0026rdquo; selector, the \u0026ldquo;#\u0026rdquo; selector, and the object type selector. Let\u0026rsquo;s look at them:\nThe Style Class Selector # Node has a method called getStyleClass(). This method returns an ObservableList\u0026lt;String\u0026gt; where the Strings will map to stylesheet selectors prefixed with a \u0026ldquo;.\u0026rdquo;. You can call this method and then add or remove elements to/from it and it will change the styles which a applied to the Node. Just as with stylesheets, all the Style Classes are processed in order, and any elements which are re-defined in a later Style Class will override definitions defined in earlier classes.\nThe ID Selector # Node also has a method called setId(), which takes a String. This ID is expected to be unique, but it\u0026rsquo;s not enforced. You can then create a selector in your stylesheet with that same ID, prefixed with a \u0026ldquo;#\u0026rdquo;.\nNotionally, this might be of some use if you have a single Node in a Scene that requires unique styling. But it\u0026rsquo;s not any easier than just creating a class selector that does the same thing and appending it to the list returned from getStyleClass().\nI have a feeling that the ID selector was introduced to facilitate styling from within an FXML file in some way. As such, it\u0026rsquo;s not really much use if you\u0026rsquo;re not using FXML. I\u0026rsquo;ve never used it myself.\nThe Type Selector # The Node class, from which virtually all JavaFX screen widgets descend, has a method called getTypeSelector(). Presumably, every Node subclass will return a different value for this method. You can use this return value as a selector in your stylesheet. From what I can see, it returns the name of the class, so Label will return \u0026ldquo;Label\u0026rdquo;, and so on.\nI\u0026rsquo;ve never seen this used anywhere, and I\u0026rsquo;ve never felt the need to use it myself. The default JavaFX stylesheets (Modena and its predecessor, Caspian) don\u0026rsquo;t use it. It\u0026rsquo;s probably best to ignore it.\nOverriding Styles # Most tutorials on the Web list three ways to change the style of a Node: The first two I\u0026rsquo;ve listed above (they never talk about getTypeSelector()), and re-using one of the predefined selectors, like \u0026ldquo;.label\u0026rdquo;.\nLet\u0026rsquo;s be clear though, this isn\u0026rsquo;t actually a fourth way of implementing a style. This is just a Style Class selector, but one where you don\u0026rsquo;t need to add the class to getStyleClass() because it\u0026rsquo;s already there for that class of Node. If you look at source code for any of the Nodes in JavaFX, you\u0026rsquo;ll find a call to getStyleClass() in every one. Sometimes there will be multiple calls if the Node has multiple parts - usually defined through a \u0026ldquo;skin\u0026rdquo;.\nWhat you are doing when you override a standard selector is that you are changing the styling of every Node that uses that selector. So if you create a selector called, \u0026ldquo;.label\u0026rdquo; in your stylesheet and you change the font size to \u0026ldquo;256px\u0026rdquo;, then every Label in your Scene is going to get really, really big. We\u0026rsquo;ll come back to this later.\nStyle Sheet Parameters # How do you know what to put in a Style Class definition?\nHappily, we have a reference for that here.\nWhat you see from that reference is that there is a list of parameters to which you can assign values. Different types of Nodes will have different parameters which will work on them. For instance, there\u0026rsquo;s no point changing the font weight of a Circle, because it doesn\u0026rsquo;t have any text to which that could possibly apply. These are all detailed in the reference.\nAlso, you\u0026rsquo;ll see what the valid values are for these parameters, and the different ways that you can specify values.\nIn the introduction to the reference it uses this example, and from it you can see how an entry is laid out:\n.my-rect { -fx-fill: yellow; -fx-stroke: green; -fx-stroke-width: 5; -fx-stroke-dash-array: 12 2 4 2; -fx-stroke-dash-offset: 6; -fx-stroke-line-cap: butt; } This is intended to be applied to a Rectangle Node, which is a descendent of Shape. First we have the selector, which is \u0026ldquo;.my-rect\u0026rdquo;, then the parameters are defined in a block delimited with \u0026ldquo;{}\u0026rdquo;. In this example, the Rectangle is filled with solid yellow, and the rest of the parameters define a green, dashed outline. You\u0026rsquo;ll notice that just about all of the parameters start with \u0026ldquo;-fx-\u0026rdquo;.\nYou should also notice that this is far easier to read than some String passed to Node.setStyle(), and that\u0026rsquo;s a major advantage to using stylesheets.\nAn Example # Let\u0026rsquo;s put all of this together see how it works. We\u0026rsquo;ll create a simple Scene, add a custom stylesheet to the \u0026ldquo;root\u0026rdquo; and then use some class selectors to change some styling:\npublic class StyleSheetDemo1 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 460, 300); stage.setScene(scene); stage.show(); } private Region createContent() { StackPane stackPane = new StackPane(createMainVBox()); stackPane.setPadding(new Insets(20)); stackPane.getStylesheets().add(StyleSheetDemo1.class.getResource(\u0026#34;stylesheet1.css\u0026#34;).toString()); return stackPane; } private Region createMainVBox() { Label headingLabel = new Label(\u0026#34;This is the heading\u0026#34;); headingLabel.getStyleClass().add(\u0026#34;label-heading\u0026#34;); Label promptLabel = new Label(\u0026#34;Name:\u0026#34;); promptLabel.getStyleClass().add(\u0026#34;label-prompt\u0026#34;); Label dataLabel = new Label(\u0026#34;Hermin Vermin\u0026#34;); dataLabel.getStyleClass().add(\u0026#34;label-data\u0026#34;); HBox nameBox = new HBox(6, promptLabel, dataLabel); nameBox.setAlignment(Pos.CENTER_LEFT); nameBox.getStyleClass().add(\u0026#34;border-box\u0026#34;); nameBox.getStyleClass().add(\u0026#34;name-box\u0026#34;); nameBox.setPadding(new Insets(60)); VBox vBox = new VBox(20, headingLabel, nameBox); vBox.getStyleClass().add(\u0026#34;border-box\u0026#34;); return vBox; } } Here\u0026rsquo;s what it looks like: I admit that this is a pretty ugly colour scheme, but it makes it clear how it\u0026rsquo;s working.\nHere\u0026rsquo;s the stylesheet:\n.root { -heading-colour: green; -prompt-colour: mediumblue; -data-colour: slategrey; -theme-colour: violet; -name-colour: lightseagreen; -background-colour: lightyellow; } .label-heading { -fx-font-size: 35px; -fx-text-fill: -heading-colour; -fx-border-color: red; -fx-border-width: 1px; -fx-padding: 5; } .label-prompt { -fx-font-size: 16px; -fx-text-fill: -prompt-colour; -fx-font-weight: bold; } .label-data { -fx-font-size: 14px; -fx-text-fill: -data-colour; } .border-box { -fx-border-color: -theme-colour; -fx-border-width: 3px; -fx-border-radius: 6px; -fx-background-insets: 2px; -fx-background-color: -background-colour; -fx-background-radius: 6px; -fx-padding: 20px; } .name-box { -fx-border-color: -name-colour; -fx-padding: 6px; } This is a pretty simple example, but there\u0026rsquo;s actually a lot going on in it:\nYou have complete stylesheet here, so you can see how the format goes together. It has a \u0026ldquo;.root\u0026rdquo; section and then a number of Style Class selectors defined.\nThe wrapper StackPane is there so that we can have some padding around the actual content, which makes the border clearer.\nThe stylesheet is applied at the wrapper StackPane level, so you can see how it is available to all of the Nodes contained within it.\nAll of the colours are defined in the .root section of the stylesheet. We\u0026rsquo;ll see how this makes it easier to change the theme in a little bit.\nNameBox has both the \u0026ldquo;border-box\u0026rdquo; and \u0026ldquo;name-box\u0026rdquo; selectors applied to it. \u0026ldquo;name-box\u0026rdquo; overrides both the padding and the border colour from \u0026ldquo;border-box\u0026rdquo;.\nThe names of the selectors make sense. The selectors intended to be used for Labels all start with \u0026ldquo;label-\u0026rdquo; and then have a suffix indicating the use of the Label. The \u0026ldquo;border-box\u0026rdquo; selector is generic and can be used for any container class Node, while \u0026ldquo;name-box\u0026rdquo; is very specific to a particular Node in the layout.\nYou can see that stylesheet trumps programmatic settings. NameBox attempts to set its padding to 60px programmatically, but clearly this doesn\u0026rsquo;t happen (you can see from the screen shot), as the stylesheet specifies 6px.\nChanging the Theme # Now let\u0026rsquo;s add a \u0026ldquo;night mode\u0026rdquo; to this screen. We\u0026rsquo;ll use the following stylesheet, called \u0026ldquo;darkmode.css\u0026rdquo;. You\u0026rsquo;ll notice that it is composed entirely of a \u0026ldquo;.root\u0026rdquo; section:\n.root { -heading-colour: darkcyan; -prompt-colour: dodgerblue; -data-colour: darksalmon; -theme-colour: darkviolet; -name-colour: #702510; -background-colour: #080838; } The only change to the code is that we\u0026rsquo;ll now add the new stylesheet to the StackPane. This stylesheet is added after the \u0026ldquo;stylesheet1.css\u0026rdquo; stylesheet, so anything defined in it will override values in \u0026ldquo;stylesheet1.css\u0026rdquo;.:\nprivate Region createContent() { StackPane stackPane = new StackPane(createMainVBox()); stackPane.setPadding(new Insets(20)); stackPane.getStylesheets().add(StyleSheetDemo2.class.getResource(\u0026#34;stylesheet1.css\u0026#34;).toString()); stackPane.getStylesheets().add(StyleSheetDemo2.class.getResource(\u0026#34;darkmode.css\u0026#34;).toString()); return stackPane; } And now it looks like this:\nWhich is still pretty hideous, but you can see how the colours have all changed because the new \u0026ldquo;.root\u0026rdquo; section has redefined the variables.\nUnderstanding and Using the Default StyleSheet: Modena # You need to understand that standard JavaFX Node classes all contain code that links them to a standard, built-in, stylesheet. Earlier versions of JavaFX used a stylesheet called \u0026ldquo;Caspian\u0026rdquo;, which was replaced years ago with the current one called \u0026ldquo;Modena\u0026rdquo;.\nModena is included in the JavaFX library jar. It\u0026rsquo;s located at: com/sun/javafx/scene/control/skin/modena. I\u0026rsquo;ve included a copy of it here. It\u0026rsquo;s entirely possible that Modena will be updated and changed between releases of JavaFX, so you should probably look at the version that\u0026rsquo;s included with the JavaFX jar that you\u0026rsquo;re using. But the version included here is probably close enough to get you started.\nThe linkage between standard JavaFX Nodes and their styling is created by setting Style Classes on the Nodes and their components. For the most part, these Style Classes are just the names of the Node classes themselves, but in lower case. So Button has the Style Class, \u0026ldquo;button\u0026rdquo; added to it, which means that there will be a selector \u0026ldquo;.button\u0026rdquo; in the Modena stylesheet.\nSome of the Node classes have multiple parts. For instance ScrollPane is way complicated. It has multiple backgrounds and vertical and horizontal ScrollBars. ScrollBars themselves have multiple parts too. They have the tracks that they run on and the things that move along them are called \u0026ldquo;thumbs\u0026rdquo;. All of these sub-elements have their own Style Classes which are nested under the main Style Class. We\u0026rsquo;ll look at this in another tutorial.\nIn addition, many of the Style Classes support a variety of Pseudo Classes. Pseudo Classes are a way of modifying the styling based on a boolean toggle, essentially on/off. For instance, Button supports the \u0026ldquo;disabled\u0026rdquo; Pseudo Class, the \u0026ldquo;default\u0026rdquo; Pseudo Class and quite a few others.\nI think it\u0026rsquo;s pretty safe to assume that the people who put together Modena know their way around a stylesheet. Unless you know better you can probably treat the things you see in Modena as \u0026ldquo;best practices\u0026rdquo; for stylesheets, and adopt them for your own use. In any event, time spent studying and understanding Modena is highly unlikely to be time wasted.\nButton in Modena # To understand how all this works a little better, we\u0026rsquo;ll look at Button and how it\u0026rsquo;s defined in Modena:\n.button, .toggle-button, .radio-button \u0026gt; .radio, .check-box \u0026gt; .box, .menu-button, .choice-box, .color-picker.split-button \u0026gt; .color-picker-label, .combo-box-base, .combo-box-base:editable \u0026gt; .arrow-button { -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color; -fx-background-insets: 0 0 -1 0, 0, 1, 2; -fx-background-radius: 3px, 3px, 2px, 1px; -fx-padding: 0.333333em 0.666667em 0.333333em 0.666667em; /* 4 8 4 8 */ -fx-text-fill: -fx-text-base-color; -fx-alignment: CENTER; -fx-content-display: LEFT; } Yuk! It turns out there\u0026rsquo;s a lot of Button-like things that share the same basic styling. ToggleButtons, MenuButtons, things like that. So, as you can see, you can specify multiple stylesheet selectors to share the same styling. For the rest of this article, I\u0026rsquo;ll strip out the selectors we\u0026rsquo;re not interested in to keep things simple.\nWhile we\u0026rsquo;re here, though. Take a look at \u0026ldquo;.radio-button \u0026gt; .radio\u0026rdquo;. What does this mean? This means that RadioButton has a sub-component which is given the Style Class \u0026ldquo;radio\u0026rdquo;. It only exists within context of the \u0026ldquo;radio-button\u0026rdquo; Style Class. In Modena, that \u0026ldquo;radio\u0026rdquo; subclass is styled exactly the same as a Button.\nThe first thing you should notice about the styling is that Button doesn\u0026rsquo;t have any borders, but it has 4 different backgrounds. Each of those backgrounds has different insets which means that the ones on top only partially cover up the ones below. Essentially, these create a multi-coloured border.\nAlmost all of the Style Class definition for Button deals with the border and the backgrounds. This makes sense as a Button is really just a Region with styling to make it look like\u0026hellip; a button.\nButton doesn\u0026rsquo;t have any sub-components, but it does have Pseudo Classes, let\u0026rsquo;s take a look at one of those:\n.button:disabled { -fx-opacity: 0.4; } That\u0026rsquo;s pretty simple. When a Button is disabled, it\u0026rsquo;s opacity is set to 40%.\nNow let\u0026rsquo;s look at \u0026ldquo;focused\u0026rdquo;\n.button:focused { -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color, -fx-faint-focus-color, -fx-body-color; -fx-background-insets: -0.2, 1, 2, -1.4, 2.6; -fx-background-radius: 3, 2, 1, 4, 1; } This changes all of the background colours that make up the \u0026ldquo;border\u0026rdquo;. The top background colour stays the same, and an extra background is added in.\nLet\u0026rsquo;s look at \u0026ldquo;armed\u0026rdquo;. That\u0026rsquo;s when you click on a Button but haven\u0026rsquo;t released it yet. It looks like it\u0026rsquo;s pressed in a little bit:\n.button:armed { -fx-color: -fx-pressed-base; } Now, what does this do? It redefines \u0026ldquo;-fx-color\u0026rdquo; within the \u0026ldquo;.button\u0026rdquo; selector, when the \u0026ldquo;armed\u0026rdquo; Pseudo Class is active.\nSure, but what does this do?\nTo understand this, we are going to need to understand where those colours come from. So let\u0026rsquo;s find them:\n-fx-base: #ececec; -fx-color: -fx-base; -fx-inner-border: linear-gradient(to bottom, ladder( -fx-color, derive(-fx-color,30%) 0%, derive(-fx-color,20%) 40%, derive(-fx-color,25%) 60%, derive(-fx-color,55%) 80%, derive(-fx-color,55%) 90%, derive(-fx-color,75%) 100% ), ladder( -fx-color, derive(-fx-color,20%) 0%, derive(-fx-color,10%) 20%, derive(-fx-color,5%) 40%, derive(-fx-color,-2%) 60%, derive(-fx-color,-5%) 100% )); -fx-body-color: linear-gradient(to bottom, ladder( -fx-color, derive(-fx-color,8%) 75%, derive(-fx-color,10%) 80% ), derive(-fx-color,-8%)); -fx-outer-border: derive(-fx-color,-23%); -fx-pressed-base: derive(-fx-base,-6%); Now you can see that \u0026ldquo;-fx-color\u0026rdquo; is used to derive \u0026ldquo;-fx-inner-border\u0026rdquo;, \u0026ldquo;-fx-outer-border\u0026rdquo; and \u0026ldquo;-fx-body-color\u0026rdquo;. All of which are used in the backgrounds for Button. You don\u0026rsquo;t need to understand the gradient and ladder stuff at this point, just know that when \u0026ldquo;-fx-color\u0026rdquo; changes, so will these other colours.\nYou can also see that \u0026ldquo;-fx-pressed-base\u0026rdquo; is the same colour as \u0026ldquo;-fx-color\u0026rdquo;, but 6% darker.\nThis means that \u0026ldquo;arming\u0026rdquo; a Button makes all of the backgrounds except the highlight on the left side go a little bit darker, making it feel like it\u0026rsquo;s been pressed down.\nThis is a pretty neat technique, in my opinion. When something is defined outside of \u0026ldquo;.root\u0026rdquo;, that definition holds only for the selector in which it is defined. So, in this case, \u0026ldquo;-fx-color\u0026rdquo;, which is used to derive a whole bunch of different colours throughout the stylesheet, is redefined just for the selector for the \u0026ldquo;armed\u0026rdquo; Pseudo Class for \u0026ldquo;.button\u0026rdquo;.\nModifying the Standard Styling # First off, this doesn\u0026rsquo;t mean that we\u0026rsquo;re going to make modifications to Modena. You don\u0026rsquo;t have to. Just add your own stylesheet to the layout, and any definitions in that stylesheet will override the ones that are also in Modena.\nNone of this need to be complicated. Here\u0026rsquo;s a few things you can do:\nChanging the Colour Scheme # Let\u0026rsquo;s look at the actual colours that are defined in Modena. There are only about 5 that are used widely, and one of those is just a transparent version of one of the others:\n-fx-base: #ececec; -fx-accent: #0096C9; -fx-default-button: #ABD8ED; -fx-focus-color: #039ED3; -fx-faint-focus-color: #039ED322; This is what they look like:\nVirtually all of the other colours that are used in the stylesheet are derived from these 5 colours. There are a bunch of specialty colours that are used for things like chart lines, but we don\u0026rsquo;t need to worry about those now.\nIn order to change them, we just need to create a new stylesheet, and put our new definitions into the \u0026ldquo;.root\u0026rdquo; section:\n.root { -fx-accent: #c93046; -fx-default-button: #fd7088; -fx-focus-color: #e3305d; -fx-faint-focus-color: #e3305d22; } This is what they look like:\nNow let\u0026rsquo;s put this to use. I\u0026rsquo;ve used Buttons because they show the colours well, and a TextField because you can see how it gets the \u0026ldquo;glow\u0026rdquo; colour around it when it has focus:\npublic class StyleSheetDemo3 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 460, 300); stage.setScene(scene); stage.show(); } private Region createContent() { StackPane stackPane = new StackPane(createMainVBox()); stackPane.setPadding(new Insets(20)); stackPane.getStylesheets().add(StyleSheetDemo3.class.getResource(\u0026#34;customcolours.css\u0026#34;).toString()); return stackPane; } private Region createMainVBox() { Label promptLabel = new Label(\u0026#34;Name:\u0026#34;); promptLabel.getStyleClass().add(\u0026#34;label-prompt\u0026#34;); TextField textField = new TextField(); HBox nameBox = new HBox(6, promptLabel, textField); nameBox.setAlignment(Pos.CENTER_LEFT); nameBox.setPadding(new Insets(60)); Button button1 = new Button(\u0026#34;Button 1\u0026#34;); Button button2 = new Button(\u0026#34;Button 2\u0026#34;); button2.setDefaultButton(true); HBox buttonBox = new HBox(8, button1, button2); VBox vBox = new VBox(20, nameBox, buttonBox); vBox.getStyleClass().add(\u0026#34;border-box\u0026#34;); return vBox; } }; This is what it looks like without the custom stylesheet added:\nAnd this is what it looks like with the custom stylesheet:\nGlobal Changes Vs Custom Style Classes # Earlier, I mentioned that a lot of web articles seem to treat overriding a Modena selector as a completely separate approach from creating a new Style Class. But it\u0026rsquo;s not really, because the base JavaFX Node classes just have a call to getStyleClass().add(\u0026quot;{Modena Selector}\u0026quot;) in them.\nA lot of examples on the web seem to just override the Modena selectors to change particular Nodes in their Scenes. This works okay if you only have one example of that Node class in your scene - like a Scene that has a single TableView in it. Generally speaking, however, this is probably not the best approach to customizing the styling.\nIt a good idea to write your programs to do exactly what you intend to do. So if you want to change a single instance of a Node class in your scene, then change just a single instance of that Node class - create a Style Class for it and add it to your Node. Globally changing the styling for every instance of that Node class and relying on the fact that you are only using that Node class a single time is sloppy programming.\nHow to properly use this technique\u0026hellip; # Personally, I think the default Modena styling for a disabled Button is horrible. It uses 40% opacity which makes the Button almost disappear. Something like 60% or 70% gets the point across, and looks much better, in my opinion. So I can see putting this in a custom stylesheet:\n.button:disabled { -fx-opacity: 0.65; } And that\u0026rsquo;s just fine because the intent is to change every Button that appears in the layout.\nLet\u0026rsquo;s look at an example where these techniques are mixed together, so that you can see how they work. Still with Buttons:\npublic class StyleSheetDemo4 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 460, 300); stage.setScene(scene); stage.show(); } private Region createContent() { StackPane stackPane = new StackPane(createMainVBox()); stackPane.setPadding(new Insets(20)); stackPane.getStylesheets().add(StyleSheetDemo4.class.getResource(\u0026#34;customcolours.css\u0026#34;).toString()); return stackPane; } private Region createMainVBox() { Label promptLabel = new Label(\u0026#34;Name:\u0026#34;); promptLabel.getStyleClass().add(\u0026#34;label-prompt\u0026#34;); TextField textField = new TextField(); HBox nameBox = new HBox(6, promptLabel, textField); nameBox.setAlignment(Pos.CENTER_LEFT); nameBox.setPadding(new Insets(60)); Button button1 = new Button(\u0026#34;Button 1\u0026#34;); Button button2 = new Button(\u0026#34;Button 2\u0026#34;); Button button3 = new Button(\u0026#34;Button 3\u0026#34;); Button button4 = new Button(\u0026#34;Button 4\u0026#34;); button2.getStyleClass().add(\u0026#34;modena-button\u0026#34;); button4.getStyleClass().add(\u0026#34;custom-button\u0026#34;); button2.setDisable(true); button3.setDisable(true); button4.setDisable(true); HBox buttonBox = new HBox(8, button1, button2, button3, button4); VBox vBox = new VBox(20, nameBox, buttonBox); vBox.getStyleClass().add(\u0026#34;border-box\u0026#34;); return vBox; } }; Here\u0026rsquo;s the stylesheet:\n.button:disabled { -fx-opacity: 0.65; } .custom-button:disabled { -fx-base: ecfed8; } .modena-button:disabled { -fx-opacity: 0.4; } And here\u0026rsquo;s what it looks like:\nLet\u0026rsquo;s look at what happening with this code and stylesheet:\nButton 1 is a normal, enabled Button. It\u0026rsquo;s there for comparison. Button 3 is a disabled Button with only the standard \u0026ldquo;.button\u0026rdquo; Style Class applied. However, since the \u0026ldquo;.button:disabled\u0026rdquo; selector has been overridden in the custom stylesheet, it has the opacity of 65%.\nButton 2 is a disabled Button with the \u0026ldquo;modena-button\u0026rdquo; Style Class added. The selector, \u0026ldquo;.modena-button:disabled\u0026rdquo; overrides the overridden default opacity to put it back to 40%, just like it is in Modena. Hence the name \u0026ldquo;modena-button\u0026rdquo;. This way you can see how the default Modena styling looks like, side by side with the new styling.\nButton 4 is a disabled Button with the \u0026ldquo;custom-button\u0026rdquo; Style Class added. The selector \u0026ldquo;.custom-button:disabled\u0026rdquo; overrides the value of \u0026ldquo;-fx-base\u0026rdquo; to an ugly yellow colour. This changes the colour, but leaves the opacity at 65%.\nEven in an example this simple, there are some interesting things to note:\nIf you add a Style Class to a Node that uses Pseudo Classes you automatically get to use those Pseudo Class selectors with that Style Class.\nYou can define the Pseudo Class selectors for a Style Class without having to define the base selector for that Style Class. In this case, we define the \u0026ldquo;disabled\u0026rdquo; Pseudo Class styles for both \u0026ldquo;custom-button\u0026rdquo; and \u0026ldquo;modena-button\u0026rdquo; without defining base styles for either Style Class.\nYou can stack styles and override any values that were previously defined in any earlier Style Class.\nConclusion # That\u0026rsquo;s it. There should be enough information here to get you started using CSS in your JavaFX projects.\nIf you want more information about how to use Pseudo Classes (it\u0026rsquo;s way easier than the official JavaDocs would have you think), you can check out this article here:\nRead About Pseudo-Classes Here{: .btn .btn\u0026ndash;info}\n","date":"12 May 2022","externalUrl":null,"permalink":"/javafx/elements/stylesheets/intro/","section":"JavaFX","summary":"This article contains all of the information you need to get started with cascading stylesheets in JavaFX.","title":"Getting Started With Styling in JavaFX Using CSS","type":"javafx"},{"content":"In this tutorial we\u0026rsquo;re going to look at one of the parts of JavaFX that causes the most headaches for beginners - background threads and the FXAT.\nIn my experience, every JavaFX programmer \u0026ldquo;knows\u0026rdquo; you cannot do long operations on the FXAT. Yet every newbie programmer breaks this rule at every opportunity. Over and over again.\nSometimes, it looks like you can get away with it. Maybe your development environment has a local development database with only a couple of programmers using it. The response time is a few milliseconds for just about any request. Your screens work fine, even when you load your data on the FXAT.\nThen you move it into production. Now the database is running somewhere in the cloud with thousands of active users. The network latency alone adds a guaranteed 50ms overhead to every request. Your application makes 100 back-to-back API calls to load your data. It took 600ms in development, now it\u0026rsquo;s 5500ms. That\u0026rsquo;s 5.5 seconds! Your GUI just hangs for 5.5 seconds. What went wrong? The phone starts to ring as the support tickets come in\u0026hellip;\nYou need to understand how to use the FXAT properly.\nWhat is the FXAT, Anyway? # All of the various elements that make up the JavaFX screens are not thread-safe. This means that they have no programming baked into them to handle concurrency issues that might arise from having separate threads accessing the same data elements at the same time, possibly changing values, while another thread is relying on the previous value. Undoubtedly, this has greatly simplified the design of all of these classes, and the authors of JavaFX have had to ensure that the screens don\u0026rsquo;t become slow and unresponsive when being serviced by only a single thread.\nThe FXAT is the \u0026ldquo;JavaFX Application Thread\u0026rdquo;, and it\u0026rsquo;s the mechanism by which JavaFX can be a single threaded library. It\u0026rsquo;s a single process which is started up automatically when you execute the launch() method in your Application class. From that point on, everything that has to do with the GUI needs to run on that single thread.\nFrom the JavaDocs for Application:\nQuote JavaFX creates an application thread for running the application start method, processing input events, and running animation timelines. Creation of JavaFX Scene and Stage objects as well as modification of scene graph operations to live objects (those objects already attached to a scene) must be done on the JavaFX application thread.\nHow the FXAT Works # Basically, it\u0026rsquo;s an infinite loop that checks a queue for jobs and executes them in order. Each \u0026ldquo;job\u0026rdquo; is a snippet of code (ie: a Runnable), that\u0026rsquo;s been triggered by some kind of Event or manually submitted to the queue.\nOne of the big things to remember about this system is that there\u0026rsquo;s no connection in time between when a job is submitted to the FXAT and when it will run. It might run very quickly, or the queue may already have hundreds of jobs in it. You don\u0026rsquo;t know. You cannot know.\nThe impact of this is that while you can count on code which is contained within a single job on the FXAT being executed sequentially, you cannot assume that two jobs submitted back-to-back will be executed back-to-back. So each job should be complete within itself.\nJobs submitted to the FXAT will be run in order, but there\u0026rsquo;s no guarantee that other jobs won\u0026rsquo;t be slipped in between two jobs. This means that the GUI environment could change, and could change a lot, between the time that a job is triggered and the time that it executes. This can become a critical design consideration.\nHow to Submit Jobs to the FXAT # There\u0026rsquo;s two ways to get your code into the FXAT queue:\nAttach an EventHandler to a Node or Object. Pretty much any place that you see a method in a JavaFX object called setOn...() you are going to be defining an EventHandler. And that EventHandler will be submitted to the FXAT queue each time that Event is triggered on that object. Call Platform.runLater() This is how you manually submit a job to the FXAT. It\u0026rsquo;s called \u0026ldquo;runLater\u0026rdquo; because that\u0026rsquo;s exactly what it does, it put\u0026rsquo;s your job onto the back end of the FXAT queue where it will be run when it hits the front of the queue. Things You Shouldn\u0026rsquo;t Do on the FXAT # There are two basic classifications of operations that should never be done on the FXAT.\nOperations that take more than a few milliseconds to complete. I\u0026rsquo;d set the limit at about 10 milliseconds, but that might be a bit conservative. There\u0026rsquo;s a lot that can be done in that much time, and in truth you probably wouldn\u0026rsquo;t even be looking to see how long something took unless you were experiencing issues with your GUI. Blocking (or Potentially Blocking) Operations.\nA blocking operation is anything that potentially executes a wait() operation or its equivalent. This includes virtually every file operation, every database operation, and any calls to API\u0026rsquo;s that might wait(). Blocking operations are particularly brutal on the FXAT because they can last for seconds at a time, during which the GUI is completely frozen. Also, blocking operations can be highly variable, and might be fast in your test environment but subject to delays at times in production. Important While the FXAT is running your code it\u0026rsquo;s not doing anything else.\nThis means that it\u0026rsquo;s not responding to mouse events, clicks, keystrokes, or anything else. New jobs submitted via Platform.runlater() or as EventHandlers are going to sit at the bottom of the queue until your code completes. Your GUI is effectively frozen until your code is finished.\nStarting Background Threads with Task # OK, so you\u0026rsquo;ve decided that some piece of code needs to be run off the FXAT. How do you do this?\nJavaFX has a tool designed just for this: Task.\nTask is an abstract generic class that has methods for communicating between a background thread and the FXAT and a single abstract method name call().\nIn the JavaDocs for Task there\u0026rsquo;s a fairly long description section. It\u0026rsquo;s accurate, but also pretty much rubbish unless you already understand how to use Task. There\u0026rsquo;s only a passing mention of the most useful use-case for Task, which is to run a job to completion, at which point some code is executed on the FXAT - possibly using the results from call().\nGenerally speaking, you\u0026rsquo;ll use Task as an anonymous inner class, so you\u0026rsquo;ll have access to all of the data available in the scope in which the Task is defined.\nWarning You need to be careful if your Task accesses data updated by the FXAT as it may have changed between the time that the Task was invoked and the time that the code in call() runs.\nThis tutorial is really about the FXAT and how to deal with it. So we\u0026rsquo;re not going to go into all of the cool features of Task (or Service) and updating progress and all that. We\u0026rsquo;re going to concentrate on the mechanics of getting off the FXAT and then back onto it through Task.\nA Simple Example # The most straight-forward use for Task is to run some process that produces data as a result, and then use that data to update your GUI.\nThe Simulated API # The JavaDocs for Task have a lot of examples with loops and sleep(), but in order to keep it clear how this fits with something more real-life, with the blocking code at arms-length from layout code, I\u0026rsquo;ve created a simulated API class. This allows us to call it and just assume that it\u0026rsquo;s doing something external which is going to take some time to complete or may possibly block.\npublic class FxatApi { public String remoteCall(String theAnswer) { try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return theAnswer; } } It\u0026rsquo;s very simple, and it just echoes back whatever is passed to it after a 3 second delay. We\u0026rsquo;re going to use this same simulated API for all of the examples.\nThe Layout # Here\u0026rsquo;s the actual screen. It\u0026rsquo;s just a Label and a Button in a VBox. The application has a Presentation Model, and that\u0026rsquo;s a single StringProperty. This is just enough skeleton to demonstrate how Task works.\npublic class FxatDemo1 extends Application { private final StringProperty stringProperty = new SimpleStringProperty(\u0026#34;Starting Value\u0026#34;); public static void main(String[] args) { launch(); } @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { Label label = new Label(); label.textProperty().bind(stringProperty); Button button = new Button(\u0026#34;Click Me\u0026#34;); button.setOnAction(evt -\u0026gt; callApi()); VBox results = new VBox(20, label, button); results.setPadding(new Insets(20)); return results; } } The Task # This method is actually part the FxatDemo1 class, but I\u0026rsquo;ve split it out here so that it\u0026rsquo;s clear how it\u0026rsquo;s operating:\nprivate void callApi() { Task\u0026lt;String\u0026gt; apiTask = new Task\u0026lt;String\u0026gt;() { @Override protected String call() throws Exception { return new FxatApi().remoteCall(\u0026#34;The Answer\u0026#34;); } }; apiTask.setOnSucceeded(evt -\u0026gt; stringProperty.set(apiTask.getValue())); Thread apiThread = new Thread(apiTask); stringProperty.set(\u0026#34;Running API...\u0026#34;); apiThread.start(); } Here we create a Task which returns a String, and then set its call() method to instantiate our dummy API and call its remoteCall() method. The Task has an EventHandler added to it, triggered on its OnSucceeded event. That event handler grabs the value calculated by call() and sets it in the Presentation Model.\nNote Task\u0026rsquo;s call() method runs on the background thread and produces results that can be retrieved later via the Task\u0026rsquo;s getValue() method.\nFinally, a new Thread is created for the Task and it is started on it. Just before it is launched, the value in the StringProperty is changed to \u0026ldquo;Running API\u0026hellip;\u0026rdquo;. This way we can see when the Task starts, and when it ends.\nImportant All of the code in this method except for that single line inside of the Task\u0026rsquo;s call() method executes on the FXAT at time that the Button\u0026rsquo;s click action runs.\nUnderstanding what-code-runs-when-and-where is a key element in using the FXAT properly.\nThe Output # Initially, the screen looks like this:\nThen, as soon as the Button is clicked, it changes to this:\nAnd then it ends like this 3 seconds later:\nAnd that\u0026rsquo;s just about the most simple and common use for Task in JavaFX.\nonSucceeded, onFailed, and onCancelled # The example above uses setOnSucceeded() to fire an EventHandler that runs when the Task completes. This gets you back on the FXAT and runs when the value calculated by call() on the background thread is available.\nNow, if you look at the JavaDocs, you\u0026rsquo;ll see that there\u0026rsquo;s a couple other events that might be fired when call() ends. These are OnFailed, and OnCancelled. If your Task can end with OnSucceeded, can\u0026rsquo;t it end with these other events, too?\nThere was a time when I felt I was obliged, for the sake of tying up all the loose ends, to deal with these other two events. But I\u0026rsquo;ve come to realize that this isn\u0026rsquo;t necessary.\nFirst, from what I\u0026rsquo;ve been able to determine, OnFailed will only be triggered when the code in call() throws an uncaught exception. Generally speaking, an exception in a background task is either a nothing, or it\u0026rsquo;s a something that you really should deal with in some explicit way and may involve updating the GUI to let the user know what\u0026rsquo;s happened. If you do need to update the GUI after an exception, then OnFailed is probably the way to go, as it will execute on the FXAT and allows you to access the exception, too.\nCancelling a running Task is a pretty specific and potentially complicated operation. It can\u0026rsquo;t happen unless you deliberately program it in. And if you don\u0026rsquo;t do this, you don\u0026rsquo;t need to worry about OnCancelled.\nThe Juggler # A lot of programmers have a great deal of difficulty envisioning how the background tasks work and the impact to their application logic.\nThe One Thing That you Cannot Do\u0026hellip; # You can\u0026rsquo;t write anything that looks like a Function. What I mean by this is some construct that accepts input data and then returns a value derived from it. Once you spawn a background thread, this linear approach is off the table. There\u0026rsquo;s no way that you can implement it without executing wait() on the FXAT somehow, and that\u0026rsquo;s an absolute \u0026ldquo;no-no\u0026rdquo;.\nWarning All new JavaFX programmers try to do this.\nIt can\u0026rsquo;t be done but, go on\u0026hellip;try now.\nContinue reading when you give up.\nNow, About Juggling # The analogy that helped me cope with this was to think about it like a juggler. So, picture a juggler\u0026hellip;\nAt any given time, he\u0026rsquo;s got some number of balls in the air, and one of them is on it\u0026rsquo;s way down and needs to be caught and tossed back into air very soon. He might have special rules about how to handle the balls. Like, say\u0026hellip; \u0026ldquo;the blue ball needs to be bounced off his knee every time\u0026rdquo;, and, \u0026ldquo;the green ball needs to be thrown twice as high as the other balls\u0026rdquo;.\nWhatever the instructions, he can\u0026rsquo;t take very long with any particular ball or he\u0026rsquo;s going to miss handling the next ball coming down, and the whole performance will come to an end. An instruction like, \u0026ldquo;grab a pen and write your name on the ball before you toss it\u0026rdquo;, is out of the question.\nObviously, the juggler is the FXAT and the balls are the Runnables that are being dumped into the queue all of the time. It\u0026rsquo;s not a perfect analogy, but it\u0026rsquo;s close enough to give some pretty solid guidelines about how to program around the FXAT.\nNow, imagine that the juggler has an assistant or another juggler with him.\nFrom time to he\u0026rsquo;ll toss one of his balls off to that assistant, and then some time later the assistant will toss a ball back to him. When that happens, he has to be able to handle it and integrate it into the balls that he\u0026rsquo;s currently juggling without dropping any of them.\nBut here\u0026rsquo;s the important part: He doesn\u0026rsquo;t know when, or even if, the assistant is going to toss a ball back. So he cannot wait for it. He has to keep juggling his other balls, and when the new ball comes to him, whenever that happens, he has to deal with it.\nThis is How You Need to Think About Background Tasks # Running a background task is just like tossing a ball off to an assistant. You package up your code to run and hand it off to the background task. At the same time, you need to write the code that will be executed to handle the event that\u0026rsquo;s triggered when the background task completes (or fails).\nYou can\u0026rsquo;t wait for it. You cannot make any assumptions about when it\u0026rsquo;s going to happen. You cannot make any assumptions about the state of your GUI when it happens. You cannot make any assumptions about which background task will complete first.\nThis is what you are doing when you write an EventHandler for OnSucceeded. You\u0026rsquo;re writing the instructions for how to handle whatever information has been updated by your Task. You don\u0026rsquo;t know when, or even if, that will happen. But this is what you want to have happen on the FXAT when it does.\nAnother Approach to Task # Personally, I prefer to have all of the business logic off in its own class, something I have been calling an Interactor. With this structure the Interactor has access to the Model, and can update it. But, since it still needs the thread handling of Task there are two public methods, one to call the API and another to update the Model:\npublic class FxatInteractor { private final StringProperty model; private String domainData; public FxatInteractor(StringProperty model) { this.model = model; } public void fetchApiData() { domainData = new FxatApi().remoteCall(\u0026#34;The Answer\u0026#34;); } public void loadModel() { model.set(domainData); } } The method fetchApiData() is intended to be called on a background thread, and it simply calls the API and stores the result in the field, domainData. The method loadModel() is called on the FXAT and updates the Model with the data previously fetched from the API on the background thread.\nNote that the Interactor is aware of the FXAT, but only at arms length. The code is divided up into things that shouldn\u0026rsquo;t happen on the FXAT and things that should happen on the FXAT. But there\u0026rsquo;s no code that actually deals with the FXAT.\nHere\u0026rsquo;s callApi() from the main class:\nprivate void callApi() { Task\u0026lt;Void\u0026gt; apiTask = new Task\u0026lt;\u0026gt;() { @Override protected Void call() { interactor.fetchApiData(); return null; } }; apiTask.setOnSucceeded(evt -\u0026gt; interactor.loadModel()); Thread apiThread = new Thread(apiTask); stringProperty.set(\u0026#34;Running API...\u0026#34;); apiThread.start(); } Additionally, interactor is instantiated as a field in the main class, and it\u0026rsquo;s given a reference to the Model:\nprivate final FxatInteractor interactor = new FxatInteractor(stringProperty); The Task now has a type of Void, as the method call() no longer needs to return anything. The Interactor will update the Model directly.\nDealing With Concurrency Issues # As soon as you start launching threads you absolutely have to start worrying about concurrency issues. There\u0026rsquo;s no way around this. For this example, there are two basic ways to cope with concurrency\u0026hellip;\nMethod 1: Prevent Multiple Background Threads # The domain data is at risk here because it could be updated by multiple threads simultaneously. One way to guard against this it to structure the application in such a way that only one background thread can be run at a time. Since the background thread in this example is only launched when the Button is clicked, we can ensure that Button cannot be clicked again until the background thread has completed:\nprivate Region createContent() { Label label = new Label(); label.textProperty().bind(stringProperty); Button button = new Button(\u0026#34;Click Me\u0026#34;); button.setOnAction(evt -\u0026gt; { button.setDisable(true); callApi(() -\u0026gt; button.setDisable(false)); }); VBox results = new VBox(20, label, button); results.setPadding(new Insets(20)); return results; } We disable the Button as soon as it has been clicked, then create a Runnable that will re-enable it. We pass that Runnable to the callApi() method.\nprivate void callApi(Runnable postFetchAction) { Task\u0026lt;Void\u0026gt; apiTask = new Task\u0026lt;Void\u0026gt;() { @Override protected Void call() { interactor.fetchApiData(); return null; } }; apiTask.setOnSucceeded(evt -\u0026gt; { interactor.loadModel(); postFetchAction.run(); }); Thread apiThread = new Thread(apiTask); stringProperty.set(\u0026#34;Running API...\u0026#34;); apiThread.start(); } The only change here is that we now take a Runnable as a parameter, and call its run() method in the OnSucceeded event handler. This will do the trick and it\u0026rsquo;s really not a bad approach. In most cases, you probably won\u0026rsquo;t want to keep open the possibility that the user will re-run whatever process is kicked off by the the Button until it\u0026rsquo;s finished anyways.\nPerhaps even more important, though, is how this example demonstrates a clean way to handle this very, very common pattern around Button handling - especially if the Button is doing a \u0026ldquo;Load\u0026rdquo; or a \u0026ldquo;Save\u0026rdquo; type function. Note how the actions which are internal to the View are all contained inside the View, and the callApi() method has no knowledge of the inner structure of the View.\nMethod 2: Isolate the Data for Each Thread # private void callApi() { Task\u0026lt;FxatInteractor\u0026gt; apiTask = new Task\u0026lt;\u0026gt;() { @Override protected FxatInteractor call() { final FxatInteractor interactor = new FxatInteractor(stringProperty); interactor.fetchApiData(); return interactor; } }; apiTask.setOnSucceeded(evt -\u0026gt; apiTask.getValue().loadModel()); Thread apiThread = new Thread(apiTask); stringProperty.set(\u0026#34;Running API...\u0026#34;); apiThread.start(); } Here the Interactor is treated as a single-purpose business logic class. It\u0026rsquo;s instantiated inside of the Task\u0026rsquo;s call() method and returned as the value so that setOnSuccedded() can retrieve it on the FXAT. This means that every time our Task is started, it will create a brand new Interactor and there\u0026rsquo;s no possible concurrency issues with the updating of domainData.\nThis method is really valuable when you have a background task that can be triggered frequently and concurrently. For instance, you may have a ListView and trigger your Task whenever a new row becomes selected. A user scrolling down through the ListView by holding down an arrow key could trigger many, many Tasks in a very short period of time. In a case like that, though, you probably want to look into using Service instead of Task since it uses a ThreadPool to limit the number of active background threads.\nAvoiding Platform.runLater() # If you look out there on the Internet you\u0026rsquo;ll find tons and tons of examples with Platform.runLater() scattered willy-nilly all over the code. Heck, even the JavaDocs for Task has them. Yuck!\nThere\u0026rsquo;s really nothing wrong with Platform.runLater() per se, and you\u0026rsquo;ll see it used in examples all over the web. It works, and it works well.\nMy observation, however, is that Platform.runLater() can be a bit of a code smell and often indicates some deeper issue with the design of a system. So I personally leave Platform.runLater() as the last alternative to get code back on the FXAT, and only use it when I can\u0026rsquo;t find a better way. I think the result is a more coherent, well thought out, JavaFX design.\nPlatform.runLater() is best used when you really need to return partial results. Not progress tracking, but actually returning results and incorporating them into the GUI as they are generated.\nIf you\u0026rsquo;re using Task, and you\u0026rsquo;re not returning partial results, there\u0026rsquo;s really no valid reason to use Platform.runLater(). If you are just manually launching a Thread to execute some Runnable, you probably should be using Task instead. There\u0026rsquo;s very little extra work involved in defining a Task, and the structure clearly declares what you\u0026rsquo;re doing for anybody who comes along looking at your code.\nAnother Use for Platform.runLater() # Sometimes you just need to make sure that some aspect of what you\u0026rsquo;re doing to your GUI needs to happen at the end of everything. It\u0026rsquo;s entirely possible that something in your code is going to trigger an Event, or the internal handling of some GUI element is going to have some side-effect you have to ensure is completed before some part of your code.\nMaybe there\u0026rsquo;s some background process that\u0026rsquo;s generating partial results, or which might complete while your code is running, and you want to make sure that some piece of your code runs after these results have been handled.\nIn these cases, you\u0026rsquo;re already writing code that\u0026rsquo;s running on the FXAT, and Platform.runLater() can be the answer. It\u0026rsquo;s right there in the name runLater(), \u0026ldquo;run later\u0026rdquo;!\nThere\u0026rsquo;s a reason it\u0026rsquo;s called this. Any Runnable that you submit to Platform.runLater() is added to the end of the FXAT job queue. Meaning that it will get run after any Events that your code has already triggered. It will run after any other jobs that have already been submitted via Platform.runLater(), perhaps from a background Task.\nIn practice, you\u0026rsquo;ll need this technique only rarely. Sometimes you\u0026rsquo;ll come across a case where sticking part of your code inside a Platform.runLater() seems to fix a problem. But it will often turn out not the best solution to the problem, and if you come back and look at the code later, after you\u0026rsquo;ve learned more about how JavaFX works, you might see what\u0026rsquo;s really going on.\n","date":"9 April 2022","externalUrl":null,"permalink":"/javafx/elements/fxat/introduction/","section":"JavaFX","summary":"“Hey, you jobs!  Get off my FXAT!”  Learn how to deal with the JavaFX Application Thread - How to get your background jobs onto their own threads the right way, and how process the results back on the FXAT.","title":"Everything You Really Need to Know About the FXAT","type":"javafx"},{"content":" Introduction # Image and ImageView are the two basic classes that you\u0026rsquo;ll need to master to be able put images into your layouts. ImageView is the layout class, while Image is the \u0026ldquo;data\u0026rdquo; class, in much the same way that String is the data class for Text.\nImage # Image is the \u0026ldquo;data\u0026rdquo; class for images. You can load BMP, GIF, JPEG or PNG formatted images into an Image object.\nHow Image Fits in the JavaFX Hierarchy # Image inherits directly from Object. It doesn\u0026rsquo;t have any JavaFX parents. However, it is still a JavaFX object and will require that the JavaFX engine is running. It\u0026rsquo;s also best to handle Images on the FXAT.\nHow To Use Image # Every constructor for Image requires that image data, or the location of image data is specified. There\u0026rsquo;s no way to create an empty Image or to load image data after an Image has been instantiated. In that respect, you may consider Image to be immutable.\nThere are two important points to remember about Image:\nA single Image can be placed into as many ImageView objects as you like. This is very different from ImageView which can only be placed once into a layout.\nLoading images is generally one of the slowest processes in rendering a screen.\nWhat this means is that you might need to implement a scheme to avoid having the cost of of Image loading affect your GUI performance. The simplest approach is to avoid loading the same Image resource over and over if it appears multiple times on a screen. It might be possible to cache Images if they are to be dynamically displayed on the layout over time. It also might be possible to treat a set of images as a Sprite, so the entire set is loaded into a single Image which is then reused.\nCreating an Image # There are two basic ways to load an image: from an InputStream, or from a URL. Each method also allows you to scale the image if you want.\nBackground Loading # If the image is being loaded from a URL, you have the option of loading it on a background thread. This is an important feature that doesn\u0026rsquo;t seem to be used as much as it should be. Since Images really should be handled on the FXAT, loading an Image from a remote location could cause blocking on the FXAT - which is a really, really bad thing.\nThere are also properties that allow you to track the progress of the loading of an image on a background thread.\nScaling the Image # It\u0026rsquo;s possible to scale an image when it is loaded in order to reduce its footprint in memory and to potentially avoid scaling calculations in the layout if the size needs to be modified in the ImageView.\nError Handling # It\u0026rsquo;s possible that your Image may fail to load. In this case there are methods that can tell you if the load failed, and any exception that was thrown by the loading process.\nWhat Can You Do With an Image? # Not much, except put it into a screen object that holds Image. This includes BackgroundImage, ImageCursor, ImagePattern and BorderImage. We\u0026rsquo;re going to stick to talking about just ImageView here.\nImageView # ImageView is a Node type that you can add to a layout. It will go anywhere that you can put Node.\nHow ImageView Fits in the JavaFX Hierarchy # ImageView inherits methods and properties from just one JavaFX class:\nNode Everything in JavaFX inherits from Node, which gives most of its methods for styling, event handling and responding to mouse actions as well as location and transformation. Instantiating ImageView # There are three ways to instantiate ImageView:\nCreate an empty ImageView.\nSpecify an image resource URL. In this case JavaFX will automatically create an Image for you and load it from the URL, but it won\u0026rsquo;t do background processing.\nPass the constructor an Image object.\nUnlike Image, ImageViews can have their contents changed, so you don\u0026rsquo;t need to consider them immutable in this respect.\nProperties of ImageView # FitHeight, FitWidth and PreserveRatio These properties control the scaling of the ImageView on the screen to fit within defined parameters. ViewPort This property defines a rectangle which allows only a portion of an image to be visible in the ImageView. Image This is the Image contained in the ImageView. Uses of ImageView # Of course, ImageView can be used on its own to put an image onto the layout somewhere. Something like this:\npublic class ImageViewDemo1 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 820, 640); stage.setScene(scene); stage.show(); } private Region createContent() { Image loadingGif = new Image(Objects.requireNonNull(ImageViewDemo1.class.getResource(\u0026#34;loading.gif\u0026#34;)).toString()); Image brokenImage = new Image(Objects.requireNonNull(ImageViewDemo1.class.getResource(\u0026#34;broken.png\u0026#34;)).toString()); ImageView imageView = new ImageView(loadingGif); imageView.setImage(loadingGif); Image image = new Image(\u0026#34;https://www.pragmaticcoding.ca/assets/images/794.png\u0026#34;, 700, 0, true, true, true); image.progressProperty().addListener(observable -\u0026gt; { System.out.println(\u0026#34;Progress \u0026#34; + image.getProgress()); if (image.getProgress() == 1.0) { if (!image.isError()) { imageView.setImage(image); } else { imageView.setImage(brokenImage); } } }); VBox vBox = new VBox(imageView); vBox.setPadding(new Insets(30)); return vBox; } } This program demonstrates how to use the background loading of Image with a large file. First, we create two Images from local resources; one for \u0026ldquo;In Progress\u0026rdquo;, which is an animated GIF and the other is to show a broken link. Then we create the ImageView and load it up with the \u0026ldquo;In Progress\u0026rdquo; GIF, so we\u0026rsquo;ll see that on the screen immediately.\nThen we create the Image that we really want to show on the screen. But it\u0026rsquo;s coming from a remote site, and it\u0026rsquo;s pretty big (about 5MB), so we\u0026rsquo;ll load it in the background. Since it\u0026rsquo;s a remote file, we really do need to worry about a problem with the file, so we are going to put some handling in there to detect a problem and put up the \u0026ldquo;Broken Link\u0026rdquo; image.\nIn order to know when the Image has finished loading, we need to place a listener on the Progress property of the Image. The Progress is a Double that runs from 0 to 1.0. Unfortunately, a call to image.isBackground() always returns true, even after the image has finished loading, so we cannot use it test for load completion.\nIn the listener, we check for the progress to reach \u0026ldquo;1.0\u0026rdquo;, then check to see if it\u0026rsquo;s encountered an error and then populate the ImageView with either the freshly loaded Image, or the \u0026ldquo;Broken\u0026rdquo; Image.\nThis is typical JavaFX. All the tools are provided for you to build a slick, professional UI but they\u0026rsquo;re all relatively low level. This can result in a lot of \u0026ldquo;boilerplate\u0026rdquo; code. If you find yourself repeating this pattern over and over, it might be worth creating a custom class that handles all of this in one place. Then you can reuse it over and over.\nWhat About the \u0026ldquo;Placeholder\u0026rdquo; Image # If you really search through the JavaDocs for Image with eagle-eyes, you\u0026rsquo;ll find the following intriguing piece of information in the sample code in the description:\n// load an image in background, displaying a placeholder while it\u0026#39;s loading // (assuming there\u0026#39;s an ImageView node somewhere displaying this image) // The image is located in default package of the classpath Image image1 = new Image(\u0026#34;/flower.png\u0026#34;, true); And then nothing more about \u0026ldquo;a placeholder\u0026rdquo; anywhere else in the JavaDocs.\nI\u0026rsquo;ve looked through the source code for JavaFX and I couldn\u0026rsquo;t find any reference to a placeholder anywhere. Of course the code for Image is super complicated, since it has all kinds of different formats and threading and graphical stuff going on, so I could have missed it. My guess is that this an \u0026ldquo;unimplemented feature\u0026rdquo; that never got off the ground. It\u0026rsquo;s a shame, because it would have been so much easier to just call a setPlaceholder() method than to go through the listener route.\nIn the meantime, if you just put an Image that\u0026rsquo;s still loading into an ImageView, you\u0026rsquo;ll just get a blank.\nAs Part of Labeled # Probably the most common use of ImageView is as the Graphic in any of the classes that inherit from Labeled. This includes, Label, Button, and the various Cell classes.\nIn this example, we\u0026rsquo;re going to put the ImageViews into Buttons:\npublic class ImageViewDemo2 extends Application { private final ImageView imageView = new ImageView(); public static void main(String[] args) { launch(); } @Override public void start(Stage stage) { Scene scene = new Scene(createContent(), 500, 320); stage.setScene(scene); stage.show(); } private Region createContent() { Image happyFace = new Image(Objects.requireNonNull(ImageViewDemo2.class.getResource(\u0026#34;smileyface.png\u0026#34;)).toString()); Image angryFace = new Image(Objects.requireNonNull(ImageViewDemo2.class.getResource(\u0026#34;sadface.png\u0026#34;)).toString()); HBox hBox = new HBox(createButton(\u0026#34;Loved it!\u0026#34;, happyFace), createButton(\u0026#34;Liked it!\u0026#34;, happyFace), createButton(\u0026#34;Hated it!\u0026#34;, angryFace)); hBox.setPadding(new Insets(30)); imageView.setPreserveRatio(true); imageView.setFitHeight(160); return new VBox(20, hBox, imageView); } private Button createButton(String text, Image image) { Button results = new Button(text, createButonImageView(image)); results.setOnAction(evt -\u0026gt; imageView.setImage(image)); return results; } private Node createButtonImageView(Image image) { ImageView results = new ImageView(image); results.setSmooth(true); results.setPreserveRatio(true); results.setFitHeight(64); return results; } } And it looks like this:\nA few things to note here:\nThe happyFace Image is used twice in the Buttons, and again in the ImageView below the Buttons. This doesn\u0026rsquo;t cause any problems. The Images are all loaded in the foreground. There would be no harm in loading them in the background, and probably should be. However, with just two reasonably small images to load, there\u0026rsquo;s probably almost no impact on the UI performance. You can freely swap out the Image in an ImageView at any time while the application is running and it works just fine. This suggests the best way to handle layouts with variable images in them. Images For the Examples # ","date":"11 March 2022","externalUrl":null,"permalink":"/javafx/elements/buttons/","section":"JavaFX","summary":"The basic component of image display in JavaFX is the ImageView. In this article, we’ll look at how to use ImageView to display images in your GUI and how to incorporate them your components.","title":"All About Image and ImageView","type":"javafx"},{"content":"JavaFX contains powerful tools to turn simple TextFields into specialized entry fields for any kind of data you can think of.\nIntroducing TextFormatter # If you are writing a JavaFX application that needs a user to type in information, then you\u0026rsquo;ll almost certainly need to use the TextField control. But what if you need to make sure that the user enters valid data? What if the data you need is not a string? How do you convert it to the type that you need?\nGenerally speaking, there are three approaches to handling verification of user data:\nCheck the data in all of the fields whenever the data is saved/acted on. Flag fields with invalid data and disable the save/action triggers. Prevent the user from entering invalid data. The first approach usually involves creating some kind of messaging system with a way to tell the user what\u0026rsquo;s wrong with their input after hitting the save button. It can be disruptive, and requires building a flow to handle the error messaging.\nThe second approach is most useful when there are interactions between the data entry elements and what is considered a valid entry can change based on other controls on the screen. For instance, if there are an upper and lower limit to a numeric value that can change based on user selections. In such a case, if the user changes one of those other controls, a value previously entered in a field and considered valid at the time may no longer be valid. Libraries such as ControlsFX have classes which can do this nicely.\nThe third case is what we\u0026rsquo;re going to talk about here: Setting up a TextField so that the user can only enter data which fits a particular structure when they are editing it. It also has the added benefit that it will handle conversions between types for you. TextFields are, by definition, controls to enter text, But by attaching a TextFormatter to your TextField it can automatically convert your text input into, for instance, an integer, allowing you to associate your TextField with integer data.\nInstalling a TextFormatter into a TextField is trivial, just call the TextField.setTextFormatter() method. The TextFormatter class itself, however, needs to be set up properly to handle the work. TextFormatter has two main parts; a filter which handles user inputs, and a converter which maintains synchronicity between the text in the TextField and a value property which is some other data type.\nA Simple Example - A Whole Number TextField # Let\u0026rsquo;s start by looking at a straight forward use of TextFormatter - creating a TextField that will only accept a string of numbers that will be converted into positive integer.\nIn order to test the code, we\u0026rsquo;ll need a basic screen with some controls:\npublic class Main extends Application { private ObjectProperty\u0026lt;Integer\u0026gt; valueProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(0); public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(new TestPane(), 300, 100); primaryStage.setScene(scene); primaryStage.show(); } public class TestPane extends BorderPane { public TestPane() { TextField textField = new TextField(); setCenter(new VBox(10, new HBox(6, new Text(\u0026#34;TextField 1\u0026#34;), textField), new HBox(6, new Text(\u0026#34;TextField 2\u0026#34;), new TextField()))); } } } This has two extra items in it beyond the TextField to test the TextFormatter: An integer property that we\u0026rsquo;ll use later to test the conversion (think of this as the \u0026ldquo;model\u0026rdquo; in an MVC setup), and a second TextField just so that we can see what happens when focus is gained or lost in the test TextField. When you run this, you\u0026rsquo;ll get a window with a couple of labels and TextFields, and the data entry in both TextFields is free form and will accept anything.\nAdding a Converter to the TextField # Now let\u0026rsquo;s add a converter to the TextField:\npublic class Main extends Application { private ObjectProperty\u0026lt;Integer\u0026gt; valueProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(0); public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(new TestPane(), 300, 100); valueProperty.addListener(((observable, oldValue, newValue) -\u0026gt; { System.out.println(\u0026#34;Value changed -\u0026gt; Old Value: \u0026#34; + oldValue + \u0026#34;, New Value: \u0026#34; + newValue); })); primaryStage.setScene(scene); primaryStage.show(); } public class TestPane extends BorderPane { public TestPane() { TextField textField = new TextField(); TextFormatter\u0026lt;Integer\u0026gt; textFormatter = new TextFormatter(new IntegerStringConverter()); textFormatter.valueProperty().bindBidirectional(valueProperty); textField.setTextFormatter(textFormatter); setCenter(new VBox(10, new HBox(6, new Text(\u0026#34;TextField 1\u0026#34;), textField), new HBox(6, new Text(\u0026#34;TextField 2\u0026#34;), new TextField()))); } } } Here we\u0026rsquo;ve added a TextFormatter with just a converter applied to it. This TextFormatter wraps an Integer property, so now we can bind that property to the already existing valueProperty field. From the perspective of externally accessing the value in the TextField, we aren\u0026rsquo;t really interested in the TextField's, textPropery any more, all the access should be done through the TextFormatter. So that it\u0026rsquo;s easier to see what\u0026rsquo;s going on with the converter, and when it fires, there\u0026rsquo;s a now listener on the valueProperty field which will display how it\u0026rsquo;s changing on the console.\nThe converter here is an IntegerStringConverter, which extends StringConverter\u0026lt;Integer\u0026gt; and is a standard part of JavaFX. StringConverter has just two methods, fromString() and toString().\nIf you run this code, you\u0026rsquo;ll see that it allows you to type in anything you like, but if you trigger the converter by hitting \u0026lt;Enter\u0026gt; or moving the focus away from the TextField, it will reset your value back to the last valid value if the conversion fails. So if you start with \u0026ldquo;0\u0026rdquo;, and then replace it with \u0026ldquo;abcd\u0026rdquo; and then hit \u0026lt;Tab\u0026gt;. It will put the value back to \u0026ldquo;0\u0026rdquo;.\nThe one thing IntegerStringConverter won\u0026rsquo;t do is prevent the user from entering negative numbers. For this, we\u0026rsquo;ll need to create a custom converter that only accepts positive integers and ensures that negative values cannot be entered:\npublic class PositiveIntegerStringConverter extends IntegerStringConverter { @Override public Integer fromString(String value) { int result = super.fromString(value); if (result \u0026lt; 0) { throw new RuntimeException(\u0026#34;Negative number\u0026#34;); } return result; } @Override public String toString(Integer value) { if (value \u0026lt; 0) { return \u0026#34;0\u0026#34;; } return super.toString(value); } } You can install this new converter when the TextFormatter is instantiated. Call the constructor like this: new TextFormatter(new PositiveIntegerStringConverter()).\nIf you look at the source code for IntegerStringConverter, you\u0026rsquo;ll see that the fromString() method doesn\u0026rsquo;t do anything special to check for non-numeric characters in the string value. It does some checking for empty or Null strings, and then just returns using, Integer.valueOf(value). As I\u0026rsquo;m sure you know, calling Integer.valueOf(value) on a string with non-numerics will throw an exception and IntegerStringConverter does nothing to prevent this from happening. The TextFormatter has a try/catch block that resets the string value if the new string fails to convert. To be consistent with this, the new converter explicitly throws a RuntimeException if the new, converted value is negative. This seems ugly, but since the fromString() method is only ever called in response to changes in the TextField to which it is attached, it\u0026rsquo;s probably okay.\nThe reverse direction is a little more problematic. The toString() method is only going to be called from external changes to the Value property of the TextFormatter. So throwing a RuntimeException is probably going to cause issues. Returning Null is also a problem, as it doesn\u0026rsquo;t trigger an update in the bound View Model property. Setting it to \u0026ldquo;0\u0026rdquo;, causes the change to the bound property when the field loses focus.\nControlling the Input with a Filter # At this point, the code just about meets the minimum criteria to work. It won\u0026rsquo;t let you put in invalid data, and it provides a value of the correct type for the View Model. From a user experience perspective, this is still less than optimal, as it silently changes the user\u0026rsquo;s input without any warning if they enter invalid data. That\u0026rsquo;s never a good thing.\nThe way to handle this is to add a filter to the TextFormatter. A filter is a class that accepts the users change to the string in the TextField, and ensures that it conforms to a set of rules. The \u0026ldquo;change\u0026rdquo; is sent through the filter in the form of an object of type TextFormatter.Change. The filter can reject the change, let it go through, or it can modify it in some way so that it changes the way that the TextField behaves.\npublic class PositiveIntegerFilter implements UnaryOperator\u0026lt;TextFormatter.Change\u0026gt; { @Override public TextFormatter.Change apply(TextFormatter.Change change) { if (change.getControlNewText().matches(\u0026#34;[0-9]*\u0026#34;)) { return change; } return null; } } A UnaryOperator is just a specialized case of the Function interface which returns data of the same type that it accepts. As the filter, it accepts TextFormatter.Change, and returns TextFormatter.Change.\nThis filter is very simple, it just checks to make sure that the new string after the change is applied will only contain digits. It does this by checking return value of getControlNewText(), which is what the string in the TextField would be if this change was applied, and then letting the change go through if it meets the regex test. Otherwise, the filter returns Null, and the change is blocked.\nYou can apply this filter by changing the instantiation of the TextFormatter by calling new TextFormatter(new PositiveIntegerStringConverter(), 0, new PositiveIntegerFilter()). When you do this, the TextField will now allow you to type in numbers, but anything else, including \u0026ldquo;-\u0026rdquo; and \u0026ldquo;.\u0026rdquo;, will be swallowed up by the filter.\nAt this point, the TextFormatter is complete and fully functional. Technically, we could probably go back and remove the negative checking in the fromString() function of the converter, but it\u0026rsquo;s probably best to leave it in so that PostiveIntegerStringConverter really does what its name implies. The important thing is that the filter and the converter need to be compatible with each other. It is possible to set up a converter with at toString() method that generates a string which cannot be edited at all by the rules enforced by the filter.\nIn Part 2, we\u0026rsquo;ll take a deeper look at TextFormatter.Change, and how to create a more complicated filter that makes large differences to the way that TextField works.\n","date":"11 May 2021","externalUrl":null,"permalink":"/javafx/elements/nodes/textformatter/part-1/","section":"JavaFX","summary":"An introduction to TextFormatter, showing you how to integrate it into a TextField and how to use it to convert the input from String to some other kind of value. Then we’ll look at how to add a filter to prevent the user from entering invalid data.","title":"TextFields and TextFormatter - Part 1","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # How to get a JavaFX project up and running fast. The Application class. The Stage class. The Scene class. Your first Nodes: Label and Region A little bit about organizing your code. The Fastest Start Possible # The quickest way to get started is to download this build.gradle file and put it in a directory all by itself.\nNow, open it with Intellij Idea. I\u0026rsquo;m sure you can do the same thing with Eclipse, too.\nThis should start up a new project. Let it build the Gradle environment and initialize the project. The select the \u0026ldquo;build-\u0026gt;build\u0026rdquo; option from the Gradle menu.\nNow go to the top level of the project and select, \u0026ldquo;New Directory\u0026rdquo;. The dialogue box should give you option for the Gradle defined source directories. Highlight them all and hit \u0026lt;Enter\u0026gt;.\nNext, if you want to change the group information in build.gradle from \u0026ldquo;ca.pragmaticcoding\u0026rdquo;, you should do it now. Then go to the \\src\\java directory and select \u0026ldquo;New -\u0026gt; Package\u0026rdquo;. You should create something like \u0026ldquo;ca.pragmaticcoding.beginners.part1\u0026rdquo;.\nYou\u0026rsquo;ll need a module-info.java file in the root of the java directory. It should look like this:\nmodule ca.pragmaticcoding.beginners { requires javafx.controls; requires javafx.graphics; requires javafx.base; exports ca.pragmaticcoding.beginners.part1; } Make sure that you change both references to the package ca.pragmaticcoding.beginners to whatever you are using.\nFinally, select that package in the Project listing, and then use the menu \u0026ldquo;New -\u0026gt; Java Class\u0026rdquo;, and call it \u0026ldquo;Main\u0026rdquo;. Copy the following into it:\nimport javafx.application.Application; import javafx.stage.Stage; public class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { } } And there you go! Now we have the skeleton of your first JavaFX application.\nApplication, Stage and Scene # Let\u0026rsquo;s take a quick look at this little bit of code, because there\u0026rsquo;s more going on there than it might seem.\nApplication # Application is an abstract class that has all of the functionality that gets the JavaFX engine up and running for you. This is all started up when its launch() method is called. Eventually, launch() will call start() which is an abstract method. We\u0026rsquo;re going to be putting all of our customized code for the application in here.\nStage and Scene # The most mystifying thing when you start with JavaFX is all this Stage and Scene stuff. Simply put, the Stage is the element that corresponds to the window on your screen. If you want to have multiple windows, you\u0026rsquo;ll generally need to have multiple Stages.\nScene is the component that holds the contents of your window. Why is Scene separate from Stage? I don\u0026rsquo;t honestly know, and I don\u0026rsquo;t know what advantage is gained by having the functionality split between two classes.\nOrdinarily, you\u0026rsquo;ll create a Scene and then put a JavaFX Region subclass object inside it. Then you put the Scene inside the Stage, and call the Stage's, show() method.\nSo let\u0026rsquo;s do that:\nimport javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.layout.Region; import javafx.stage.Stage; public class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); } private Region createContent() { return new Label(\u0026#34;Hello World\u0026#34;); } } Running the Application # You can run it now. I\u0026rsquo;d suggest using the \u0026ldquo;Application -\u0026gt; Run\u0026rdquo; from the Gradle menu. The output should look like this:\nNot very exciting really, but it is something that runs and opens a window.\nTwo little pieces of explanation first, and then we\u0026rsquo;ll conclude Part 1.\nYou\u0026rsquo;ll notice that I\u0026rsquo;ve split createContent() out from the rest of the code in start(), even though it\u0026rsquo;s really simple. Personally, I think start() should be all about Scene and Stage and getting the window on the screen. The details about the contents of the Scene really don\u0026rsquo;t belong in start(). Keeping methods small and purposeful is just as important with JavaFX as it is with any other programming.\nYou should also notice that createContent() returns Label as a Region. Strangely enough, all of the Control classes in JavaFX, including Label extend from Region, even though they don\u0026rsquo;t seem at first to be Region kind of objects. In the specific case of Label, there\u0026rsquo;s actually more going on to its structure than you think, and it actually does make sense to think of it as a Region. We\u0026rsquo;ll look at that later.\n","externalUrl":null,"permalink":"/beginners/part1/","section":"JavaFX","summary":"The very first step.  What else?  “Hello World” in JavaFX.","title":"Part 1 - A Quick Start","type":"javafx"},{"content":" Introduction # This is Part II of a series about setting up whole-home audio using SnapCast, Mopidy and RaspberryPi Zero\u0026rsquo;s (Pi0\u0026rsquo;s). SnapCast Part I gives an introduction to how SnapCast works, how we\u0026rsquo;re going to use it with Mopidy, and instructions for setting up the SnapCast server.\nIn this article we\u0026rsquo;ll look at setting up the Pi0\u0026rsquo;s as SnapCast clients.\nRaspberryPi Zero\u0026rsquo;s # The RaspberryPi Zero is one of the smallest of the RaspberryPi\u0026rsquo;s (with the Pico being a bit smaller). It looks like this:\nThere\u0026rsquo;s no banana for scale, but the board is about 6cm long.\nIt has built-in WiFi, which some of the earlier full-sized Pi\u0026rsquo;s did not, that adds a little bit more versatility to it. It also has the full set GPIO pins, just like the larger models. It\u0026rsquo;s a bit less powerful than most of the other models, with less memory, but it will do just fine as a SnapCast client.\nAt the time that I did the installations there were images available for Raspbian based on Debian 12, also known as \u0026ldquo;Bookworm\u0026rdquo;. I didn\u0026rsquo;t see anything listed for Debian 13, or \u0026ldquo;Trixie\u0026rdquo; (there was, and I missed it). So I went with the older Bookworm-based Raspbian.\nIn these instructions I\u0026rsquo;m going to stick with the Bookworm, because that\u0026rsquo;s what I used. However, it is highly likely that you could install Trixie and then use the Trixie compatible version of the SnapCast client software and it would work just fine.\nI consider these things, configured as they are, to be essentially \u0026ldquo;appliances\u0026rdquo;. They do a specific job, they\u0026rsquo;re not exposed to the outside world, and, once they are up and running, I don\u0026rsquo;t expect to be updated - ever. It is possible that one day there might be some new feature in SnapCast that I absolutely have to have, but I cannot imagine what that might be right now. They are working fine for me, and I wouldn\u0026rsquo;t be looking at upgrading just for bug fixes or minor changes. I certainly wouldn\u0026rsquo;t update just to move the devices to a newer O/S.\nBy the same token, it wouldn\u0026rsquo;t affect my homelab if the SnapCast project was halted and removed from GitHub tomorrow. My installation would just continue to work as it always had.\nThere\u0026rsquo;s really two elements that need to be configured on the RPi to make it work. The first is the audio hardware, and the second is the SnapCast client, which runs as a service. By far, the audio hardware was the most difficult to figure out, and required a fair bit of trial and error. Consequently, I didn\u0026rsquo;t create a step-by-step guide as I was doing it. I did end up with a process that I could follow 3 more times after I had figured it out.\nFor what it\u0026rsquo;s worth, when setting up the 2nd, 3rd and 4th clients I just SSH\u0026rsquo;d into the first one and typed history, then replicated the steps on the new RPi0.\nInstall the DAC Board # The RPi0 does not have any analog audio output, so you\u0026rsquo;ll need an add-on board to do this. For this project, I\u0026rsquo;ve used the Pimoroni DAC SHIM because it was readily available and inexpensive. It\u0026rsquo;s possible, even if you are using a larger RPi, that you\u0026rsquo;ll still need an external DAC of some sort to get adequate quality sound.\nThe Pimoroni SHIM looks like this:\nIf you are using the Pimoroni board, then orient it such that the 3.5mm jack is over top of the SD card slot on the RPi0. Then line up the holes with the GPIO pins and push it down, all the way to the bottom. It should be a tight fit and require a fair amount of pressure to get it to slide down. But not so much that you would describe it as \u0026ldquo;forcing\u0026rdquo; it down.\nThat\u0026rsquo;s all it takes. Some of the HiFiBerry boards have the same kind of connection, so they should be just as easy. If you\u0026rsquo;re using something else, then RTFM to figure it out.\nInstall Raspbian # If you are new to Raspberry Pi\u0026rsquo;s, then you need to understand that they usually boot from an operating system loaded onto an SD card of some sort. The RPi0\u0026rsquo;s use MicroSD cards. 8GB or 16GB cards should be more than enough. Bigger ones work just fine. The operating system is loaded via some other computer that you have that can read/write to SD cards.\nTo load the O/S onto the the SD card you should use the official Imager software. The instructions for installing it are here. I\u0026rsquo;m using Ubuntu on my desktop system, so I was able to just use sudo apt install rpi-imager and it was all done.\nWhen you run the imager, the first step is to pick the type of RPi device and then the O/S version. As of the time of writing this, the correct option was \u0026ldquo;Legacy\u0026rdquo;, not \u0026ldquo;Trixie\u0026rdquo;. This should give you the \u0026ldquo;Bookworm\u0026rdquo; based Raspbian, which is compatible with the SnapCast client software.\nOnce you\u0026rsquo;ve picked the output device, which should be your new MicroSD card, you\u0026rsquo;ll get an option to edit the settings. Do that.\nChange the hostname to something that makes sense to you. I picked \u0026ldquo;snapclient0\u0026rdquo;, then \u0026ldquo;snapclient1\u0026rdquo; for the next one, and so on.\nSet a username and a password, make note of both. Then input the WiFi connection details and set the time zone and locale.\nIn the \u0026ldquo;SERVICES\u0026rdquo; tab, enable SSH and use password authentication.\nClick \u0026ldquo;SAVE\u0026rdquo;, and then pick \u0026ldquo;YES\u0026rdquo;. Go through the \u0026ldquo;Are you sure?\u0026rdquo; warnings and let it run.\nWhen it\u0026rsquo;s done, stick the MicroSD card in the RPi0 and plug it in the power. Theoretically at least, in a little while, you should be able to SSH into it. In practice, I have found that they get stuck on the first boot waiting for some kind of keyboard input. So you might need to plug in a monitor, keyboard and mouse.\nBe warned that the RPi0 only has two USB \u0026ldquo;B\u0026rdquo; ports, and one is taken up with the power input. So you\u0026rsquo;ll need a USB hub to connect both a mouse and keyboard at the same time. Also note that those two USB ports are very close together, so if you\u0026rsquo;re using a bunch of adaptors and converters, you\u0026rsquo;ll need to make sure that they fit when the power port is populated.\nSecondly, the HDMI connector on the RPi0 is a mini HDMI, You\u0026rsquo;ll need a compatible HDMI cable or a converter for that.\nI\u0026rsquo;m going off memory here, but I think that one of the key things you\u0026rsquo;ll need to do with the KVM hooked up is to tell the RPi0 not to load the GUI desktop at boot. This will speed up subsequent boots and boost your performance. Also, you\u0026rsquo;ll never use the GUI environment.\nOnce you\u0026rsquo;ve got the RPi0 fully booted, you should be able to SSH into it. I\u0026rsquo;d give it one more test boot, just to make sure completes a \u0026ldquo;hands free\u0026rdquo; boot, before disconnecting the KVM equipment.\nI know these instructions seem a little vague, but there\u0026rsquo;s nothing difficult or tricky about this part, and the steps should be obvious as you run through it.\nSSH into your RPi0 and start configuring the SnapCast client\u0026hellip;\nEdit The Firmware Configuration # This is how you tell the RPi to use the add-on board and not any native audio equipment.\nThe file you need to edit is /boot/firmware/config.txt. I\u0026rsquo;ve marked the lines that needed to be changed or added:\n# For more options and information see # http://rptl.io/configtxt # Some settings may impact device functionality. See link above for details # Uncomment some or all of these to enable the optional hardware interfaces dtparam=i2c_arm=on \u0026lt;---- uncomment dtparam=i2s=on \u0026lt;---- uncomment dtparam=spi=on \u0026lt;---- uncomment # Enable audio (loads snd_bcm2835) dtparam=audio=off # Additional overlays and parameters are documented # /boot/firmware/overlays/README # Automatically load overlays for detected cameras camera_auto_detect=1 # Automatically load overlays for detected DSI displays display_auto_detect=1 # Automatically load initramfs files, if found auto_initramfs=1 # Enable DRM VC4 V3D driver dtoverlay=vc4-kms-v3d max_framebuffers=2 # Don\u0026#39;t have the firmware create an initial video= setting in cmdline.txt. # Use the kernel\u0026#39;s default instead. disable_fw_kms_setup=1 # Disable compensation for displays with overscan disable_overscan=1 # Run as fast as firmware / board allows arm_boost=1 dtoverlay=hifiberry-dac \u0026lt;--- add this line gpio=25=op,dh [cm4] # Enable host mode on the 2711 built-in XHCI USB controller. # This line should be removed if the legacy DWC2 controller is required # (e.g. for USB device mode) or if USB support is not required. otg_mode=1 [cm5] dtoverlay=dwc2,dr_mode=host [all] gpio=25=op,dh \u0026lt;--- add this line You will also need a file called /etc/asound.conf. You\u0026rsquo;ll probably have to create it, and it needs to have just two lines:\ndefaults.pcm.card 1 defaults.ctl.card 1 Installing the SnapClient # The next step is to install the SnapClient service software. Just as with the SnapServer software, it\u0026rsquo;s best to download it from GitHub and then use dpkg to install it. It\u0026rsquo;s also advisable to make sure that all of your O/S elements are updated before you attempt the installation, so I\u0026rsquo;ve included those steps in here:\n4 wget https://github.com/snapcast/snapcast/releases/download/v0.34.0/snapclient_0.34.0-1_armhf_bookworm.deb 5 sudo apt update 6 sudo apt upgrade 7 sudo dpkg -i snapclient_0.34.0-1_armhf_bookworm.deb This will install the snapclient software and set it up as a service. You should set it up to run at boot:\nsudo systemctl enable snapclient In most versions of Linux, you use systemctl to control sevices on the system. The enable command in systemctl tells the system to load the service at boot time.\nBefore you do anything else, you should configure the client. Edit the file /etc/default/snapclient. The file should already be there, and it should look something like this when you are done:\n# Start the client, used only by the init.d script START_SNAPCLIENT=true # Additional command line options that will be passed to snapclient # note that user/group should be configured in the init.d script or the systemd unit file # For a list of available options, invoke \u0026#34;snapclient --help\u0026#34; SNAPCLIENT_OPTS=\u0026#34;-h 10.93.50.107 -s 1\u0026#34; Obviously, you\u0026rsquo;ll need to put in whatever IP address your SnapServer is running on, instead of mine. The -s 1 is important as it tells the SnapCast client to use the DAC board.\nAt this time, you should reboot the RPi0, which will cause it to load the new audio configuration and start the SnapClient service.\nTesting and Debugging # If everything has gone well, then your SnapCast server should have received a connection request from your RPi0. This means that you should see it if you go back into your SnapCast web page.\nIf you issue the following command on your SnapCast server:\nsudo journalctl -u snapserver -n40 You should see a line near the bottom that looks something like this:\nApr 02 18:28:40 snapcast snapserver[41889]: (Server) Hello from b8:27:eb:a3:3d:60, host: snapcast0, v0.34.0, ClientName: Snapclient, OS: Raspbian GNU/Linux 12 (bookworm), Arch: armv6l, Protocol version: 2, Auth: {\u0026#34;param\u0026#34;:\u0026#34;\u0026#34;,\u0026#34;scheme\u0026#34;:\u0026#34;\u0026#34;} If not, then your RPi0 hasn\u0026rsquo;t connected to SnapCast server. Back on the RPi0, you should be able to do this:\n$ sudo systemctl status snapclient --lines 30 ● snapclient.service - Snapcast client Loaded: loaded (/lib/systemd/system/snapclient.service; enabled; preset: enabled) Active: active (running) since Fri 2026-04-03 12:46:35 EDT; 3min 16s ago Docs: man:snapclient(1) Main PID: 17962 (snapclient) Tasks: 2 (limit: 373) CPU: 32.803s CGroup: /system.slice/snapclient.service └─17962 /usr/bin/snapclient --hostID SomeRoom --logsink=system -h 10.93.50.107 -s 1 Apr 03 12:46:35 snapcast1 systemd[1]: Started snapclient.service - Snapcast client. Apr 03 12:46:35 snapcast1 snapclient[17962]: (Snapclient) Options \u0026#39;--host\u0026#39; and \u0026#39;--port\u0026#39; are deprecated. Please add the server URI as last command line argument Apr 03 12:46:35 snapcast1 snapclient[17962]: (Snapclient) Version 0.34.0, revision 0ac55084 Apr 03 12:46:35 snapcast1 snapclient[17962]: (Connection) Resolving host IP for: 10.93.50.107 Apr 03 12:46:35 snapcast1 snapclient[17962]: (Connection) Connecting to host: 10.93.50.107:1704, port: 1704, protocol: tcp Apr 03 12:46:35 snapcast1 snapclient[17962]: (Connection) Connected to 10.93.50.107 Apr 03 12:46:35 snapcast1 snapclient[17962]: (Connection) My MAC: \u0026#34;b8:27:eb:7b:b4:33\u0026#34;, socket: 9 Apr 03 12:46:36 snapcast1 snapclient[17962]: (Controller) ServerSettings - buffer: 2000, latency: 0, volume: 90, muted: 0 Apr 03 12:46:36 snapcast1 snapclient[17962]: (Controller) Codec: flac, sampleformat: 48000:16:2 Apr 03 12:46:36 snapcast1 snapclient[17962]: (Player) Player name: alsa, device: lavrate, description: Rate Converter Plugin Using Libav/FFmpeg Library, idx: 1, sharing mode: unspecified, parameters: \u0026lt;none\u0026gt; Apr 03 12:46:36 snapcast1 snapclient[17962]: (Player) Mixer mode: software, parameters: \u0026lt;none\u0026gt; Apr 03 12:46:36 snapcast1 snapclient[17962]: (Player) Sampleformat: 48000:16:2, stream: 48000:16:2 Apr 03 12:46:36 snapcast1 snapclient[17962]: (Alsa) Using default buffer_time: 80 ms, default fragments: 4 Apr 03 12:46:36 snapcast1 snapclient[17962]: (Alsa) PCM name: lavrate, sample rate: 48000 Hz, channels: 2, buffer time: 80000 us, periods: 4, period time: 20000 us, period frames: 960 Apr 03 12:46:36 snapcast1 snapclient[17962]: (Stream) No chunks available Apr 03 12:46:36 snapcast1 snapclient[17962]: (Alsa) Failed to get chunk Apr 03 12:46:36 snapcast1 snapclient[17962]: (Controller) diff to server [ms]: -3.68704e+08 You should see that it is active in the \u0026ldquo;Active:\u0026rdquo; line. You should also see that it says \u0026ldquo;enabled\u0026rdquo; somewhere in the \u0026ldquo;Loaded:\u0026rdquo; line.\nYou should also see in the \u0026ldquo;CGroup:\u0026rdquo; line 2, that you have a host IP address that is correct for your SnapCast server.\nUnderneath the summary is most recent journal log output since the service has been started. You should see the interaction between the client and the server happening here.\nYou can push the white noise through the fifo again, and you should hear it through the RPi0. Just about anything that you can plug into a 3.5mm jack should work. A powered speaker, an amplifier, even headphones.\nWhat If You Don\u0026rsquo;t Hear Anything # If you saw the client connecting to the server, but you don\u0026rsquo;t hear anything. Then you have a problem with your sound configuration or you haven\u0026rsquo;t included that \u0026ldquo;-s 1\u0026rdquo; in the client options. Check that last bit first.\nYou can confirm the sound devices with aplay:\n$ aplay -l **** List of PLAYBACK Hardware Devices **** card 0: vc4hdmi [vc4-hdmi], device 0: MAI PCM i2s-hifi-0 [MAI PCM i2s-hifi-0] Subdevices: 1/1 Subdevice #0: subdevice #0 card 1: sndrpihifiberry [snd_rpi_hifiberry_dac], device 0: HifiBerry DAC HiFi pcm5102a-hifi-0 [HifiBerry DAC HiFi pcm5102a-hifi-0] Subdevices: 0/1 Subdevice #0: subdevice #0 That \u0026ldquo;card 1\u0026rdquo; should line up with your \u0026ldquo;-s 1\u0026rdquo;. If the DAC has some different number then use it for the \u0026ldquo;-s\u0026rdquo; option.\nIt took me a lot of fiddling about to find out how to get the setup right on the very first RPi0 that I set up, because I didn\u0026rsquo;t get any sound at the beginning. Looking back at my history stack from that first RPi0, I see that aplay was really the tool that let\u0026rsquo;s you figure it out.\nYou can use aplay to play a file. This let\u0026rsquo;s you eliminate the SnapCast elements and confirm that, yes, your RPi0 is sending sound through the DAC:\n$ wget https://sample-files.com/downloads/audio/wav/pure-tone.wav $ aplay pure-tone.wav Take a good look at the man pages for aplay. There are lots of different options you can play with to understand what\u0026rsquo;s going on with you sound system.\nConclusion # At this point you should have a Raspberry Pi Zero running as a SnapCast client that you can send sound to from a SnapCast server!\nI can say that the first one took me a couple of days to get running because the documentation just isn\u0026rsquo;t there, and there are quite a few moving parts to figure out at the beginning. However, if you are using a RPi0 and a HiFiBerry or Pimoroni DAC there is no reason that you need to have go through the same experience because all of the details are right here in this article.\nThis is Actually Easy # I\u0026rsquo;ve now set up 4 of these RPi0\u0026rsquo;s and it takes about half an hour all-in, including the 15 minutes or so that the Imager program takes to set up the SD card. Here is the entire history stack from the 4th one:\n1 ip a 2 sudo apt update 3 sudo apt upgrade 4 sudo vi /boot/firmware/config.txt 5 wget https://github.com/snapcast/snapcast/releases/download/v0.34.0/snapclient_0.34.0-1_armhf_bookworm.deb 6 sudo dpkg -i snapclient_0.34.0-1_armhf_bookworm.deb 7 sudo systemctl status snapclient 8 sudo vi /etc/default/snapclient 9 sudo systemctl restart snapclient 10 sudo systemctl status snapclient 11 aplay -l 12 sudo reboot 13 aplay -l 14 sudo systemctl status snapclient 15 sudo vi /etc/asound.conf 16 sudo reboot The ip a at the beginning was to make it easier for me to find the device in my DHCP leases and set the address as static there.\nAfter line 10, I don\u0026rsquo;t think it was playing anything when I plugged it into the amp. Then you can see me checking aplay -l, rebooting it to make sure it had reread the configuration, then checking again. I finally realized that I had forgotten to create /etc/asound.conf, so I created it. Then another reboot, and sound was coming out of it.\nAt that point I disconnected from it and haven\u0026rsquo;t log in again until I started writing this article.\nIn Part III\u0026hellip; # Part III is the final article in the series, and shows how to set up Mopidy to stream music and how to connect it to your SnapCast server. It also shows how you can use the Mopidy REST API to do some simple automation and stream recovery.\n","date":"21 June 2026","externalUrl":null,"permalink":"/homelab/snapcast/part1/","section":"Homelabs","summary":"Part 2 of a 3 part series describing how to set up whole-home audio using SnapCast, Mopidy and Rasberry Pi’s.  In this article, we look at how to configure a Raspberry Pi Zero as a SnapCast client.","title":"Whole Home Audio With SnapCast - Part II","type":"homelab"},{"content":"The popularity of FXML frustrates me.\nNot so much because FXML is bad. It does what it does about as well as you could expect.\nBut because of two things\u0026hellip;\nClaims That it Does Much More Than it Actually Does # There are lots of claims (or beliefs, if you like) that FXML creates a meaningful \u0026ldquo;separation of concerns\u0026rdquo;, that it provides the \u0026ldquo;View\u0026rdquo; and \u0026ldquo;Controller\u0026rdquo; in Model-View-Controller, and that its \u0026ldquo;declarative structure\u0026rdquo; simplifies applications.\nPhooey!\nIt doesn\u0026rsquo;t do any of these things.\nIt does one thing, and one thing only: It lets you use SceneBuilder.\nIn my opinion, that\u0026rsquo;s not really that much. Yes, SceneBuilder is drag and drop design that lets you see how the screen will look (mostly!) as you build the layout. But how valuable is that? In my experience, not much. I can cycle through test runs of a hand coded screen just about as fast as someone can do the same in SceneBuilder, and I\u0026rsquo;ve actually got working code at the end.\nBut the cost of using SceneBuilder\u0026hellip;\nFXML Makes Everything Else More Complex # Over the years, it has become clear to me that dealing with the output of SceneBuilder, which is FXML, complicates every other aspect of your application.\nIf nothing else, the mysterious nature of FXMLLoader confuses every single beginner that ever started their JavaFX journey using it. No beginners understand how it works. None.\nAnd heaven forbid you want to have a custom constructor for your FXMLController with passed parameters. Now you\u0026rsquo;ve got to create ControllerFactories and do even more unfathomable things to make it work. And the beginners are getting shell-shocked.\nAnd don\u0026rsquo;t forget that you cannot do anything meaningful with FXML without having a custom constructor for the FXMLController. At least, not if you want to use some framework properly.\nThe net result is that virtually every example of FXML + FXMLController that you find on the web ends up with the FXMLController ends up being a \u0026ldquo;God class\u0026rdquo;, doing all of the application logic. And that means that is what every beginner does, and they think that\u0026rsquo;s the way it\u0026rsquo;s supposed to be.\nIn my opinion, the cost of FXML outweighs the dubious benefits of SceneBuilder - which, by the way, is virtually useless as a beginner learning tool - and I suspect that many, many beginners to JavaFX have just given up out of frustration.\nSo, don\u0026rsquo;t use FXML.\nJust write your layouts in code. It almost sounds crude, but it\u0026rsquo;s easy, it works and it can result in much simpler applications.\n","date":"22 July 2025","externalUrl":null,"permalink":"/javafx/techniques/nofxml/","section":"JavaFX","summary":"It seems that one of the initial selling points of JavaFX, back in 2014 was that you could use drag and drop to design your layouts, and this would be stored as something called FXML.  I don’t think you should use it, and you can read why right here.","title":"Don't Use FXML","type":"javafx"},{"content":" Introduction # If you use JavaFX, then you are almost certainly using the Modena stylesheet - or a custom stylesheet designed to work with the standard JavaFX Node subclasses in much the same way that Modena does.\nUnfortunately, Modena is big. Like\u0026hellip; 3,440 lines big. And it can seem very complicated because it uses a lot of advanced and subtle techniques that aren\u0026rsquo;t obvious if you\u0026rsquo;re not a CSS guru.\nThis article is about understanding Modena, and how it interacts with the standard JavaFX Nodes. The intention is that once you\u0026rsquo;ve read this article, you should be able to look at the styling of just about any of those Nodes on the screen, and then find the right places in Modena that make it look way and understand how it does it.\nYou aren\u0026rsquo;t going to see any specific methods for handling any specific Node classes here. This article is all about giving you the tools to be able to figure that for yourself, which is far more valuable.\nThe Components of JavaFX Styling # In JavaFX there are four basic elements that are used to create styling for a Node subclass.\nStyleableProperties\nThese are the base units of styling in JavaFX. Every StyleableProperty has a connection back to a stylesheet attribute and provides the mechanism to turn that stylesheet attribute setting into something visual in the GUI. CSS Selectors\nStylesheets are broken down into sections that are identified via a name called a \u0026ldquo;Selector\u0026rdquo;. JavaFX Nodes contain a list of CSS selectors that will be applied, in order, to provide values for the various StyleableProperties defined for the Node. When Node subclasses inherit from their parent Nodes they can either start a brand new list of selectors, or add new values to the inherited list. Pseudoclasses\nPseudoClasses are used to provide selectors that are applied to a Node on a temporary basis. Each PseudoClass is a Boolean value that is associated with a CSS selector. When the PseudoClass value is true, then the selector is applied to the Node and then removed when it changes to false. StyleSheets\nStylesheets contain all of the specifications of how the StyleableProperties will be applied to various Nodes in the GUI. Multiple stylesheets can be added at the Stage, Scene or root Node level of a window and they will be applied in the order in which they are listed. In this article we are going to concentrate on the last item in this list, the stylesheet. Specifically, we are going to look at the Modena stylesheet, which is the default stylesheet that has shipped with JavaFX for quite a few years.\nModena is Integrated with the JavaFX Library # While it is easy enough to say, \u0026ldquo;We\u0026rsquo;re going to look at the Modena stylesheet\u0026rdquo;, in truth you cannot study it in isolation. Take a look at this snippet:\n.web-view .form-select-button { -fx-background-radius: 2, 2, 1, 0; -fx-background-insets: 2 2 1 2, 2, 3, 4; } To understand what this does you need to understand what the selector \u0026ldquo;.web-view .form-select-button\u0026rdquo; means. This is defined somewhere in the JavaFX source code, probably in the skin for the WebView control. In the same way, the meanings of \u0026ldquo;-fx-background-radius\u0026rdquo; and \u0026ldquo;-fx-background-insets\u0026rdquo; are defined as StyleableProperties somewhere in the class hierarchy leading to WebView, probably in Region.\nIt should be clear at this point that Modena and the JavaFX classes are closely integrated, and you cannot understand Modena without understanding how the various Node classes work and are interelated. One of the biggest challenges is when a Control is actually composed of a number of different Controls - and this happens a lot in JavaFX. Just look at the example above, we can infer that WebView is composed of a number of different elements, one of which is a Button that is related to some function identified as \u0026ldquo;form select\u0026rdquo;.\nApplying your own custom styling to a Node is generally going to involve adding a CSS selector in your own stylesheet for that Node, and then defining values for the various attributes that apply to that Node class, or its superclasses.\nGetting a Copy of Modena # I would strongly suggest that you download a copy of Modena and save it somewhere on your system where you can get at it easily for future reference. It used to be easiest to just extract it from whatever JFX jar you had downloaded to use in your projects. Nowadays, using Maven or Gradle the associated plugin will download OpenJFX is the background and you don\u0026rsquo;t have to go through the process of grabbing the jar for yourself. You can still pull Modena out of your IDE, however, if you know where to look.\nOnce you\u0026rsquo;ve compiled a JavaFX project at least once with Gradle or Maven, you should have a section in your project called \u0026ldquo;External Libraries\u0026rdquo;. Somewhere in that section you\u0026rsquo;ll have a bunch of OpenJFX items. You\u0026rsquo;re looking for the one that is called \u0026ldquo;org.openjfx:javafx-controls:{O/S}:{version}\u0026rdquo;. For me this is \u0026ldquo;org.openjfx:javafx-controls:linux:23\u0026rdquo;.\nInside that library, you are looking for the following path: com.sun.javafx.scene.control.skin.modena.modena.css. And that\u0026rsquo;s the file. You can just open it in your IDE, or you can copy it somewhere that makes it easier for you.\nYou can get a copy directly from the GitHub project for OpenJFX is the link for the JFX 23 version.\nThe Reference Guide # The last thing that you\u0026rsquo;ll need is the CSS Reference Guide for JavaFX. This is available as a web page. For JFX 23, it can be found here:\nhttps://openjfx.io/javadoc/23/javafx.graphics/javafx/scene/doc-files/cssref.html\nYou should bookmark this, you\u0026rsquo;ll need it a lot.\nThe Colours # A large part of understanding Modena boils down to understanding how it deals with colours. Modena does a lot of hocus-pocus with colours and huge amounts of the definitions are about defining and applying various colours.\nBase Colours # In reality, Modena has very few colours defined as simple colours. Virtually all of the colours are derived from a handful of base colours, and you can completely change the colour scheme of your application just by redefining some of those colours. Let\u0026rsquo;s take a look at them, these are all defined in the .root section:\n-fx-base: #; /* A very light grey used for the background of windows. See also * -fx-text-background-color, which should be used as the -fx-text-fill * value for text painted on top of backgrounds colored with -fx-background. */ -fx-background: derive(-fx-base,26.4%); /* Used for the inside of text boxes, password boxes, lists, trees, and * tables. See also -fx-text-inner-color, which should be used as the * -fx-text-fill value for text painted on top of backgrounds colored * with -fx-control-inner-background. */ -fx-control-inner-background: derive(-fx-base,80%); -fx-dark-text-color: black; -fx-mid-text-color: #333; -fx-light-text-color: white; -fx-accent: #0096C9; -fx-default-button: #ABD8ED; -fx-focus-color: #039ED3; -fx-faint-focus-color: #039ED322; /* The color that is used in styling controls. The default value is based * on -fx-base, but is changed by pseudoclasses to change the base color. * For example, the \u0026#34;hover\u0026#34; pseudoclass will typically set -fx-color to * -fx-hover-base (see below) and the \u0026#34;armed\u0026#34; pseudoclass will typically * set -fx-color to -fx-pressed-base. */ -fx-color: -fx-base; -fx-background-color: -fx-background; I\u0026rsquo;ve left the comments in because they are useful. Here are the main colours:\n-fx-default-button -fx-base -fx-accent -fx-focus-color This is, as you can see, the familiar blue and gray theme that you see in most JavaFX applications. You can change it by adding the following stylesheet to your application:\n.root { -fx-default-button: #EE8434; -fx-focus-color: #C95D63; -fx-faint-focus-color: #C95D6322; } And it will look like this:\nThere\u0026rsquo;s a lot of other colour definitions in the .root section, but they virtually all involve values derived from the handful listed above. For instance:\n/* A little lighter than -fx-base and used as the -fx-color for the * \u0026#34;hovered\u0026#34; pseudoclass state. */ -fx-hover-base: ladder( -fx-base, derive(-fx-base,20%) 20%, derive(-fx-base,30%) 35%, derive(-fx-base,40%) 50% ); or:\n/* A little darker than -fx-color and used to draw boxes around objects such * as progress bars, scroll bars, scroll panes, trees, tables, and lists. */ -fx-box-border: ladder( -fx-color, black 20%, derive(-fx-color,-15%) 30% ); /* Darker than -fx-background and used to draw boxes around text boxes and * password boxes. */ -fx-text-box-border: ladder( -fx-background, black 10%, derive(-fx-background, -15%) 30% ); You get the idea.\nYou will also see a lot of uses of derive, gradient and ladder in Modena. You will need to become comfortable with these concepts in order to understand how Modena works.\nRedefining Colours # One \u0026ldquo;trick\u0026rdquo; that you\u0026rsquo;ll see quite often in Modena is contextual redefinition of one of the named colours. This most often happens in Pseudo-class selectors like this:\n.button:hover { -fx-color: -fx-hover-base; } What this means is that for a Button, when it has the hover Pseudo-class applied, the value of -fx-color will be changed to be whatever color -fx-hover-base has been defined as. As you\u0026rsquo;ll see in the next section Button has 4 background colours. Let\u0026rsquo;s look at one of them:\n-fx-body-color: linear-gradient(to bottom, ladder( -fx-color, derive(-fx-color,8%) 75%, derive(-fx-color,10%) 80% ), derive(-fx-color,-8%)); You can see how changing -fx-color will change the definition of -fx-body-color, and this will change the look of the Button when the mouse is hovering over it.\nSometimes a Node will retain most of its inherited styling, but just change the definition of a colour:\n.list-cell, .tree-cell { -fx-background: -fx-control-inner-background; -fx-background-color: -fx-background; -fx-text-fill: -fx-text-background-color; } Remember that this colour change will also hold for any sub-components of the Node. In this case, since ListCell is a subclass of Labeled, there will be a graphic and a text element.\nSometimes you see this redefinition used inside a sub-component of another Node:\n.combo-box \u0026gt; .list-cell { -fx-background: transparent; -fx-background-color: transparent; -fx-text-fill: -fx-text-base-color; -fx-padding: 0.333333em 0.666667em 0.333333em 0.666667em; /* 4 8 4 8 */ } Here we see the -fx-background redefined from its previous redefinition when it is the the ListCell in the main part of a ComboBox.\nDefault Styling # If you look at the styling for list-cell in the section above, you\u0026rsquo;ll see that there isn\u0026rsquo;t much there. Where does the rest of the styling come from?\nThe hierarchy to get to ListCell is thisNode -\u0026gt; Parent -\u0026gt; Region -\u0026gt; Control -\u0026gt; Labeled -\u0026gt; Cell -\u0026gt; IndexedCell -\u0026gt; ListCell. Not all of these classes have CSS selectors specified, Node, Parent, Region, Control and Labeled do not. While there are selectors for ListCell, as we\u0026rsquo;ve seen, there are no selectors in Modena for IndexedCell or Cell, although there are selectors for sub-components and Pseudo-classes of Cell.\nSo where does the default styling come from?\nThe answer is to look at Region where you\u0026rsquo;ll find this:\nprivate static class StyleableProperties { private static final CssMetaData\u0026lt;Region, Insets\u0026gt; PADDING; private static final CssMetaData\u0026lt;Region, Insets\u0026gt; OPAQUE_INSETS; private static final CssMetaData\u0026lt;Region, Background\u0026gt; BACKGROUND; private static final CssMetaData\u0026lt;Region, Border\u0026gt; BORDER; private static final CssMetaData\u0026lt;Region, Shape\u0026gt; SHAPE; private static final CssMetaData\u0026lt;Region, Boolean\u0026gt; SCALE_SHAPE; private static final CssMetaData\u0026lt;Region, Boolean\u0026gt; POSITION_SHAPE; private static final CssMetaData\u0026lt;Region, Boolean\u0026gt; CACHE_SHAPE; private static final CssMetaData\u0026lt;Region, Boolean\u0026gt; SNAP_TO_PIXEL; private static final CssMetaData\u0026lt;Region, Number\u0026gt; MIN_HEIGHT; private static final CssMetaData\u0026lt;Region, Number\u0026gt; PREF_HEIGHT; private static final CssMetaData\u0026lt;Region, Number\u0026gt; MAX_HEIGHT; private static final CssMetaData\u0026lt;Region, Number\u0026gt; MIN_WIDTH; private static final CssMetaData\u0026lt;Region, Number\u0026gt; PREF_WIDTH; private static final CssMetaData\u0026lt;Region, Number\u0026gt; MAX_WIDTH; private static final List\u0026lt;CssMetaData\u0026lt;? extends Styleable, ?\u0026gt;\u0026gt; STYLEABLES; private StyleableProperties() { } static { PADDING = new CssMetaData\u0026lt;Region, Insets\u0026gt;(\u0026#34;-fx-padding\u0026#34;, InsetsConverter.getInstance(), Insets.EMPTY) { public boolean isSettable(Region var1) { return var1.padding == null || !var1.padding.isBound(); } public StyleableProperty\u0026lt;Insets\u0026gt; getStyleableProperty(Region var1) { return (StyleableProperty)var1.paddingProperty(); } }; . . . MAX_WIDTH = new CssMetaData\u0026lt;Region, Number\u0026gt;(\u0026#34;-fx-max-width\u0026#34;, SizeConverter.getInstance(), -1.0) { public boolean isSettable(Region var1) { return var1.maxWidth == null || !var1.maxWidth.isBound(); } public StyleableProperty\u0026lt;Number\u0026gt; getStyleableProperty(Region var1) { return (StyleableProperty)var1.maxWidthProperty(); } }; } } This is where the default values are established, in the code, and they will be passed to sub-classes through inheritance. There are even more defined in Node, and presumably any class in the hierachy can inject its own styleable properties into the structure. You don\u0026rsquo;t have to look into the code to find the values, though, the CSS Reference Guide for Region has all this information.\nEnclosed Components # It is important to understand that the standard JavaFX Nodes that are composed of other Nodes, especially the ones with skins, don\u0026rsquo;t have any special qualities when it comes to the stylesheets. Any Region that you use in a layout to hold child Nodes, even nested layout classes, will interact with the stylesheet in exactly the same way.\nCSS has two ways to specify compound selectors (which refer to Nodes which are children of other Nodes). The first is .:\n.slider .thumb { -fx-background-color: linear-gradient(to bottom, derive(-fx-text-box-border, -20%), derive(-fx-text-box-border, -30%)), -fx-inner-border, -fx-body-color; -fx-background-insets: 0, 1, 2; -fx-background-radius: 1.0em; /* makes sure this remains circular */ -fx-padding: 0.583333em; /* 7 */ -fx-effect: dropshadow(two-pass-box , rgba(0, 0, 0, 0.1), 5, 0.0 , 0, 2); } and the second is \u0026gt; .:\n.tool-bar \u0026gt; .container \u0026gt; .separator { -fx-orientation: vertical; } The first form refers to any occurance of the second selector in any of the children, or the children of children, of the first selector. The second form refers only to occurances of the second selector in direct children of the first selector. You\u0026rsquo;ll find many more usages of the second form in Modena than you will of the first form.\nHere\u0026rsquo;s an example of how this works:\nclass ModenaExample1 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 300.0, 240.0).apply { ModenaExample1::class.java.getResource(\u0026#34;example1.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = VBox(10.0).apply { styleClass += \u0026#34;nest-outer\u0026#34; children += Button(\u0026#34;Outside\u0026#34;) children += VBox(10.0).apply { styleClass += \u0026#34;nest-middle\u0026#34; children += Button(\u0026#34;Middle\u0026#34;) children += VBox(10.0).apply { styleClass += \u0026#34;nest-inner\u0026#34; children += Button(\u0026#34;Inside\u0026#34;) } } } } fun main() = Application.launch(ModenaExample1::class.java) This is just a set of nested VBoxes, each having a single Button in them. Here\u0026rsquo;s the stylesheet:\n.nest-outer { -fx-background : blanchedalmond; -fx-padding: 20px; -fx-border-color: green; -fx-border-width: 3px; -fx-body-color: coral; } .nest-middle { -fx-border-color: blue; -fx-border-width: 3px; -fx-padding: 20px; } .nest-inner { -fx-border-color: red; -fx-border-width: 3px; -fx-padding: 20px; } .nest-middle .button { -fx-body-color: thistle; } .nest-middle \u0026gt; .button { -fx-body-color: aqua; } Each of the VBoxes has a different coloured border, so that you can see where they are, and the outermost VBox with the selector, nest-outer, redefines -fx-body-color which is the innermost colour in the background for Button. It looks like this:\nFirst, you can see that the redefinition of -fx-background holds for all of the elements inside of the nest-outer VBox because all three VBoxes have that same colour.\nThe last two selectors in the stylesheet redefine -fx-body-color for Buttons inside of the nest-middle VBox. The first one redefines it for every Button inside nest-middle and the second, the \u0026gt; . selector, redefines it for Buttons directly contained inside nest-middle. The order of the selectors inside the stylesheet is important, and if we reverse these two last selectors we get this:\nThis is becasue the more general selector would supercede the more specific \u0026gt; . selector because it came second.\nModena Doesn\u0026rsquo;t Use Borders! # Let\u0026rsquo;s look at the styling for (amongst other things) Button:\n.button { -fx-background-color: -fx-shadow-highlight-color, -fx-outer-border, -fx-inner-border, -fx-body-color; -fx-background-insets: 0 0 -1 0, 0, 1, 2; -fx-background-radius: 3px, 3px, 2px, 1px; -fx-padding: 0.333333em 0.666667em 0.333333em 0.666667em; /* 4 8 4 8 */ -fx-text-fill: -fx-text-base-color; -fx-alignment: CENTER; -fx-content-display: LEFT; } Here we have 4 different backgrounds defined. The are applied in order, each successive background going on top of the previous backgrounds. However, the backgrounds on the bottom are all a little bit bigger than the ones that follow, at least in some dimension. This is defined by the insets.\nLet\u0026rsquo;s look at how this works\u0026hellip;\nThe background on the bottom is -fx-shadow-highlight-color and its insets are 0 except for the bottom, which is -1. This means that this background will exactly match the size of the Button on the top, left and right, but extend 1px down below the bottom of the Button.\nThe next background is -fx-outer-border and it has insets of 0 (or 0,0,0,0), which means that it will exactly match the size of the Button. This will cover up all of the first background, excepty for that strip of 1px at the bottom where it sticks out below the Button. This is hard to see with the normal palette on a white background, but if we change -fx-shadow-highlight-color to red in our custom colour stylesheet, it\u0026rsquo;s quite clear:\nThe third background is -fx-inner-border and it has insets of 1. This will leave a 1px wide strip all around the edge of the Button where the second background is visible.\nFinally, the last background is -fx-body-color and it has insets of 2. This will be the colour that you see for most of Button and it will leave a 1px wide ring of the third background visible just inside the dimensions of the Button.\nEssentially, we have a \u0026ldquo;border\u0026rdquo; made up of three different colours - at least on the bottom - and then a background of -fx-body-color. It makes the Button look three dimensional.\nThis is the technique that you will see used over, and over in Modena. It\u0026rsquo;s actually very rare to see a real border used in Modena. The attribute -fx-border-width is set only 7 times in the entire file, and -fx-border-color is set to something non-transparent only 15 times.\nSo borders are used in Modena, it\u0026rsquo;s just that most of them are composed of stacked backgrounds, and then a tiny handful are actual borders as defined in the JavaFX library.\nThe Standard PseudoClasses # In order to understand Modena, you\u0026rsquo;ll need to know about the various Pseudo-classes that are used. These are defined in the JavaFX source code for the various nodes, but you will seem them referenced in Modena, and many are available but not used in Modena itself. You can, however, use them in your own style sheets to customize based on Modena.\nAll Node subclasses support the following Pseudo-classes:\ndisabled focused focus-visible focus-within hover pressed show-mnemomonic Various other Node subclasses support some of these common Pseudo-classes:\narmed cancel default selected determinate indeterminate empty filled editable showing vertical horizontal And then there are a number of Pseudo-classes with a very specific scope. All of these are documented in the JavaFX CSS Reference Guide.\nHere\u0026rsquo;s an example of how Modena commonly implements Pseudo-classes:\n.toggle-button:selected { -fx-background-color: -fx-shadow-highlight-color, linear-gradient(to bottom, derive(-fx-outer-border, -20%), -fx-outer-border), linear-gradient(to bottom, derive(-fx-color, -22%) 0%, derive(-fx-color, -13%) 20%, derive(-fx-color, -11%) 50%); -fx-background-insets: 0 0 -1 0, 0, 1; } This is the selected Pseudo-class for a ToggleButton. You can see that it\u0026rsquo;s mostly a colour change, which isn\u0026rsquo;t surprising. This definition sets the background colour and insets.\nYou can see that there are three different background colours stacked on top of each other to create a three dimensional \u0026ldquo;border\u0026rdquo; in a manner similar to what we saw with Button. And that\u0026rsquo;s all this Pseudo-class does, changes the background and border so that it looks \u0026ldquo;selected\u0026rdquo;.\nYou will also see Pseudo-classes stacked quite often:\n.toggle-button:selected:focused { -fx-background-color: -fx-focus-color, linear-gradient(to bottom, derive(-fx-color, -22%) 0%, derive(-fx-color, -13%) 20%, derive(-fx-color, -11%) 50%), -fx-faint-focus-color, linear-gradient(to bottom, derive(-fx-color, -22%) 0%, derive(-fx-color, -13%) 20%, derive(-fx-color, -11%) 50%); -fx-background-insets: -0.2, 1, -1.4, 2.6; -fx-background-radius: 3, 2, 4, 0; } This is the selector for a ToggleButton that is both \u0026ldquo;selected\u0026rdquo; and \u0026ldquo;focused\u0026rdquo;.\nShared Definitions # When multiple selectors have the same settings, Modena will combine them into a single entry using \u0026ldquo;,\u0026rdquo; to separate the selectors. For instance, the selector that defines Button is\n.button, .toggle-button, .radio-button \u0026gt; .radio, .check-box \u0026gt; .box, .menu-button, .choice-box, .color-picker.split-button \u0026gt; .color-picker-label, .combo-box-base, .combo-box-base:editable \u0026gt; .arrow-button { . . . } This means that if you want to change the styling of Button you\u0026rsquo;re going to have to decide if you really just mean Button or everything that is \u0026ldquo;button-like\u0026rdquo;. Otherwise, your application could take on an inconsistent look.\nCompound Nodes # There are a large number of Node classes that are actually composed of multiple internal Nodes. Each of these internal components can have their own CSS selectors and be styled individually.\nMany times you can spot these in Modena. Here\u0026rsquo;s CheckBox:\n.check-box { -fx-label-padding: 0.0em 0.0em 0.0em 0.416667em; /* 0 0 0 5 */ -fx-text-fill: -fx-text-background-color; } .check-box \u0026gt; .box { -fx-background-radius: 3, 2, 1; -fx-padding: 0.166667em 0.166667em 0.25em 0.25em; /* 2 2 3 3 */ } .check-box \u0026gt; .box \u0026gt; .mark { -fx-background-color: null; -fx-padding: 0.416667em 0.416667em 0.5em 0.5em; /* 5 5 6 6 */ -fx-shape: \u0026#34;M-0.25,6.083c0.843-0.758,4.583,4.833,5.75,4.833S14.5-1.5,15.917-0.917c1.292,0.532-8.75,17.083-10.5,17.083C3,16.167-1.083,6.833-0.25,6.083z\u0026#34;; } But how do we know that this is all of the internal components that might be styled? Just as there\u0026rsquo;s no requirement that all of the Pseudo-classes for each Node are styled in Modena, it\u0026rsquo;s possible that internal components are defined that are just left at their default styling.\nTo find out definitively, we will need to check the source code for the Node. Let\u0026rsquo;s do that for CheckBox\u0026hellip;\nIf we put a call to CheckBox() in an application, then we can click through and see the decompiled code for CheckBox. From there we can do a search for \u0026ldquo;getStyleClass\u0026rdquo;, which has to be called to add a CSS selector to the class.\nprivate void initialize() { this.getStyleClass().setAll(new String[]{\u0026#34;check-box\u0026#34;}); this.setAccessibleRole(AccessibleRole.CHECK_BOX); this.setAlignment(Pos.CENTER_LEFT); this.setMnemonicParsing(true); this.pseudoClassStateChanged(PSEUDO_CLASS_DETERMINATE, true); } The first thing we can see is that all of the StyleClasses that might have been inherited from any superclasses are cleaned out via the setAll() method. And the CheckBox overall is assigned the selector of check-box. There are no other calls to getStyleClass() in the class. This means that any other CSS selectors have to be assigned in the skin.\nWe need to find where the skin is created. Searching in the code for \u0026ldquo;skin\u0026rdquo; gives this:\nprotected Skin\u0026lt;?\u0026gt; createDefaultSkin() { return new CheckBoxSkin(this); } If we click-through into this we\u0026rsquo;ll come to the constructor for CheckBoxSkin which gives is this:\npublic CheckBoxSkin(CheckBox var1) { super(var1); this.behavior = new ButtonBehavior(var1); this.box.getStyleClass().setAll(new String[]{\u0026#34;box\u0026#34;}); this.innerbox = new StackPane(); this.innerbox.getStyleClass().setAll(new String[]{\u0026#34;mark\u0026#34;}); this.innerbox.setNodeOrientation(NodeOrientation.LEFT_TO_RIGHT); this.box.getChildren().add(this.innerbox); this.updateChildren(); } And a search for \u0026ldquo;getStyleClass\u0026rdquo; only yields two hits, which are both in this constructor method. So the only selectors are the ones that we saw used in Modena at the top of this section. This means that we can completely style CheckBox with these three selectors.\nWe can also now find out two very valuable pieces of information - the types of box and mark. This is important because this will tell us what styling attributes are available to us to customize.\nRight above that constructor are these two lines:\nprivate final StackPane box = new StackPane(); private StackPane innerbox; Now we know that both of these are StackPanes. We could have guessed that they were some kind of Region from the CSS in Modena, but now we know exactly what they are.\nExploring Via Node.lookupAll() # If looking through the source code seems daunting, there is another method you can try. Node has a method called lookupAll() which will accept wildcard characters. This means that you can call Node.lookupAll(\u0026quot;*\u0026quot;) to find all of the styleclass selectors that are present in a compound Control. It works like this:\nclass ModenaExample0 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent()) stage.show() } private fun createContent(): Region = BorderPane().apply { val checkBox = CheckBox().apply { styleClass += \u0026#34;fred\u0026#34; } center = HBox(10.0).apply { children += Button(\u0026#34;Button\u0026#34;).apply { isDefaultButton = false onAction = EventHandler { checkBox.lookupAll(\u0026#34;*\u0026#34;).forEach { println(\u0026#34;Id: ${it.id}\\tStyleClass: ${it.styleClass}\\t\\tType: ${it::class.java.simpleName}\u0026#34;) } } } children += checkBox } padding = Insets(30.0) } } fun main() = Application.launch(ModenaExample0::class.java) You need to run Node.lookupAll() after the SceneGraph has been displayed. That\u0026rsquo;s why it has been implemented inside the onAction EventHandler for a Button. When you click on the Button, this is what you get:\nId: null StyleClass: check-box fred Type: CheckBox Id: null StyleClass: text Type: LabeledText Id: null StyleClass: box Type: StackPane Id: null StyleClass: mark Type: StackPane We see check-box, box and mark, as well as fred that I explicitly added to the CheckBox to see how it would show in the listing. We get to see all of the selectors added to each Node, which is good.\nBut what\u0026rsquo;s this text??? We can see that it\u0026rsquo;s a LabeledText, but where did it come from?\nIf we go back to the skin of ComboBox, we see that it extends LabeledSkinBase. If we look at LabeledSkinBase and search for usages of LabeledText we find it right away at the top of the class:\npublic abstract class LabeledSkinBase\u0026lt;C extends Labeled\u0026gt; extends SkinBase\u0026lt;C\u0026gt; { LabeledText text; . . . } But you won\u0026rsquo;t find any place where it is assigned a styleclass selector. However, if you look at the source code for LabeledText you will find it assigns itself the selector text, and this just stays when it\u0026rsquo;s part of CheckBoxSkin.\nIf you want to know everything that you might style in a JavaFX Control, especially one with a skin, then it looks like the best place to start is with a test program like this that uses Node.lookupAll(\u0026quot;*\u0026quot;) to find all of the components that stylesheet selectors. However, this won\u0026rsquo;t show you how the various components are related to each other, and how they are used. For that, you\u0026rsquo;ll have to look at the source code.\nAll that being said, for CheckBox the elements are pretty clear. The text element is obviously the text beside the checkbox, and the mark is the checkmark that goes inside the checkbox. Other skinned Controls could be much more complicated.\nCustomizing Modena Selectors # I see lots and lots of example programs, especially when people are asking questions on StackOverflow, where they\u0026rsquo;ll employ a custom style sheet that uses one of the standard Node selectors. Like button or check-box. This is fine in a small sample program, but isn\u0026rsquo;t necessarily the best approach with a bigger application with multiple screens.\nThere\u0026rsquo;s two potential issues with this.\nThe first is that you are changing the styling of every Node of that type, everywhere. Is this really what you want to do? OK, the Modena styling isn\u0026rsquo;t exactly exciting, but it is consistent and it does look professional. Change the styling of one element without thinking about how it will integrate everywhere with everything else might look a bit odd.\nThe second is that many of the stylings work together. If you go back and look at the styling for button, you can see that it is shared with 9 other selectors, some of which are enclosed in other Nodes. Do you want to just change Button? Or should you change all of selectors that share that same styling? I rarely see people do that. Additionally, you should also look for places that depend on the original styling, like Pseudo-classes and enclosed Nodes.\nEssentially, you are coupling your styling change all over your application. That\u0026rsquo;s a good thing when you want to restyle the whole application to give it an entirely different feel, but it can be a problem if you\u0026rsquo;ve made a change for a specific use case and it impacts seemingly unrelated parts of your GUI.\nThe alternative is to create a new selector and then perform styleClass += \u0026quot;{new selector}\u0026quot; on the Node. Now you are just styling a particular instance of that Node type, and you won\u0026rsquo;t have any global issues. If you are adding this new styleclass selector to a whole bunch of instances of the Node then create a builder method to instantiate the Node with the selector added.\nConclusion # My biggest complaint about the JavaFX documentation is that there is no \u0026ldquo;Styling Guide\u0026rdquo; that details all of the styleclass selectors and nested components of the standard JavaFX Nodes. It would make life so much easier to be able to simply look up the documentation for a skin class and see what they\u0026rsquo;ve put there for you to customize. Instead, you\u0026rsquo;ll have to pour over the source code, search through Modena and figure out how make it work.\nHowever, once you\u0026rsquo;re comfortable with the techniques used in Modena it is fairly easy to understand how the styling that you see on screen (for most Nodes) is achieved through the stylesheet. Hopefully, this article has given you the tools to understand these techniques.\n","date":"20 November 2024","externalUrl":null,"permalink":"/javafx/elements/modena/","section":"JavaFX","summary":"If you are going to do any custom styling in JavaFX, you need to understand at least the basics about the Modena stylesheet and how it works with the standard JavaFX Nodes.","title":"Dealing With Modena","type":"javafx"},{"content":" Introduction # In Part 1 of this series, we looked at the Observable classes designed to wrap aribitrary Object values. In this article we are going to move on to the typed Observable classes and interfaces. We\u0026rsquo;ll look at how they are different from the generic types, and how to deal with compatibility issues between the two.\nIf you haven\u0026rsquo;t read Part 1 of this series, you should probably go and do that now, as the information in this article will make more sense to you.\nThe Typed Classes and Interfaces # There are 6 sets of typed Observable classes and interfaces. The each deal with one of the following types:\nString Boolean Integer Double Long Float We don\u0026rsquo;t need to look at all of them because they break down into two groups: The numeric types, and the non-numeric types. We will look at just one type from each group, String for the non-numeric and Integer for the numeric. The non-numeric is simpler, so we\u0026rsquo;ll start with that:\nString Observables # The String types are a little bit more interesting to look at than Boolean because the Fluent API library for String is larger. So we\u0026rsquo;ll use String as our example type for a deep dive. Here\u0026rsquo;s the chart:\nHere you can see that all of the generic \u0026lt;T\u0026gt;\u0026rsquo;s have been replaced with \u0026lt;String\u0026gt;. Understand, though, that there is no interface called ObservableValue\u0026lt;String\u0026gt;, it\u0026rsquo;s just ObservableValue\u0026lt;T\u0026gt; with the \u0026lt;T\u0026gt; resolved as a \u0026lt;String\u0026gt;. The same holds true for all of the other entries on the chart defined with \u0026lt;String\u0026gt;.\nThe Interfaces # We\u0026rsquo;ll start with the interfaces. Some of the descriptions are a little bit stripped down from what you\u0026rsquo;ll see in Part 1 . I want this article to be complete within itself, but I don\u0026rsquo;t want to repeat a lot of information that you can find in Part 1 .\nObservable \u0026amp; ObservableValue\u0026lt;String\u0026gt; # At the top of the chart, you\u0026rsquo;ll find the interface, Observable. You\u0026rsquo;ll notice that it is not typed at all, it\u0026rsquo;s not even a generic. That\u0026rsquo;s because all of its methods do not deal with data.\nThis is the root for all of the other classes and interfaces, but it\u0026rsquo;s really quite simple and only defines three methods: addListener(), removeListener() and subscribe(). These are all related to the process called \u0026ldquo;Invalidation\u0026rdquo;.\nThe next interface down is ObservableValue\u0026lt;String\u0026gt;, which extends Observable. The key method in this interface is ObservableValue.getValue(), which allows us to actually read the value and will re-validate the Observable.\nNow that we can read the value we can have the methods related to ChangeListeners, including those related to Subscriptions. The other methods it adds are the map() and flatMap() methods used for transforming an ObservableValue into a different ObservableValue.\nThere is also the ObservableObjectValue\u0026lt;String\u0026gt; interface, which introduces the get() method, which at this point is effectively identical to getValue().\nObservableStringValue # ObservableStringValue adds no new methods and simply extends ObservableObjectValue\u0026lt;String\u0026gt;. Essentially, you can use these two interfaces interchangeably.\nHowever, if you are writing code that deals with the specific String Observables using ObservableStringValue might give a more consistent feel to the code.\nReadOnlyProperty\u0026lt;String\u0026gt; \u0026amp; WritableValue\u0026lt;String\u0026gt; # These are the two parent interfaces for all of the Property classes.\nReadOnlyProperty\u0026lt;String\u0026gt; simply introduces the getBean() and getName() methods. These are presumably meaningful methods if you are serializing your Properties.\nThe interface WritableValue\u0026lt;String\u0026gt; gives us the setValue() method. Now we can actually put something into an Observable!\nWritableObjectValue\u0026lt;String\u0026gt; and WritableStringValue # WritabaleStringValue is an extension of WritableObjectValue\u0026lt;String\u0026gt; that adds no methods. So you can treat it as a synonym for WritableObjectValue\u0026lt;String\u0026gt;.\nThese two classes add the set() method, which, for String types is identical to setValue().\nProperty\u0026lt;String\u0026gt; # The Property interface extends the previous two interfaces and adds the ability to bind to other observables. We get bind() and bindBidirectional(), and their corresponding unbinding methods. Also there is the isBound() method that will tell us if the Property has been bound to something.\nBinding\u0026lt;String\u0026gt; # Binding\u0026lt;String\u0026gt; is the last interface on the chart. It extendsObservableValue\u0026lt;String\u0026gt; adding a method to force the Binding to invalidate, a method to check if the Binding is valid and a method to get the dependencies of the Binding.\nThe Classes # Everything else on the chart is a class, let\u0026rsquo;s take a look at them.\nStringExpression # This is the root class for all of the Property\u0026lt;String\u0026gt; classes, even though the diagram goes diagonally down to ObjectProperty\u0026lt;String\u0026gt;. Note that StringExpression is a drop-in replacement for ObjectExpression from Part I and does not extend or implement ObjectExpression\nThis is the only class in the chart that introduces new concrete methods that don\u0026rsquo;t implement methods already defined by any of the interfaces in the chart.\nNote StringExpression is where this chart really diverges from the generic chart. Every class that inherits from StringExpression is fundamentally different from the generic equivalent.\nThis class provides the core functionality for the \u0026ldquo;Fluent API\u0026rdquo; for creating String bindings. StringExpression is an abstract class that implements a number of String related operations such as concat() and length() and isEmpty() in an Observable fashion. We also have methods for equality, inequality, greater than, less than, greater than or equal, less than or equal, and checking for a Null value, each of which creates a BooleanBinding.\nThere\u0026rsquo;s also a method called getValueSafe(). It returns a String and will return an empty String if the value in the StringExpression is null.\nIt is StringExpression that lets you do things like this:\nvar label = Label() label.textProperty().bind(model.lastNameProperty().concat(\u0026#34;, \u0026#34;).concat(model.firstNameProperty())) This inclusion of StringExpression in the class hierarchy is what makes ObjectProperty\u0026lt;String\u0026gt; different from StringProperty. The code snippet above assumes that model.lastNameProperty() is a StringProperty, not an ObjectProperty\u0026lt;String\u0026gt;. Because the latter wouldn\u0026rsquo;t work because ObjectExpression (which is the base class for ObjectProperty does not define concat().\nReadOnlyStringProperty # This class extends StringExpression and implements ReadOnlyProperty. This class can also be thought of as very much like ReadOnlyProperty but with the ability to use the Fluent API with it to create bindings.\nStringProperty # The best way to think about StringProperty is an implementation of Property\u0026lt;String\u0026gt; but, since it inherits from StringExpression through ReadOnlyStringProperty, also has the methods for creating bindings via the Fluent API. However, it only has implementations for bidirectional binding and setting the value.\nThis is, however, a great class to declare your instantiated Properties as. Like this:\nval someProperty : StringProperty = SimpleStringProperty(\u0026#34;abc\u0026#34;) StringPropertyBase # This class adds almost all the remaining methods defined in the various interfaces up hierarchy. We get methods to bind and unbind, add and remove Listeners and a get() method.\nThe methods that are missing are the two silly Java Bean related methods. This is the class that you probably want to extend from if you want to create your own StringProperty classes, especially if you want to ignore Java Bean stuff as much you can.\nSimpleStringProperty # This is the only class in the chart that you can directly instantiate, so everybody is familiar with it. It adds the getBean() and getName() methods and the infrastructure and constructors to set their values.\nReadOnlyStringPropertyBase and ReadOnlyStringWrapper # ReadOnlyStringPropertyBase is very similar to StringPropertyBase in that it is an abstract class that has almost all of the interface methods implemented except for the \u0026ldquo;Bean\u0026rdquo; methods. It\u0026rsquo;s also missing the get() method.\nThe only \u0026ldquo;only out of the box\u0026rdquo; implementation of ReadOnlyStringPropertyBase is contained in ReadOnlyStringWrapper. This is a fairly involved topic that was covered in depth here in Part I.\nStringBinding # This is the sole class on the Binding side of the chart, and it\u0026rsquo;s abstract and it extends StringExpression. Any utility (including the Fluent API) that creates String Bindings will do so by extending from this class.\nIt doesn\u0026rsquo;t add any new public methods but there are a few protected methods that are the framework for custom Binding classes. We get bind() and unbind() to start with. We also get allowValidation(), onInvalidating() and isObserved(). There is one abstract method, and that\u0026rsquo;s computeValue() which is also protected.\nThe intention is clear about the standard use case for custom classes extended from StringBinding. It\u0026rsquo;s that the Observables to be bound are defined when the class is instantiated and that you\u0026rsquo;ll also define a computeValue() method that will use those bound values to determine the return value of any calls to get() or getValue().\nTip Note that computeValue() is only called when a call is made to get() or getValue() when the Binding has been invalidated.\nSince StringBinding extends from StringExpression you can use the Fluent API to modify the results or combine it with other StringExpressions.\nCompatibility Issues # The main difference between the entries on this chart and the one for the generic Observables is that ObjectExpression has been swapped out for StringExpression. These two classes have different methods, which will create some type incompatibilities.\nHowever, it turns out that you can fairly freely mix and match StringProperty and ObjectProperty\u0026lt;String\u0026gt; in bindings without a lot of problems.\nThis is mostly because the bind() method in ObjectPropertyBase\u0026lt;String\u0026gt; takes an ObservableValue\u0026lt;? extends String\u0026gt;, while StringPropertyBase.bind() also takes ObservableValue\u0026lt;? extends String\u0026gt;. And of course, both classes implement ObservableValue\u0026lt;String\u0026gt;.\nYou can even put a ObjectProperty\u0026lt;String\u0026gt; as a parameter in the Fluent API methods and it will work without a hitch. Generally speaking, the Fluent API methods will accept primitive classes as well as Observable classes, but obviously won\u0026rsquo;t trigger an invalidation if a primitive value changes. Internally, a dependency is created if the parameter is an instance of ObservableValue. Which of course, ObjectProperty\u0026lt;String\u0026gt; is.\nHowever, ObjectProperty\u0026lt;String\u0026gt; and StringProperty are not type compatible as method parameter declarations. Unless you are planning on using the Fluent API do not create your method parameters as StringProperty. The same goes for ObjectProperty\u0026lt;String\u0026gt; although you\u0026rsquo;re probably not going to be using the methods from ObjectExpression over StringExpression. Specify your parameters as Property\u0026lt;String\u0026gt; and it will accept both ObjectProperty\u0026lt;String\u0026gt; and StringProperty.\nNumber Types # This is the most confusing part of the entire discussion. JavaFX utilizes the standard Java class called Number to create a framework for all of the numeric Observables. I\u0026rsquo;m not sure what this adds, other than a layer of complexity, but it\u0026rsquo;s a layer that you can\u0026rsquo;t ignore.\nNumber is an abstract class that is the super-class of all of the \u0026ldquo;boxed\u0026rdquo; number types in Java. For intstance, Integer (as opposed to int) is a subclass of Number. Number defines a set of methods that deal with conversion between the various subtypes of Number.\nThe Number types of Observables share many of the same differences from the generic Observables as the String types do. So in this section we\u0026rsquo;re going to concentrate on the extra complexity that comes from using Number for all of these variants.\nAll four of the numeric types work the same way. Let\u0026rsquo;s look at the chart for Integer:\nThe Generically Defined Types # The generic classes in this chart all resolve to \u0026lt;Number\u0026gt;, not \u0026lt;Integer\u0026gt;. So IntegerProperty implements Property\u0026lt;Number\u0026gt;, not Property\u0026lt;Integer\u0026gt;. This means that if you have an instance of ObjectProperty\u0026lt;Integer\u0026gt; it is not type compatible with IntegerProperty. This can be a source of frustration.\nObservableNumberValue \u0026amp; ObservableIntegerValue # The ObservableNumberValue interface extends ObservableValue\u0026lt;Number\u0026gt;, which gives it the Listener, mapping and Subscription methods. Its getValue() method returns Number.\nAdditionally, it has type specific getValue() equivalents: doubleValue(), floatValue(), intValue() and longValue().\nObservableIntegerValue extends ObservableNumberValue() and adds one method, get(). However ObservableIntegerValue.get() returns int not Number.\nThis is very significant.\nThis is the first example that we\u0026rsquo;ve seen where get() returns a different value from getValue(). This suggests to me, that when you are retrieving the value from a numeric Observable, you can largely ignore their \u0026lt;Number\u0026gt; nature if you just use get() all the time instead getValue().\nAlso, ObservableBooleanValue.get() returns boolean instead of Boolean returned from ObseravbleBooleanValue.getValue(). It\u0026rsquo;s a smaller change, but still a difference.\nWritableNumberValue \u0026amp; WritableIntegerValue # WritableNumberValue is what the JavaDocs describe as a \u0026ldquo;tagging interface\u0026rdquo;. It has no methods defined, and simply appears to be a common ancestor for the numeric Writable{Type}Value interfaces.\nWritableIntegerValue is a parallel to ObservableIntegerValue but for writing. We have a difference between the two setters, as WritableIntegerValue.set() takes an int, while the setValue() method takes Number.\nNumberExpression # Unlike ObjectExpression and StringExpression, NumberExpression is an interface, not a class. It defines methods for the four basic math operations - add, subtract, multiply and divide - and these operations all return a NumberBinding. Additionally, it defines a number of comparison methods, all of which return BooleanBinding. There\u0026rsquo;s also asString() which returns a StringBinding.\nFor the math operations, each method is overloaded to take 5 possible parameter types. The first four are non-Obseravble number classes: double, int,long and float. The fifth is for ObservableNumberValue. Regardless of the parameter type, you always get a NumberBinding (which we haven\u0026rsquo;t seen yet) in return.\nNumberExpressionBase # This is our first class on the binding side of the chart, and this is abstract and partially implements NumberExpression.\nThis class has concrete implementations of all of the comparison methods, and for the numeric operation methods taking ObservableNumberValue as a parameter. In other words, you\u0026rsquo;ll find add(ObservableNumberValue other), but not add(int other), or add(double other).\nIntegerExpression # Another abstract class, IntegerExpression supplies all of the concrete implementations of the methods defined in NumberExpression that are not implemented in NumberExpressionBase. However, it returns values that match the type of the result of the operation.\nThis means that IntegerExpression.add(int other) will return a IntegerBinding instead of a NumberBinding.\nAlso, this class has asObject() which returns a linked ObjectExpression\u0026lt;Integer\u0026gt;. More on this later.\nNumberBinding \u0026amp; IntegerBinding # NumberBinding is another \u0026ldquo;tagging\u0026rdquo; interface. It has no methods, but extends Binding\u0026lt;Number\u0026gt; and NumberExpression.\nIntegerBinding, just like StringBinding is an abstract class that provides the starting point for any custom Bindings that you want to write, but for Integer values. Note though, that it implements Binding\u0026lt;Number\u0026gt; and ObservableValue\u0026lt;Number\u0026gt;, not Binding\u0026lt;Integer\u0026gt; and ObservableValue\u0026lt;Integer\u0026gt;.\nUsing this Information # At this point you should have a handle on when and how to use both the generic and the typed Observable classes. What are the some of the factors you need to keep in mind when using both the generic and the typed Obsevables?\nConverting Between Generic and Typed Properties # As we\u0026rsquo;ve seen IntegerProperty implements Property\u0026lt;Number\u0026gt;, not Property\u0026lt;Integer\u0026gt;. This means that it also implements ObservableValue\u0026lt;Number\u0026gt;.\nAnd that means that you cannot pass IntegerProperty as a parameter when Property\u0026lt;Integer\u0026gt; or ObservableValue\u0026lt;Integer\u0026gt; is required.\nThis quickly becomes frustrating.\nFortunately all of the Property\u0026lt;Number\u0026gt; implementations have a function called asObject(). For IntegerProperty this will return you a Property\u0026lt;Integer\u0026gt; that is bidirectionally bound to your IntegerProperty. It\u0026rsquo;s not the same object cast differently, but an actual new object that\u0026rsquo;s bidirectionally bound to the original.\nAll of these typed classes also have a static method that does the reverse. For instance, DoubleProperty has a method called doubleProperty(). You pass it a Property\u0026lt;Double\u0026gt; and it gives you back a new DoubleProperty that is bidirectionally bound to the original Property\u0026lt;Double\u0026gt;. This goes for StringExpression and BooleanExpression\nKnowing about this makes any issues about whether or not to use typed or generic Properties a lot less critical. You can very easily convert from one to the other when required.\nFluent API # The Fluent API is delivered via the {Type}Expression classes, and is the main motivation to use typed Observable classes over the generic ones.\nSince the introduction of ObservableValue.map() in JFX 19, a lot of the utility in the Fluent API is a bit less compelling, since map() is simpler to use because the Function passed to it is just Java (or Kotlin) code. What ObservableValue.map() cannot do, however, is to combine multiple Observables together as dependencies.\nI find the Fluent API to be fine when the transformations are fairly simple. But when the operations start to get long and involved, then the Fluent API tends to become less clear. And if it\u0026rsquo;s less clear, then it\u0026rsquo;s more likely to be the source of a bug or cause headaches in maintenance. In those cases, it can be better to use a static methods from the Bindings class, or to create a Binding by extending one of the {Type}Binding classes.\nIn my opinion, then, the use case for the Fluent API is fairly narrow: Simple transformations involving two or more Observables.\nNot that this is uncommon. Use cases like this happen all the time:\nval fullName : ObservableValue\u0026lt;String\u0026gt; = lastNameProperty.concat(\u0026#34;, \u0026#34;).concat(firstNameProperty) It\u0026rsquo;s hard to think of an easier, cleaner way to do this. However\u0026hellip;\nval nameString : ObservableValue\u0026lt;String\u0026gt; = lastNameProperty.map{\u0026#34;The last name is $it\u0026#34;} Cannot be performed via the Fluent API alone and you would have to otherwise use the Bindings library:\nval nameString : ObservableValue\u0026lt;String\u0026gt; = Bindings.concat(\u0026#34;The last name is \u0026#34;).concat(lastNameProperty) Note that the first concat() is from Bindings and the second is from StringExpression.\nYou can combine ObservableValue.map() with the Fluent API:\nval nameString : ObservableValue\u0026lt;String\u0026gt; = lastNameProperty.concat(\u0026#34;, \u0026#34;).concat(firstNameProperty).map{\u0026#34;The full name is $it\u0026#34;} But the map() call has to come at the end, because it returs ObservableValue\u0026lt;String\u0026gt; which doesn\u0026rsquo;t support the Fluent API. If you really did want to do it the other way:\nval nameString : ObservableValue\u0026lt;String\u0026gt; = StringExpression.stringExpression(lastNameProperty.map{\u0026#34;The full name is $it,\u0026#34;}).concat(firstNameProperty) But, honestly, that seems like a lot of work.\nMethod Overloading # In Java, you cannot overload methods where the signatures only differ by the types of their enclosed generics. For instance this code will fail to compile:\nfun doSomething(param1 : Property\u0026lt;String\u0026gt;) { . . } fun doSomething(param1: Property\u0026lt;Int\u0026gt;) { . . } You get an error that says something about \u0026ldquo;erasure\u0026rdquo; and non-unique signatures. What you can do instead is this:\nfun doSomething(param1 : StringProperty) { . . } fun doSomething(param1: IntegerProperty) { . . } This will work just fine. This is the second reason that you might want to use the typed Observable classes.\nDealing With Node Properties # You should be aware that virtually all of the JavaFX Nodes expose their Properties as typed Properties whenever possible. For instance, Region.widthProperty() will return a ReadOnlyDoubleProperty while, Region.paddingProperty() will return ObjectProperty\u0026lt;Insets\u0026gt;.\nThis makes sense, since, as a general purpose API, they would want to provide the most \u0026ldquo;out of the box\u0026rdquo; functionality, and returning Properties that can be used in the Fluent API without any conversion seems like the way to go. You\u0026rsquo;ll have to decide for yourself if this would influence the way that you declare your own Properties.\nThere are a few places in the JavaFX API where it\u0026rsquo;s hard to avoid understanding some of the nuances about the typed classes versus the generic classes. For instance, TextFormatter\u0026lt;Integer\u0026gt; has a method valueProperty() that returns ObjectProperty\u0026lt;Integer\u0026gt;, which is going to give you issues if you try to bidirectionally bind it with an IntegerProperty in your Model.\nWhen to Use ReadOnly{Type}Property # If you look at the JavaDocs for Region you\u0026rsquo;ll see that Region.widthProperty() returns ReadOnlyDoubleProperty, because they are using ReadOnlyIntegerWrapper internally and want widthProperty() to return a truly \u0026ldquo;read only\u0026rdquo; object. This pattern is repeated frequently through the JavaFX library.\nI think that most programmers are used to dealing with ReadOnly{Type}Property as return values from the JavaFX API, and probably don\u0026rsquo;t even know about {Type}Expression, even though these two class categories differ only in support for the Java Bean methods.\nAt the end of the day, ReadOnlyDoubleProperty does no harm. Yes, you\u0026rsquo;re giving/taking more than you need to, but that extra functionality is trivial and meaningless, so there\u0026rsquo;s really no added coupling to worry about. Nobody is ever going to call those two extra methods.\nSo, go with the flow and just use ReadOnly{Type}Property when you need something bind-able through the Fluent API\nAvoid Calling getValue() # Unless you want Number (and who does?), there\u0026rsquo;s no advantage to calling getValue() on any Observable object. Just use get() instead and you can largely ignore the differences between typed numeric Obsevables and the object versions.\nConclusion # When I started out in JavaFX I used IntegerProperty, DoubleProperty, StringProperty and BooleanProperty virtually exclusively. I never thought much about ObjectProperty\u0026lt;T\u0026gt; at all. Eventually I became aware of ObjectProperty\u0026lt;T\u0026gt; and it wasn\u0026rsquo;t clear to me how it was any better or worse than the typed Properties.\nAt some point I considered the typed Properties to be mostly convenience classes, that just made it a bit easier to declare stuff without all those angle brackets. Later, I started to encounter occasional issues stemming from IntegerProperty implementing Property\u0026lt;Number\u0026gt; and not Property\u0026lt;Integer\u0026gt;. At that point I started to view these as inconvenience classes. I never had the same issues with StringProperty or BooleanProperty, though.\nI\u0026rsquo;m not a big user of the Fluent API, and I went through a period when I didn\u0026rsquo;t really use the typed Properties at all. But there are still those times when the API forces you to contend with the typed Properties as most of the Node Properties are defined this way.\nHowever, through years of learning more about this, I\u0026rsquo;ve always used StringProperty and BooleanProperty over the generic Properties. This is because you don\u0026rsquo;t ever the encounter the issues created by Property\u0026lt;Number\u0026gt;.\nPresently I lean towards just using the typed Properties unless there is a good reason not to. Here\u0026rsquo;s why:\nPeople are used to them, even if they don\u0026rsquo;t understand them. The standard JavaFX Nodes are going to give them to you. 99% of the time, it just doesn\u0026rsquo;t matter\nYou\u0026rsquo;ll probably end up binding a DoubleProperty to a DoubleProperty anyway. It\u0026rsquo;s trivial to convert when you need it. The biggest remaining issue is defining parameters for your methods. If you just need an Observable value to bind to something else without using the Fluent API, then you don\u0026rsquo;t need anything more than ObseravbleValue\u0026lt;T\u0026gt;. If you define the parameter as ObservableValue\u0026lt;Double\u0026gt; then it won\u0026rsquo;t accept DoubleProperty. Can you get away with ObservableValue\u0026lt;Number\u0026gt;? If you can, then client code can pass you DoubleProperty. But if you really, really to need deal with it as ObservableValue\u0026lt;Double\u0026gt;, then the client code can pass you DoupbleProperty.asObject().\n","date":"22 August 2024","externalUrl":null,"permalink":"/javafx/elements/observables/properties/observable-classes/observable-classes-typed/","section":"JavaFX","summary":"This article is about the all of the typed observable classes and interfaces except the ones concerned with List.\nYou’ll find that all of the relationships and structure found in the generic classes is just about exactly the same, but then there are a few extra classes and interfaces added to the hierarchy depending on the type of data that they hold. We’ll concentrate mostly on two types, String and Integer here, because dealing with the others would just be redundant. String is typical of stand-alone types, and Integer is typical of types that are included in Number.\n","title":"Guide To the Observable Classes - Part II","type":"javafx"},{"content":" The Articles: # ","date":"20 July 2024","externalUrl":null,"permalink":"/javafx/elements/observables/bindings/","section":"JavaFX","summary":"If you look at the JavaDocs for the interface, Property you’ll find that all of its methods are only concerned with binding. That’s how important Bindings are to JavaFX.\nBinding is a class, but it’s also an action. Bindings are how you connect two or more Properties together, so that you can have actual data stored inside a single place - a Property - and use it all over your application without having to write code to update all those places whenever it changes.\nSince the JavaFX screen Nodes are just chock full of Properties, this means that it’s easy to connect and control your GUI from Properties that you create in your Presentation Model.\n","title":"Bindings","type":"javafx"},{"content":" Introduction # Since the JavaFX GUI elements are not thread-safe, virtually all of the JavaFX methods are intended to run on a single thread called the \u0026ldquo;FX Application Thread\u0026rdquo; or \u0026ldquo;FXAT\u0026rdquo;. This includes any code that you write that runs under an EventHandler or a Listener, as well as the internal routines that control how JavaFX works.\nWhat this means is that if you write any code that runs on the FXAT that takes a long time to execute or blocks, then you\u0026rsquo;ll end up \u0026ldquo;hanging\u0026rdquo; your GUI. JavaFX provides a mechanism to run long or blocking code on a background thread, including a way to get back onto the FXAT when the job completes and you want to update your GUI.\nThat mechanism is called Task. In this article, we are going to look at how Task provides a facility to monitor the progress of a job from the FXAT and how to expand that facility to provide custom data updates.\nAbout Task # Task is a special JavaFX class for background jobs that implements Runnable. This means that it can be loaded into a Thread via new Thread(task), which allows it to be run on a background thread. You can read the basics about using Task here .\nAdditionally, Task implements the Worker interface, which allows the status of a running Task to be fully observable. This is useful because you can now bind these Properties of Task to elements of your GUI to keep your users informed of the progress of the Task.\nIn the usual course of using Task you would instantiate it as an anonymous inner class, override the Task.call() method, supply an EventHandler for when it completes, and pass it to a Thread through its constructor. Then you call Thread.start() and it rolls along from there. Whatever you put in the Task.call() method is the code that you want to run it the background thread, and whatever you put in the completion EventHandler will execute on the FXAT.\nAs expected, the introduction in JavaDocs for Task is jumbled, confusing mess. Everything you need to know is there, but meaningless unless you already know what you need to know. Once again, you should read my article about the FXAT and Task if you need help getting started with Task.\nMonitoring Progress # The standard use case for progress monitoring of a Task is presented as a fraction of the total work. Task provides for this with a read-only Property called progress. You can update progress through two version of a method called updateProgress(). In both of these, you provide the amount of work completed and the total amount of work to be done and Task converts this into a single Double between 0.0 and 1.0 which it stores in progress. These methods also update two other properties, totalWork and workDone which store the amounts given as Double.\nI\u0026rsquo;m just going to give a slightly modified version of the example from the JavaDocs here:\nTask\u0026lt;Integer\u0026gt; task = new Task\u0026lt;Integer\u0026gt;() { @Override protected Integer call() throws Exception { int iterations; for (iterations = 0; iterations \u0026lt; 10000000; iterations++) { updateProgress(iterations, 10000000); } return iterations; } }; In a similar manner, there is also a read-only StringProperty called message. You can update it via updateMessage(). These 4 Properties (progress, message, totalWork and workDone) can be bound to elements of your GUI to keep the user informed of how the Task is progressing. Let\u0026rsquo;s look at the simplest implementation of something like that, using a ProgressIndicator:\nclass TaskProgress0App : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 300.0, 300.0) stage.title = \u0026#34;Task Progress Example 0\u0026#34; stage.show() } private fun createContent(): Region = BorderPane().apply { val task = object : Task\u0026lt;Unit\u0026gt;() { init { updateMessage(\u0026#34;Not Started\u0026#34;) } override fun call() { updateProgress(0,100) updateMessage(\u0026#34;Initializing\u0026#34;) Thread.sleep(Duration.ofMillis(3000)) updateProgress(30, 100) updateMessage(\u0026#34;Processing\u0026#34;) for (x in 31..90) { updateProgress(x.toLong(), 100) Thread.sleep(52.toLong()) } updateMessage(\u0026#34;Verifying\u0026#34;) Thread.sleep(2000.toLong()) updateProgress(100, 100) updateMessage(\u0026#34;Completed\u0026#34;) } } center = VBox(10.0).apply { children += Label().apply { textProperty().bind(task.messageProperty()) } children += ProgressBar().apply { progressProperty().bind(task.progressProperty()) minWidth = 200.0 } alignment = Pos.CENTER } bottom = HBox(Button(\u0026#34;Run Task\u0026#34;).apply { setOnAction { Thread(task).start() } }).apply { alignment = Pos.CENTER padding = Insets(20.0) } } } fun main() { Application.launch(TaskProgress0App::class.java) } It looks like this at first:\nAnd then, when it\u0026rsquo;s in the \u0026ldquo;Processing\u0026rdquo; loop:\nAnd, finally, like this:\nThese Update Methods Have \u0026ldquo;Protected\u0026rdquo; Scope # What if you don\u0026rsquo;t want to have all of the processing happen inside Task.call()? For instance, if you are using a framework like MVCI and you want to put the business logic where it belongs, in the Interactor?\nWell, then you\u0026rsquo;re going to have a problem because Task.updateProgress(), and Task.updateMessage() have protected scope, meaning that they can only be called from inside your Task object. To get around this, you\u0026rsquo;ll need to provide some functional elements to do the updates, like this:\nclass TaskProgress2App : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 300.0, 300.0) stage.title = \u0026#34;Task Progress Example 2\u0026#34; stage.show() } private fun createContent(): Region = BorderPane().apply { val task = object : Task\u0026lt;Unit\u0026gt;() { init { updateMessage(\u0026#34;Not Started\u0026#34;) } override fun call() { handleProcessing({ done, total -\u0026gt; updateProgress(done, total) }, { updateMessage(it) }) } } center = VBox(10.0).apply { children += Label().apply { textProperty().bind(task.messageProperty()) } children += ProgressBar().apply { progressProperty().bind(task.progressProperty()) minWidth = 200.0 } alignment = Pos.CENTER } bottom = HBox(Button(\u0026#34;Run Task\u0026#34;).apply { setOnAction { Thread(task).start() } }).apply { alignment = Pos.CENTER padding = Insets(20.0) } } } fun handleProcessing(progressUpdater: (Long, Long) -\u0026gt; Unit, messageUpdater: (String) -\u0026gt; Unit) { progressUpdater(0, 100) messageUpdater(\u0026#34;Initializing\u0026#34;) Thread.sleep(Duration.ofMillis(3000)) progressUpdater(30, 100) messageUpdater(\u0026#34;Processing\u0026#34;) for (x in 31..90) { progressUpdater(x.toLong(), 100) Thread.sleep(52.toLong()) } messageUpdater(\u0026#34;Verifying\u0026#34;) Thread.sleep(2000.toLong()) progressUpdater(100, 100) messageUpdater(\u0026#34;Completed\u0026#34;) } fun main() { Application.launch(TaskProgress2App::class.java) } Updates from Background Threads # One thing that you might have noticed is that these calls to Task.updateProgress() and Task.updateMessage are happening from background threads. And yet these values are bound to elements on the SceneGraph! How does that work?\nThe JavaDocs clearly state that these methods are safe to be called from any thread. We\u0026rsquo;ll look at how this works in just a little bit, but it\u0026rsquo;s important to note that you do NOT need to put these calls in Platform.runLater(). As a matter of fact, as we\u0026rsquo;ll see, you shouldn\u0026rsquo;t put them in Platform.runLater(), as it could impact performance in cases where you are doing a lot of updates.\nCustom Updates # This basic use case seems like it\u0026rsquo;s what you need, but in practice maybe not so much. My experience has been that you often just don\u0026rsquo;t know how much processing is going to happen, or that you cannot measure it terms of \u0026ldquo;x number of total items\u0026rdquo; processed. You often end up with something that performs like the Microsoft updates progress bar, that gets to 95% in the first few seconds, and then sits there for 10 minutes.\nMore often than not, you just set the ProgressBar progress to \u0026ldquo;Indeterminent\u0026rdquo; and hope that the user doesn\u0026rsquo;t get bored and turn off the computer before it ends.\nAs you\u0026rsquo;ve seen, you can use message to convey progress and a sense that things aren\u0026rsquo;t hung to the user. But String is a poor substitute for some kind of custom object that holds status when you are using the values for actual processing. Imagine that you want certain areas of your GUI to be disabled only when certain stages of the process are actually running, or you want to trigger Events when the status changes to particular values. Using String in these cases would be a \u0026ldquo;code smell\u0026rdquo; if not an actual anti-pattern.\nHappily, we can build our own \u0026ldquo;thread-safe\u0026rdquo; methods that can hold a status, state or progress value of our own design that will work just as well as the built-in progress and message facilities.\nBut, before we can do that, we need to take a look at how Task handles those updates-from-background-threads, because that will show us how to build our own\u0026hellip;\nUnder the Hood With Task Updates # There\u0026rsquo;s something very important in the JavaDocs that\u0026rsquo;s easy to miss:\nQuote protected void updateMessage(String message)\nUpdates the message property. Calls to updateMessage are coalesced and run later on the FX application thread, so calls to updateMessage, even from the FX Application thread, may not necessarily result in immediate updates to this property, and intermediate message values may be coalesced to save on event notifications.\nThis method is safe to be called from any thread.\nWhat we want to understand here is this part:\n\u0026hellip;intermediate message values may be coalesced to save on event notifications.\nWhat does this mean?\nLet\u0026rsquo;s say that you have a background thread that\u0026rsquo;s generationg gazillions of calls to Task.updateMessage() from a background thread - way faster than the FXAT can process them given all the other stuff it\u0026rsquo;s doing. Well, in that case, it seems that this means that not all of our calls to Task.updateMessage() are going to result in screen updates.\nLet\u0026rsquo;s take a look at the source code for Task.updateMessage() to see how it does this, and what it really means:\nprivate AtomicReference\u0026lt;String\u0026gt; messageUpdate; . . . protected void updateMessage(String var1) { if (this.isFxApplicationThread()) { this.message.set(var1); } else if (this.messageUpdate.getAndSet(var1) == null) { this.runLater(new Runnable() { public void run() { String var1 = (String)Task.this.messageUpdate.getAndSet((Object)null); Task.this.message.set(var1); } }); } } First, we have a variable messageUpdate, which is an AtomicReference\u0026lt;String\u0026gt;. Atomic variables are designed to be concurrently accessed by multiple threads, which primarily means that get and set functions can be combined into a single, indivisible, atomic, operation. In this way, it\u0026rsquo;s guaranteed that some other thread won\u0026rsquo;t get in and change the value in between your thread checking the value and then changing it.\nThis concept is key to how this method works. First, let\u0026rsquo;s look at the flow when the call to Task.updateMessage() comes from a background thread\u0026hellip;\nTo understand this, you need to keep track of what code is running on what thread. The function is entirely composed of a single if statement. So if we come in on a background thread, then the if and the else if are going to run on that background thread. Most importantly, it means that:\nif (this.messageUpdate.getAndSet(var1) == null) including the call to messageUpdate.getAndSet(var1) will run on the background thread. AtomiceReference.getAndSet() will instantly update it\u0026rsquo;s stored value with var1, and then it returns whatever was previously stored in it.\nOnly in the case where the previous value in messageUpdate was null will it invoke Platform.runlater() to update the message Property.\nWhy? To understand that we need to look at what happens inside Platform.runLater(). There are two lines of code:\nString var1 = (String)Task.this.messageUpdate.getAndSet((Object)null); Task.this.message.set(var1); The first line gets the value from messageUpdate and instanly sets it value to null. The second line will update the Property with that value.\nFrom this, you can see that the FXAT is the only thread that puts null into messageUpdate (well, var1 could be null, but let\u0026rsquo;s not worry about that). So if our background thread checks messageUpdate and finds that it\u0026rsquo;s not null, then that must mean that there is a pending operation in the FXAT\u0026rsquo;s queue to update the message Property from messageUpdate. In that case, it\u0026rsquo;s not necessary to invoke a new FXAT job, but we can update the value in messageUpdate (actually, we already did that) and this new value is the one that the FXAT will pick up.\nLet\u0026rsquo;s imagine that we have a message that we want to walk through the values, \u0026ldquo;Starting\u0026rdquo;, \u0026ldquo;First\u0026rdquo;, \u0026ldquo;Second\u0026rdquo;, \u0026ldquo;Third\u0026rdquo;, and \u0026ldquo;Fourth\u0026rdquo;. These values to be supplied from our background thread. We\u0026rsquo;ll assume that message has just been updated to \u0026ldquo;Starting\u0026rdquo; and it\u0026rsquo;s put messageUpdate to null. The background thread is running really fast, and it will call Task.updateMessage() a few times before the FXAT can get around to copying the value into message.\nHere\u0026rsquo;s what our values look like:\nThread New Value Old Value message Queue FXAT Job? Background First Null Starting Yes Background Second First Starting No Background Third Second Starting No FXAT Null Third Third Is the FXAT Job From this, you can see that only one job is launched for the FXAT, and at that time, the value of messageUpdate was \u0026ldquo;First\u0026rdquo;. However, by the time that that initial job on the FXAT was executed, the background thread had called Task.updateMessage() two more times, and the value in messageUpdate was now \u0026ldquo;Third\u0026rdquo;. The values \u0026ldquo;First\u0026rdquo; and \u0026ldquo;Second\u0026rdquo; never made it to message. Once again, messageUpdate has the value null, and any call to Task.updateMessage() from the background thread will queue up a new FXAT job.\nBut what if a call to Task.updateMessage() comes from the FXAT? Then it just updates message and doesn\u0026rsquo;t touch messageUpdate. But what if messageUpdate isn\u0026rsquo;t null when this happens?\nIf messageUpdate has a non-null value in it, then there is a job on the FXAT to copy the current value of messageUpdate into message. The FXAT always processes jobs in sequential order. This means that if this code is running from the FXAT, then it was queued before the call from the background thread called Platform.runLater(). It may be running after that background code ran, but the job on the FXAT generated by that background code is still pending processing.\nThis is why you don\u0026rsquo;t want to put your calls to Task.updateMessage() inside Platform.runlater(). Because if you do that, then you\u0026rsquo;ll be creating a job on the FXAT for every single update to message, and if you are generating a huge number of updates in a short span of time, you might swamp the FXAT and cause performance issues with your GUI. If you issue your calls from the background thread, then you only get as many jobs on the FXAT as the FXAT can handle.\nUnderstanding this means that it\u0026rsquo;s pretty easy to implement our own status updates that work the same way..\nCreating Custom Data Updates # Let\u0026rsquo;s imagine that we have process that can be broken down into stages, each of which takes some time to process on a single background thread. We want the user to be updated as each stage progresses. We\u0026rsquo;ll all assume that we\u0026rsquo;re using a framework, so the actual work is going to be done from our Interactor.\nFirst let\u0026rsquo;s look at the data stuff:\nenum class ProcessingStage { STAGE1, STAGE2, STAGE3, STAGE4, DONE } class Model { val currentStage = SimpleObjectProperty(ProcessingStage.STAGE1) } This is pretty straight-forward. We have an Enum to hold our progress values, and a Model with just a single field - a Property to hold our progress Enum.\nNow let\u0026rsquo;s look at the Controller code:\nclass Controller { private val model = Model() private val interactor = Interactor(model) private val viewBuilder = ViewBuilder(model) { launchTask() } fun getView() = viewBuilder.build() private fun launchTask() { val task = object : Task\u0026lt;Unit\u0026gt;() { val stageUpdate = AtomicReference\u0026lt;ProcessingStage?\u0026gt;() val stage = SimpleObjectProperty(ProcessingStage.STAGE1) override fun call() { interactor.doProcessing { updateStage(it) } } fun updateStage(newStage: ProcessingStage) { if (Platform.isFxApplicationThread()) { stage.set(newStage) } else if (stageUpdate.getAndSet(newStage) == null) { Platform.runLater { stage.set(stageUpdate.getAndSet(null)) } } } } task.setOnSucceeded { model.currentStage.unbind() } model.currentStage.bind(task.stage) Thread(task).start() } } As usual, we intstantiate the other MVCI components and supply a getView() method for the Application.\nIn the Task we create a field to hold the AtomicReference for the processing stage Enum, then we create a Property to hold the processing stage. This is just like the way that standard Task handles message. Looking at the updateStage method you\u0026rsquo;ll see that it also follows exactly the same structure as Task.updateMessage(), updating the AtomicReference, launching a job on the FXAT to update the Property and manipulating the null value of the AtomicReference as a control.\nThe Task.call() implementation is simple, it just calls the Interactor method doProcessing() passing it a consumer that will invoke updateStage().\nThe launchTask() method instatiates our custom Task, binds the Model\u0026rsquo;s currentStage Property to the stage Property of the Task, loads the Task into a Thread and launches it. The only other thing it does it to create an onEventSucceeded handler that unbinds the currentStage Property of the Model.\nThe Interactor looks like this:\nclass Interactor(private val model: Model) { fun doProcessing(updater: (ProcessingStage) -\u0026gt; Unit) { doStep1() updater(ProcessingStage.STAGE2) doStep2() updater(ProcessingStage.STAGE3) doStep3() updater(ProcessingStage.STAGE4) doStep4() updater(ProcessingStage.DONE) } private fun doStep1() { Thread.sleep(5000) } private fun doStep2() { Thread.sleep(5000) } private fun doStep3() { Thread.sleep(5000) } private fun doStep4() { Thread.sleep(5000) } } Not much to say here. doProcessing() simply calls the various update sub-methods and invokes the updater Consumer between each method call.\nNext, the ViewBuilder:\nclass ViewBuilder(private val model: Model, private val taskRunner: () -\u0026gt; Unit) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region = BorderPane().apply { center = Label().apply { textProperty().bind(model.currentStage.asString()) } bottom = Button(\u0026#34;Click Me\u0026#34;).apply { setOnAction { taskRunner.invoke() } } } } And, finally, the Application:\nclass TaskProgress1App : Application() { override fun start(stage: Stage) { stage.scene = Scene(Controller().getView(), 300.0, 300.0) stage.show() } } Treating it as Data # Okay, but our Enum is hardly better than String in this example. So let\u0026rsquo;s put it together with another concept so that we treat it more like \u0026ldquo;data\u0026rdquo;.\ninterface PseudoClassSupplier { fun getPseudoClass(): PseudoClass } enum class ProgressPseudoClass : PseudoClassSupplier { STAGE1, STAGE2, STAGE3, STAGE4, DONE; companion object PseudoClasses { val stage1PseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;stage1\u0026#34;) val stage2PseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;stage2\u0026#34;) val stage3PseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;stage3\u0026#34;) val stage4PseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;stage4\u0026#34;) val donePseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;done\u0026#34;) } override fun getPseudoClass() = when (this) { STAGE1 -\u0026gt; stage1PseudoClass STAGE2 -\u0026gt; stage2PseudoClass STAGE3 -\u0026gt; stage3PseudoClass STAGE4 -\u0026gt; stage4PseudoClass DONE -\u0026gt; donePseudoClass } } This is a structure lifted from my article Non-Binary PseudoClasses. It\u0026rsquo;s an Enum with a PseudoClass references built in, and implementing the method getPseudoClass().\nWe can use this new class as a drop-in replacement for the Enum class ProcessingStage in our example. None of that code will change, except the class reference. In the ViewBuilder, we just need to add a Subscription to the Property and invoke Node.pseudoClassStateChange() on our Label which now has the StyleClass of progress-label added to it:\nclass ViewBuilder3(private val model: Model3, private val taskRunner: () -\u0026gt; Unit) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region = BorderPane().apply { center = Label().apply { textProperty().bind(model.currentStage.asString()) styleClass += \u0026#34;progress-label\u0026#34; model.currentStage.subscribe { oldVal, newVal -\u0026gt; oldVal?.let { pseudoClassStateChanged(it.getPseudoClass(), false) } newVal?.let { pseudoClassStateChanged(it.getPseudoClass(), true) } } } bottom = Button(\u0026#34;Click Me\u0026#34;).apply { setOnAction { taskRunner.invoke() } } } } We need to add this style sheet:\n.root {} .progress-label { -fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: blue; } .progress-label: stage1 { -fx-text-fill: black; } .progress-label: stage2 { -fx-text-fill: orange; } .progress-label: stage3 { -fx-text-fill: pink; } .progress-label: stage4 { -fx-text-fill: green; } .progress-label: done { -fx-text-fill: red; } To our Application:\nclass TaskProgress3App : Application() { override fun start(stage: Stage) { println(\u0026#34;Running Example3\u0026#34;) stage.scene = Scene(Controller3().getView(), 300.0, 300.0).apply { TaskProgress3App::class.java.getResource(\u0026#34;tasks.css\u0026#34;)?.toString()?.let { println(\u0026#34;adding stylesheet\u0026#34;) stylesheets += it } } stage.show() } } And we get something like this:\nCreating a Library Version # If you think that this is, perhaps, the key missing element from the standard JavaFX Task class, then you can easily turn this into a generic template of your own:\nabstract class StagedTask\u0026lt;T,S\u0026gt; : Task\u0026lt;T\u0026gt;() { private val stageUpdate = AtomicReference\u0026lt;S?\u0026gt;() val stage = SimpleObjectProperty\u0026lt;S?\u0026gt;() fun updateStage(newStage: S) { if (Platform.isFxApplicationThread()) { stage.set(newStage) } else if (stageUpdate.getAndSet(newStage) == null) { Platform.runLater { stage.set(stageUpdate.getAndSet(null)) } } } } This is still an abstract class, so you\u0026rsquo;ll need to provide the implementation of the call() method when you instantiate it. However, the \u0026ldquo;stage\u0026rdquo; element is now also a generic type, so you can provide updates via any class that you like. In our example, the implementation in the Controller would look like this:\nval task = object : StagedTask\u0026lt;Unit, ProgressPseudoClass\u0026gt;() { override fun call() { interactor.doProcessing { updateStage(it) } } } You could even provide an optional constructor parameter to set the initial value of stage:\nabstract class StagedTask\u0026lt;T, S\u0026gt;(initialStage: S? = null) : Task\u0026lt;T\u0026gt;() { private val stageUpdate = AtomicReference\u0026lt;S?\u0026gt;() val stage = SimpleObjectProperty\u0026lt;S?\u0026gt;(initialStage) fun updateStage(newStage: S) { if (Platform.isFxApplicationThread()) { stage.set(newStage) } else if (stageUpdate.getAndSet(newStage) == null) { Platform.runLater { stage.set(stageUpdate.getAndSet(null)) } } } } Conclusion # I think that the most interesting thing about this technique is that it\u0026rsquo;s literally lifted right out of the source code for Task and modified just enough for a new use case. It shows us two things:\nYou should take the time to look at the JavaFX source code, because there\u0026rsquo;s lots of nifty ideas and techniques that you can appropriate as your own. There\u0026rsquo;s nothing magical about the JavaFX source code.\n99% of the code that you see inside the various classes is just plain old Java. It\u0026rsquo;s not full of hand-compiled machine code, or mystery libraries that you can\u0026rsquo;t see. There are some annoying non-public API\u0026rsquo;s that are used in some cases that can make it difficult to copy an entire class to make a small change to the way it works, but you\u0026rsquo;ll usually only run into this when you are doing really advanced stuff. From time to time I hear people complaining that some specific feature is missing from JavaFX, or lamenting that no new layout classes or significant new features have been added to JavaFX in years. Yet, most of the time JavaFX gives you all of the tools you need to create those features yourself, just as we created StagedTask here. Often, it doesn\u0026rsquo;t even take much code to create those features. StagedTask is 13 lines long, including all the lines with nothing but } on them.\nOne of the more recent new features, Subscription is only about 50 lines of code all told. There is the Subscription interface itself, which is less than 20 lines long, and the three different subscribe methods, each of which is 9 lines long. You could have coded those yourself, creating the Subscription interface and then implementing the subscribe methods as extension functions on ObservableValue and Observable and it would have been just as good.\nAnd that\u0026rsquo;s the point. There\u0026rsquo;s nothing inherently inferior to creating your own features for JavaFX because there\u0026rsquo;s nothing inherently superior to those contained inside the standard JavaFX library. As long as your code is good and you understand what you are doing, your own features will run just as well, and just as safely and as efficiently as the ones inside the standard JavaFX library.\n","date":"20 June 2024","externalUrl":null,"permalink":"/javafx/elements/fxat/task-progress/","section":"JavaFX","summary":"A deeper look at the Task class and how to monitor progress while a Task in running on a background thread.","title":"Tracking Task Progress","type":"javafx"},{"content":" Introduction # This is the second article in a series that started with Understanding ListView . If you\u0026rsquo;re new to ListView you might want to take a look at that article first, as it explains some important concepts about how ListView works, and we\u0026rsquo;ll be building on that information in this article.\nListCell as a Layout # ListView really starts to shine when you look at it as an alternative to TableView to display complex data models. ListView is especially good when you have sparsely populated fields in your data, where TableView would gobble up valuable screen real estate on largely empty columns. It can also compress the display of information into a much tighter space and improve the visibility of certain data. Even more, ListView is extremely powerful when you look at it as a container for layouts that using the power of VirtualFlow to handle large amounts of data.\nFor the example of this, I found a really nifty resource on the web - a JSON file filled with thousands and thousands of clues and answers from the game show, \u0026ldquo;Jeopardy!\u0026rdquo;. The data for this is highly structured, with information about the show that each question appeared on, what round it came from, the question value and so on. It also has good potential to be interactive, which further shows how ListView can be much, much more than just a \u0026ldquo;List\u0026rdquo;.\nBefore we look at any code, let\u0026rsquo;s take a look at what the final product should look like:\nYeah, it looks a little busy, but that\u0026rsquo;s the point (as a matter of fact, it looks like a bad 1990\u0026rsquo;s website). Each row on the ListView here has an image to show what round the question came from, and then a section that shows the question itself, with a heading that\u0026rsquo;s holds the value of the question and its category. On the right side there\u0026rsquo;s some information about the show number and the date and a Button that says, \u0026ldquo;Reveal Answer\u0026rdquo;. When the user clicks the Button, it disappears and is replaced with the answer and a pair of RadioButtons that say, \u0026ldquo;Right\u0026rdquo; and \u0026ldquo;Wrong\u0026rdquo;. When you click one of the RadioButtons it changes the background colour of the question heading to either red or green.\nThe whole thing starts to look like the visualization of an interface for a practice application for Jeopardy! contestants. There\u0026rsquo;s no database functionality behind it to allow subsets of the questions to be selected, or to track the results, but the visuals are there.\nYou can see from this that each row works like its own application screen, yet they are contained within the ListView and it really does correspond to the visual display of a List of data.\nI switched over to Kotlin for this example. It\u0026rsquo;s just easier to do some of the layout in Kotlin, and the JSON handling is absolutely trivial using the built-in Kotlin serialization library.\nKotlin The examples in this article are written in Kotlin. If you need some help understanding Kotlin, then refer to this article. The Data # The original data for this was a 50MB JSON file. I stripped out the first 10KB or so of data to use as a sample. The JSON data looks like this:\n{ \u0026#34;category\u0026#34;: \u0026#34;HISTORY\u0026#34;, \u0026#34;air_date\u0026#34;: \u0026#34;2004-12-31\u0026#34;, \u0026#34;question\u0026#34;: \u0026#34;\u0026#39;This Asian political party was founded in 1885 with \\\u0026#34;Indian National\\\u0026#34; as part of its name\u0026#39;\u0026#34;, \u0026#34;value\u0026#34;: \u0026#34;$1000\u0026#34;, \u0026#34;answer\u0026#34;: \u0026#34;the Congress Party\u0026#34;, \u0026#34;round\u0026#34;: \u0026#34;Jeopardy!\u0026#34;, \u0026#34;show_number\u0026#34;: \u0026#34;4680\u0026#34; } I want to point out that this is the state that data came. If you\u0026rsquo;re offended that the field names are backwards, and the \u0026ldquo;question\u0026rdquo; is actually the Jeopardy! \u0026ldquo;answer\u0026rdquo; and response is in the \u0026ldquo;answer\u0026rdquo; field, or that the response is not in the form of a question\u0026hellip;\nWell, so am I. But I wasn\u0026rsquo;t about to fiddle with the JSON data to fix it.\nAnd I de-serialized it into a data object that has the same wrong field names:\n@Serializable data class JeopardyQuestion( val category: String = \u0026#34;\u0026#34;, @SerialName(\u0026#34;air_date\u0026#34;) val airDate: String = \u0026#34;\u0026#34;, val question: String = \u0026#34;\u0026#34;, val value: String = \u0026#34;\u0026#34;, val answer: String = \u0026#34;\u0026#34;, val round: String = \u0026#34;\u0026#34;, @SerialName(\u0026#34;show_number\u0026#34;) val showNumber: Int = 0 ) You can see how simple the Kotlin serialization library is to use. In this case, I just left everything as Strings because I\u0026rsquo;m not doing anything other than displaying it as Strings on the screen (except for round, which we\u0026rsquo;ll get to). If I was going to search or sort on value or airDate then I\u0026rsquo;d have to convert them to other, more appropriate, data types. But this works for the demo.\nLet\u0026rsquo;s take a look at a sample data model:\nclass DemoListModel1(question: JeopardyQuestion) { val category: StringProperty = SimpleStringProperty(question.category) val airDate: StringProperty = SimpleStringProperty(question.airDate) val question: StringProperty = SimpleStringProperty(question.question.removeSurrounding(\u0026#34;\u0026#39;\u0026#34;)) val questionValue: StringProperty = SimpleStringProperty(question.value) val answer: StringProperty = SimpleStringProperty(question.answer) val round: ObjectProperty\u0026lt;QuestionType\u0026gt; = SimpleObjectProperty(QuestionType.fromString(question.round)) val showNumber: IntegerProperty = SimpleIntegerProperty(question.showNumber) val revealed: BooleanProperty = SimpleBooleanProperty(false) val gotItRight: BooleanProperty = SimpleBooleanProperty(false) val gotItWrong: BooleanProperty = SimpleBooleanProperty(false) public fun unbindFrom(other: DemoListModel1?) { other?.also { otherModel: DemoListModel1 -\u0026gt; revealed.unbindBidirectional(otherModel.revealed) gotItRight.unbindBidirectional(otherModel.gotItRight) gotItWrong.unbindBidirectional(otherModel.gotItWrong) } question.unbind() round.unbind() answer.unbind() category.unbind() questionValue.unbind() showNumber.unbind() airDate.unbind() } public fun bindTo(other: DemoListModel1) { question.bind(other.question) round.bind(other.round) questionValue.bind(other.questionValue) category.bind(other.category) answer.bind(other.answer) showNumber.bind(other.showNumber) airDate.bind(other.airDate) revealed.bindBidirectional(other.revealed) gotItRight.bindBidirectional(other.gotItRight) gotItWrong.bindBidirectional(other.gotItWrong) } } The constructor allows us to create DemoListModel1 from JeopardyQuestion, simply putting the values from JeopardyQuestion into the corresponding Property fields in DemoListModel1. There are 3 BooleanProperty fields that aren\u0026rsquo;t populated from JeopardyQuestion and these are all used to control aspects of the display in each ListView row.\nAfter that, we have two methods that allow the individual fields in two DemoListModel1s to be bound together. Since we\u0026rsquo;re going to use bidirectional binding on those 3 BooleanProperty fields, we need to pass the previously bound DemoListModel so that the unbinding can happen. These are basically convenience methods that allow this logic to be kept out of our custom ListCell. We\u0026rsquo;ll see this in a little bit.\nSo let\u0026rsquo;s look at that:\nval format = Json { ignoreUnknownKeys = true } public fun getData(): List\u0026lt;JeopardyQuestion\u0026gt; { val fileData: String = DemoApplication1::class.java.getResource(\u0026#34;JEOPARDY_QUESTIONS.json\u0026#34;)?.readText() ?: \u0026#34;\u0026#34; val data: List\u0026lt;JeopardyQuestion\u0026gt; = format.decodeFromString\u0026lt;List\u0026lt;JeopardyQuestion\u0026gt;\u0026gt;(fileData) println(\u0026#34;The Data $data\u0026#34;) return data.shuffled() } public fun loadModel(data: List\u0026lt;JeopardyQuestion\u0026gt;) = data.map { DemoListModel1(it) } } I left it as two methods because, if there was a database or service behind this, you\u0026rsquo;d use a Task in the Controller and call the getData() function from the background thread, and loadModel() from the FXAT. That\u0026rsquo;s just habit, but I didn\u0026rsquo;t bother with the Task since I\u0026rsquo;m only using a small dataset from a file resource.\nThe Layout and the Application # The layout for the application is just the ListView:\nclass DemoViewBuilder1(val data: ObservableList\u0026lt;DemoListModel1\u0026gt;) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region { return ListView\u0026lt;DemoListModel1\u0026gt;().apply { items = data cellFactory = Callback { JeopardyCell() } } } } Here\u0026rsquo;s the Controller:\nclass DemoController1() { val model = FXCollections.observableArrayList\u0026lt;DemoListModel1\u0026gt;() val interactor = DemoInteractor1() val viewBuilder = DemoViewBuilder1(model) init { model.setAll(interactor.loadModel(interactor.getData())) } public fun getView() = viewBuilder.build() } And the Application itself:\nclass DemoApplication1 : Application() { val format = Json { ignoreUnknownKeys = true } override fun start(stage: Stage) { val scene = Scene(DemoController1().getView(), 820.0, 840.0) scene.addWidgetStyles() stage.title = \u0026#34;Jeopardy Questions!\u0026#34; stage.scene = scene stage.show() } } fun main() { Application.launch(DemoApplication1::class.java) } Finally, we\u0026rsquo;ll look at the enum for the \u0026ldquo;round\u0026rdquo;, which I called QuestionType:\nenum class QuestionType(filePath: String) { JEOPARDY(\u0026#34;Jeopardy_S38_OnSetLogo.png\u0026#34;), DOUBLE_JEOPARDY(\u0026#34;JS37TOC-S38_DJ.png\u0026#34;), FINAL_JEOPARDY(\u0026#34;JS37TOC-S38_FJ.png\u0026#34;); val image = Image(QuestionType::class.java.getResource(filePath)?.toExternalForm()) companion object { public fun fromString(stringName: String) = when (stringName) { \u0026#34;Jeopardy!\u0026#34; -\u0026gt; JEOPARDY \u0026#34;Double Jeopardy!\u0026#34; -\u0026gt; DOUBLE_JEOPARDY \u0026#34;Final Jeopardy!\u0026#34; -\u0026gt; FINAL_JEOPARDY else -\u0026gt; JEOPARDY } } } This is very similar to the Animal enum earlier. However, we\u0026rsquo;ve added a static fromString() function to make the mapping from the JSON String values easier.\nThe Custom ListCell # You can see from this that just about all of the components of the application are very straight-forward and simple. The Interactor gets the data and loads it into the Presentation Model, which is just a list of JavaFX Property POJO\u0026rsquo;s, the layout is just a ListView and the Controller doesn\u0026rsquo;t do much except instantiate things and get the Interactor started on fetching the data.\nWhich means that all the cool stuff must be in the custom ListCell! First let\u0026rsquo;s take another look at Cell.updateItem()\nAbout Cell.updateItem() (Part 2 if you\u0026rsquo;ve read the Previous Article) # Let\u0026rsquo;s talk about what Cell.updateItem() actually does. First, here\u0026rsquo;s the source code for it:\nprotected void updateItem(T item, boolean empty) { setItem(item); setEmpty(empty); if (empty \u0026amp;\u0026amp; isSelected()) { updateSelected(false); } } That\u0026rsquo;s not much, actually. What do setItem(), setEmpty() and updateSelected() do? Here\u0026rsquo;s the setItem() code:\nprivate ObjectProperty\u0026lt;T\u0026gt; item = new SimpleObjectProperty\u0026lt;T\u0026gt;(this, \u0026#34;item\u0026#34;); public final ObjectProperty\u0026lt;T\u0026gt; itemProperty() { return item; } /** * Sets the item to the given value - should not be called directly as the * item is managed by the virtualized control. * @param value the new data value to set in this cell */ public final void setItem(T value) { item.set(value); } OK. From this we can see that there\u0026rsquo;s an ObjectProperty\u0026lt;T\u0026gt; in the Cell called item. T is just the generic type of the data that we\u0026rsquo;re putting in the Cell. And Cell.updateItem() just takes whatever new data is being put into the cell, and loads it into that property. Which explains the name, updateItem() - because that\u0026rsquo;s what it does. Along the way, it also updates empty and selected, which are also properties of the Cell.\nI think that this is the way to view Cell.updateItem(). It\u0026rsquo;s data update function, and it should just do data update stuff. More specifically, item, empty and selected form the Presentation Model for the Cell, and that\u0026rsquo;s all that Cell.updateItem() should be doing, even when we override it - updating the Presentation Model.\nIt\u0026rsquo;s tempting at this point to consider item to be the Presentation Model for the Cell and to build a layout based on that. That\u0026rsquo;s not a bad idea, except for one thing: Since item is actually a Property wrapper around that Presentation Model, it will change constantly as the application runs. This is fundamentally different from the way we create standard Reactive layouts, where the Presentation Model is immutable, and only the values in its properties change.\nThis makes it very difficult to simply Bind your data properties in the Nodes in your layout to properties in the object contained in item, since whenever the value in item changes all of the field properties inside it change their references. So, if you do something like this:\nval someText = Text().apply{ textProperty.bind(getItem().abcProperty()) } Then you are going to have problems. The first of which is that getItem() is going to return Null when this code is run as part of the constructor of your ListCell, that\u0026rsquo;s because nothing will have been loaded into the ListCell yet.\nBut even if you could get around that somehow, as soon as a new value is loaded into item, then getItem().abcProperty() would return an entirely new Property, and your binding on someText.textProperty() would become invalid.\nClearly, that\u0026rsquo;s a problem.\nA better approach is to create an immutable Presentation Model of the same type as the ListCell data type (in other words, the same type as the object contained in item), and bind your Node properties to that object. Then manage the values in its fields, updating them as item changes.\nAnd that brings us back to Cell.updateItem().\nSince we know that any changes to item have to go through here, we can treat it as a virtual ChangeListener and use it to reset our immutable Presentation Model. Also, since we want our layout to respond to any changes in the underlying data in real-time, we Bind the fields in our Presentation Model to the fields in the new item. Let\u0026rsquo;s take a look at that:\npublic override fun updateItem(item: DemoListModel1?, isEmpty: Boolean) { model.unbindFrom(this.item) super.updateItem(item, isEmpty) graphic = if (!isEmpty \u0026amp;\u0026amp; (item != null)) { model.bindTo(item) layout } else { null } } In this code snippet, model is the immutable Presentation Model in our ListCell. Now you can see the reason that we put the bindTo() and unbindFrom() methods into DemoListModel1. The other thing to note about this is that we are unbinding before we call super.updateItem(). This means that we are really treating updateItem() as a ChangeListener since we\u0026rsquo;re accessing both the old value in item and the new value that\u0026rsquo;s going in.\nIn Kotlin, if is also an expression, and the value of the last line in a block is the return value of that block. The if statement here will either populate graphic with Null or our layout depending on the the condition.\nThe Layout # Honestly, we could just stop this article right here, since you\u0026rsquo;ve seen all of the code specific to manipulating the data into and out of the ListCell. At this point the layout of the ListCell is just like any other layout, with an immutable Presentation Model that we bind to the various properties of our layout\u0026rsquo;s Nodes. But we\u0026rsquo;ll take a look at it anyway.\nHere\u0026rsquo;s the entire ListCell:\nclass JeopardyCell() : ListCell\u0026lt;DemoListModel1\u0026gt;() { private val model = DemoListModel1(JeopardyQuestion()) private val layout: Region companion object { val wrongAnswerPS: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;wrong\u0026#34;) val rightAnswerPS: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;right\u0026#34;) } init { layout = HBox(10.0, createImageBox(), createQuestionPanel(), createRightPanel()) addStyle \u0026#34;cell-box\u0026#34; text = null } private fun createImageBox() = StackPane( imageViewOf(Bindings.createObjectBinding({ model.round.value.image }, model.round)) ) addStyle \u0026#34;image-box\u0026#34; private fun createQuestionPanel() = VBox(4.0, createHeadingBox(), wrappingTextOf(model.question, 440.0)) withMinWidth 450.0 private fun createHeadingBox() = HBox(10.0).apply { children += Label() styleAs \u0026#34;question-value\u0026#34; bindTo model.questionValue children += headingOf(model.category) model.gotItRight.addListener(InvalidationListener { this.pseudoClassStateChanged( rightAnswerPS, model.gotItRight.value ) }) model.gotItWrong.addListener(InvalidationListener { this.pseudoClassStateChanged( wrongAnswerPS, model.gotItWrong.value ) }) } addStyle \u0026#34;heading-box\u0026#34; private fun createRightPanel() = VBox(2.0, createShowInfo(), createAnswerPane()) private fun createShowInfo() = TextFlow( styledTextOf(\u0026#34;Show \u0026#34;, \u0026#34;show-info\u0026#34;), styledTextOf(model.showNumber.asString(), \u0026#34;show-info\u0026#34;), styledTextOf(\u0026#34;: \u0026#34;, \u0026#34;show-info\u0026#34;), styledTextOf(model.airDate, \u0026#34;show-info\u0026#34;) ) private fun createAnswerPane() = StackPane().apply { children += Button(\u0026#34;Reveal Answer\u0026#34;).apply { onAction = EventHandler { model.revealed.value = true } visibleProperty().bind(model.revealed.not()) } styleAs \u0026#34;answer-button\u0026#34; children += createRevealAnswerBox() alignment = Pos.CENTER_LEFT } private fun createRevealAnswerBox() = VBox(2.0).apply { children += (Label() styleAs \u0026#34;label-answer\u0026#34; bindTo model.answer).apply { maxWidth = 180.0 minWidth = 180.0 isWrapText = true } children += createRightWrongBox() visibleProperty().bind(model.revealed) } private fun createRightWrongBox() = HBox(6.0).apply { val rightRB = RadioButton(\u0026#34;Right\u0026#34;) val wrongRB = RadioButton(\u0026#34;Wrong\u0026#34;) rightRB.selectedProperty().bindBidirectional(model.gotItRight) wrongRB.selectedProperty().bindBidirectional(model.gotItWrong) ToggleGroup().apply { toggles += listOf(rightRB, wrongRB) } children += listOf(rightRB, wrongRB) } public override fun updateItem(item: DemoListModel1?, isEmpty: Boolean) { model.unbindFrom(this.item) super.updateItem(item, isEmpty) graphic = if (!isEmpty \u0026amp;\u0026amp; (item != null)) { model.bindTo(item) layout } else { null } } } This layout code makes extensive use of a library of factory methods and extension methods (which is a Kotlin thing) to compress the layout code so that it\u0026rsquo;s just layout, with as much configuration code as possible off somewhere else where we don\u0026rsquo;t need to worry about it. Most of the meanings are intuitively obvious (I hope). For instance: styledTextOf(StringProperty, String) creates a Text bound to the specified StringProperty and with the specified CSS selector appended to its StyleClass list. Even if you don\u0026rsquo;t really understand the syntax, you should be able to get the idea about what it\u0026rsquo;s doing.\nThe main layout is an HBox with 3 sections: an image, a \u0026ldquo;Question Box\u0026rdquo; and then a Pane with show info and the interactive answer stuff. These are all built in their own builder methods.\nThe PseudoClass code is there to change the background colour of the header box to correspond to whether or not the question was answered correctly.\nSince the \u0026ldquo;Show Information\u0026rdquo; section is composed of some static text and some bound values, a TextFlow was used. This seemlessly puts the various pieces together into a single element.\nThe interactive part is where most of the complexity lies. Basically, it\u0026rsquo;s a StackPane with two children, only one of which is visible at any given time. The visibility is controlled by model.revealed; the Button is visible when it\u0026rsquo;s false and the revealAnswerBox when it\u0026rsquo;s true. Clicking the Button does nothing more than set model.revealed to true.\nThe revealAnswerBox is just a VBox with a Label showing the answer and an HBox holding the RadioButtons. The RadioButtons are in a ToggleGroup so that only one can be selected at any given time. Each RadioButton has its selected property bound to one of model.gotItRight or model.gotItWrong. Selecting one of these RadioButtons will trigger a change in one or both of the PseudoClasses that control the background colour of the question heading box.\nWhat\u0026rsquo;s important to note here is that all three of the interactive elements are bidirectionally bound (well, the Button is an action) to the model properties which are, in turn, bidirectionally bound to the item properties. And the item is actually a reference to one of the DemoListModel1 objects in the list backing the ListView. This means that clicking the Button or selecting one of the RadioButtons triggers a permanent change to the related field in one of the items in the ObservableList.\nAnd this is important because when the user scrolls that row out of the viewport, then that ListCell is released to be reused for another row. But if the user scrolls that row back into the viewport, then those updated values are going to be loaded back into the ListCell through updateItem().\nHere\u0026rsquo;s the Stylesheet:\n.root { -theme-colour: #3612dF; -contrast-colour: #8F2F2F; -complementary1: #3c91e6; -complementary2: #daebfd; -wrong-colour: #de9dc0; -right-colour: #76a975; -image-background-colour: #17104d; } .label-heading { -fx-text-fill: -theme-colour; -fx-font-weight: bold; -fx-font-size: 20px; } .question-value { -fx-text-fill: -contrast-colour; -fx-font-weight: bold; -fx-font-size: 20px; } .show-info { -fx-fill: -contrast-colour; -fx-font-weight: bold; -fx-font-size: 14px; } .label-answer { -fx-text-fill: -theme-colour; -fx-font-weight: bold; -fx-font-size: 15px; } .heading-box { -fx-border-color: -theme-colour; -fx-border-radius: 4px; -fx-padding: 2px; -fx-background-color: -complementary2; -fx-background-radius: 4px; } .heading-box:wrong { -fx-background-color: -wrong-colour; } .heading-box:right { -fx-background-color: -right-colour; } .image-box { -fx-background-color: -image-background-colour; } .cell-box { -fx-border-color: -theme-colour; -fx-border-radius: 2px; -fx-padding: 2px; } .answer-button { -fx-background-color: linear-gradient(-theme-colour, -contrast-colour), radial-gradient(center 50% -10%, radius 200%, -theme-colour 45%, -complementary1 50%); -fx-background-radius: 6, 5; -fx-background-insets: 0, 1; -fx-effect: dropshadow( three-pass-box , rgba(0,0,0,0.4) , 5, 0.0 , 0 , 1 ); -fx-text-fill: #F9CCA2; } Conclusion # The point of this tutorial is to help you to see a ListView as a container for a reusable layout allowing it to display a potentially large amount of date - not just as a vehicle to show a \u0026ldquo;list\u0026rdquo; of stuff. Essentially, this reusable layout is no different from any other kind of Reactive layout that you might create, except that you need to deal with the issue of loading new Presentation Models into it as cells are reused when they scroll into the viewport.\nOnce you see ListView in this light, it opens up a whole world of possibilities for interesting GUI designs. And it frees you from the spreadsheet-like confines of TableView.\n","date":"18 March 2024","externalUrl":null,"permalink":"/javafx/elements/nodes/tableview-listview/listview-layouts/","section":"JavaFX","summary":"Looking at ListView as a scrolling region of customized layouts, not just a list of String.","title":"Using ListView Cell Layouts","type":"javafx"},{"content":" Introduction # This is Part II in a series about creating custom controls that starts with this article. In the first instalment, we looked at how to look at DRY as the gateway technique towards viewing everything in your layout as a custom control and then examined the basic techniques to create custom controls.\nThe final result of Part I was a custom control made by extending the standard Region class of JavaFX. This is useful because the method Region.getChildren() is protected, which means that we won\u0026rsquo;t be exposing the implementation of our control to the client code.\nIn this article, we\u0026rsquo;re going to look at how to complete the setup of our custom control to turn it into a true utility class that is virtually indistinguishable from the standard JavaFX Node classes, and can be used and styled in exactly the same ways.\nOur Example # For this tutorial we\u0026rsquo;re going to continue (from the previous article) to work with a control that looks like this:\nIt\u0026rsquo;s an HBox with a Label, a TextField and a Button in it. When the TextField has focus, then the Button becomes the default button, so you can just type in the TextField and hit to trigger the Button. When the TextField is empty, then the Button is disabled.\nThe Original Version - Extended From Region # In the first part of this series, we created a class that extends from Region, which means that the client code can\u0026rsquo;t mess with its implementation:\nclass CustomRegion(private val labelText: String, private val buttonText: String, private val boundProperty: StringProperty) : Region() { private var buttonAction: ButtonRunner = {} private val actionRunning: BooleanProperty = SimpleBooleanProperty(false) private val hBox: HBox init { hBox = createLayout() children.add(hBox) } private fun createLayout() = HBox().apply { val textField = TextField().apply { textProperty().bindBidirectional(boundProperty) } children += Label(labelText) children += textField children += Button(buttonText).apply { onAction = EventHandler\u0026lt;ActionEvent\u0026gt; { actionRunning.value = true buttonAction { actionRunning.value = false } } defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind((textField.textProperty().isEmpty).or(actionRunning)) } alignment = Pos.CENTER_LEFT } fun setButtonAction(newAction: ButtonRunner) = this.apply { buttonAction = newAction } fun withSpacing(newSpacing: Double) = this.apply { hBox.spacing = newSpacing } } And it was called from this:\nclass RegionStyle : Application() { private val name: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val results: StringProperty = SimpleStringProperty(\u0026#34;No search done\u0026#34;) override fun start(stage: Stage) { stage.scene = Scene(createContent()) stage.show() } private fun createContent(): Region = BorderPane().apply { center = CustomRegion(\u0026#34;Last Name:\u0026#34;, \u0026#34;Search\u0026#34;, name).setButtonAction { postRunAction -\u0026gt; doSearch() PauseTransition(Duration(3000.0)).apply { onFinished = EventHandler { postRunAction.run() } play() } }.withSpacing(50.0) bottom = Label().apply { textProperty().bind(results) } padding = Insets(20.0) } private fun doSearch() { results.value = \u0026#34;Nothing found for: ${name.value}\u0026#34; } } fun main() = Application.launch(RegionStyle::class.java) Adding More Client Options # This original version is okay, but the implementation is specific in its layout, and doesn\u0026rsquo;t really allow client code to adjust it for a wide range of applications. The first thing we\u0026rsquo;ll do is create some delegate properties to the component Node properties:\nclass InputActionWidget1(private val boundProperty: StringProperty, labelText: String = \u0026#34;Prompt:\u0026#34;, buttonText: String = \u0026#34;Start\u0026#34;) : Region() { private var buttonAction: ButtonRunner = {} private val actionRunning: BooleanProperty = SimpleBooleanProperty(false) private val hBox: HBox private val label = Label(labelText) private val textField = TextField() private val button = Button(buttonText) var labelText: String get() = label.text set(value) = run { label.text = value } val labelTextProperty: StringProperty get() = label.textProperty() var buttonText: String get() = button.text set(value) = run { button.text = value } val buttonTextProperty: StringProperty get() = button.textProperty() var textFieldText: String get() = textField.text set(value) = run { textField.text = value } val textFieldTextProperty: StringProperty get() = textField.textProperty() init { hBox = createLayout() children.add(hBox) } private fun createLayout() = HBox().apply { children += listOf(label, textField, button) textFieldTextProperty.bindBidirectional(boundProperty) configureButton() alignment = Pos.CENTER_LEFT } private fun configureButton() { button.apply { onAction = EventHandler\u0026lt;ActionEvent\u0026gt; { actionRunning.value = true buttonAction { actionRunning.value = false } } defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind((textField.textProperty().isEmpty).or(actionRunning)) } } fun setButtonAction(newAction: ButtonRunner) = this.apply { buttonAction = newAction } fun withSpacing(newSpacing: Double) = this.apply { hBox.spacing = newSpacing } } Here\u0026rsquo;s what we\u0026rsquo;ve done:\nChanged the Name The original name, \u0026ldquo;CustomRegion\u0026rdquo; didn\u0026rsquo;t really say anything about what this control does. The new name \u0026ldquo;InputActionWidget\u0026rdquo; describes what it does a bit better. It takes some input, then performs an action on it. Delegate Fields for Node Text Properties The Button, Label and TextField all have a \u0026ldquo;Text\u0026rdquo; property. We\u0026rsquo;ve created delegate fields for all of these properties so that we can access them from the client code. Kotlin makes this approach really easy, but you can do the same thing with Java methods that just delegate to the Node properties. These all have unique names, like textFieldText, in our custom control. Default Constructor Values This is another Kotlin bonus, but you can do the same thing in Java by supplying additional constructors that take different parameters. In order to make this work cleanly, the only mandatory parameter, boundProperty has been moved to the front. Cleaned Up createLayout() Now that we have all of the Nodes instantiated as fields, it makes sense to clean up the layout code and move the Button configuration into its own method. This version is a bit more versatile than the original one.\nCleaning up the Layout # The layout of our custom class is a bit lame. Let\u0026rsquo;s try making the Button and the TextField look more like a single element.\nThe first thing we can do is to remove the space between them. This is done easiest by putting them in their own HBox and setting the spacing in that HBox to zero. We change this line:\nchildren += listOf(label, textField, button) to this:\nchildren += listOf(label, HBox(0.0, textField, button)) The result looks like this:\nAdding Styling # That doesn\u0026rsquo;t look too bad, but the Button and the TextField look more like they\u0026rsquo;re squished up together than attached to each other as a single thing. It\u0026rsquo;s the rounded corners that cause the problem. Let\u0026rsquo;s fix that.\nTo do this, we need to understand nested style classes in the CSS file. The first thing that we\u0026rsquo;re going to do is to add a style class for our custom region, we\u0026rsquo;ll call it \u0026ldquo;input-action-widget\u0026rdquo;. We\u0026rsquo;ll add a line to init{}:\ninit { styleClass += \u0026#34;input-action-widget\u0026#34; hBox = createLayout() children.add(hBox) } The styling for Buttons in Modena doesn\u0026rsquo;t have borders, but it has a set of backgrounds stacked on top of each other with different insets. The effect looks like a border with shadows and highlights, but it\u0026rsquo;s not actually a border.\nIn our StyleSheet, we\u0026rsquo;ll add this:\n.input-action-widget .button { -fx-background-radius: 0 7 7 0px, 0 7 7 0px, 0 6 6 0px, 0 5 5 0px; } This changes the styling for any Buttons in our custom class to be rounded on the right side, and square on the left. I\u0026rsquo;ve added a Button to the layout outside the custom class so you can see the difference:\nYou can see that the nested styling is working. We need to do the same for the TextField, making the right side have sharp corners.\nNow we need to have the Button have the same kind of focus highlighting that the TextField does but when the TextField has focus. We\u0026rsquo;re looking for this:\nThis is a little bit trickier to achieve. To do this, we need to create a new PseudoClass for the Button to be true when the TextField has focus. If you\u0026rsquo;re confused about PseudoClases, then you can look at this article which explains the concept. Next, we put a ChangeListener on the FocusProperty of the TextField and trigger a state change in the PseudoClass through it. Finally, we create a CSS entry for it.\nHere\u0026rsquo;s the code that does this:\ncompanion object PseudoClasses { val TEXTFIELD_FOCUSED: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;textfield-focused\u0026#34;); } init { styleClass += \u0026#34;input-action-widget\u0026#34; hBox = createLayout() children.add(hBox) textField.focusedProperty().addListener { observable, oldValue, newValue -\u0026gt; button.pseudoClassStateChanged(TEXTFIELD_FOCUSED, newValue); } } A companion object is the Kotlin way to do something like static elements. It\u0026rsquo;s actual a nested singleton, but that\u0026rsquo;s not really important. Kotlin let\u0026rsquo;s you refer to its members without specifying the companion object. In the init{} block we connect the PseudoClass to textField.focusedProperty() with a ChangeListener. Now our PseudoClass will stay synchronized with the FocusedProperty of the TextField.\nHere\u0026rsquo;s the StyleSheet entry for the PseudoClass\n.input-action-widget .button:textfield-focused { -fx-background-color: -fx-focus-color, -fx-outer-border, -fx-inner-border, -fx-body-color; -fx-background-insets: -0.5, 0.7,1.3, 2; } This was figured out by looking at the Modena stylesheet and putting together the outside blue ring of TextField onto the inner colours of Button.\nIn the end, it now looks and acts very much like a single control.\nThe Code So Far # Here\u0026rsquo;s the entire code for InputActionWidget2 so far:\nclass InputActionWidget2(private val boundProperty: StringProperty, labelText: String = \u0026#34;Prompt:\u0026#34;, buttonText: String = \u0026#34;Start\u0026#34;) : Region() { private var buttonAction: ButtonRunner = {} private val actionRunning: BooleanProperty = SimpleBooleanProperty(false) private val label = Label(labelText) private val textField = TextField() private val button = Button(buttonText) var labelText: String get() = label.text set(value) = run { label.text = value } val labelTextProperty: StringProperty get() = label.textProperty() var buttonText: String get() = button.text set(value) = run { button.text = value } val buttonTextProperty: StringProperty get() = button.textProperty() var textFieldText: String get() = textField.text set(value) = run { textField.text = value } val textFieldTextProperty: StringProperty get() = textField.textProperty() companion object PseudoClasses { val TEXTFIELD_FOCUSED: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;textfield-focused\u0026#34;); } init { styleClass += \u0026#34;input-action-widget\u0026#34; children.add(createLayout()) } private fun createLayout() = HBox(4.0).apply { children += listOf(label, HBox(0.0, textField, button)) textFieldTextProperty.bindBidirectional(boundProperty) configureButton() textField.focusedProperty().addListener { _, _, newValue -\u0026gt; button.pseudoClassStateChanged(TEXTFIELD_FOCUSED, newValue); } alignment = Pos.CENTER_LEFT } private fun configureButton() { button.apply { onAction = EventHandler\u0026lt;ActionEvent\u0026gt; { actionRunning.value = true buttonAction { actionRunning.value = false } } isFocusTraversable = false defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind((textField.textProperty().isEmpty).or(actionRunning)) } } fun setButtonAction(newAction: ButtonRunner) = this.apply { buttonAction = newAction } } The only other change here is that the setSpacing() function didn\u0026rsquo;t seem needed any more, so it\u0026rsquo;s been removed.\nAnd here\u0026rsquo;s the complete set of StyleSheet entries for it:\n.input-action-widget .button { -fx-background-radius: 0 7 7 0px, 0 7 7 0px, 0 6 6 0px, 0 5 5 0px; } .input-action-widget .button:textfield-focused { -fx-background-color: -fx-focus-color, -fx-outer-border, -fx-inner-border, -fx-body-color; -fx-background-insets: -0.5, 0.7,1.3, 2; } .input-action-widget .text-input { -fx-background-radius: 3 0 0 3, 2 0 0 2; } Custom Styling Elements # Regular JavaFX Nodes allow you to do a lot of styling through either code or CSS. For instance that spacing in the HBox can be controlled through HBox.setSpacing() or by using the -fx-spacing tag in the style sheet. I tend to think of spacing as a coding thing more than a style element because there\u0026rsquo;s a version of the constructor for both HBox and VBox that let\u0026rsquo;s you specify the spacing. Otherwise, I usually lean towards the style sheet implementations for most styling.\nLet\u0026rsquo;s keep looking at that space item, and deal with it as a custom styling element.\nWe don\u0026rsquo;t want to expose the outer HBox of our layout (nor the inner one around the TextField and Button), because we don\u0026rsquo;t want client messing with that. So, let\u0026rsquo;s keep it hidden.\nBut how do we allow a stylesheet entry for the spacing?\nStyleable Properties # Have you ever wondered how that -fx-spacing line in the style sheet ends up changing the spacing in an HBox? Or how any style sheet attribute controls a Node property?\nClearly there\u0026rsquo;s some stuff going on deep inside the layout manager in JavaFX that we don\u0026rsquo;t want to look at, but all of the pieces that allow the authors of JavaFX to configure HBox to respond to -fx-spacing in the style sheet are available to us to create our own style sheet properties.\nOnce the layout manager has found a style sheet selector that applies to a Node is has to follow a process close to this list to apply a style sheet property from that selector to that Node on the screen:\nDetermine if the style sheet property is associated with the Node. Determine if the style sheet is allowed to update the Node. Identify the StyleableProperty associated with the style sheet property. Convert the value in the style sheet to something compatible with the StyleableProperty. Update the StyleableProperty with the converted value from the style sheet. This is done through two elements:\nStyleableProperty This is an interface that has one key method that we care about, applyStyle(). There\u0026rsquo;s a suit of SimpleStyleable{Type}Property classes that implement this interface, which also extend or implement all of the appropriate Observable classes and interfaces. CssMetaData This is the class that provides the connection between the style sheet and the StyleableProperty. It has all of the data and the methods to do virtually all of those steps listed above. If you look at the JavaDocs for StyleableProperty, StyleablePropertyFactory and CssMetaData your head is probably going to explode. It\u0026rsquo;s not that the concepts are hard to understand, but the explanations in the JavaDocs seem designed to make it nearly impossible to figure it out.\nAdding a \u0026ldquo;TagGap\u0026rdquo; Property # We\u0026rsquo;re going to look at how this works by implementing a fairly simple scenario, creating a custom stylesheet property to hold the size of the gap between the Label and the TextField in our custom widget. In reality, this is the spacing between the Label and the HBox holding the TextField and the Button. Also, in reality, these are the only two Nodes in the outer HBox so we can control this by changing the Spacing property of the outer HBox.\nThe StyleableProperty # The Spacing property is a Double, so we need a compatible StyleableProperty as a field in InputActionWidget:\nval tagGap: StyleableDoubleProperty = SimpleStyleableDoubleProperty(GAP_META_DATA, this, \u0026#34;tagGap\u0026#34;) Here, GAP_META_DATA is the CssMetaData that will be associated with this property, we haven\u0026rsquo;t defined it yet, but it\u0026rsquo;s next.\nThe next two constructor parameters are \u0026ldquo;Bean\u0026rdquo; stuff. I\u0026rsquo;m not sure how they are used, or if they are used. In this case, this is the InputActionWidget, and \u0026ldquo;tagGap\u0026rdquo; is it\u0026rsquo;s \u0026ldquo;Name\u0026rdquo;.\nThat\u0026rsquo;s it. Now we have a StyleableProperty!\nThe CssMetaData # This is where it get a little bit more complicated. Now, this is one place where Kotlin really, really makes everything super easy, because it has great tools for doing List stuff\u0026hellip;\ncompanion object CssStuff { val TEXTFIELD_FOCUSED: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;textfield-focused\u0026#34;); val GAP_META_DATA: CssMetaData\u0026lt;InputActionWidget3, Number\u0026gt; = object : CssMetaData\u0026lt;InputActionWidget3, Number\u0026gt;(\u0026#34;-wfx-gap\u0026#34;, StyleConverter.getSizeConverter()) { override fun isSettable(styleable: InputActionWidget3) = !styleable.tagGap.isBound override fun getStyleableProperty(styleable: InputActionWidget3) = styleable.tagGap } private val cssMetaDataList = (Region.getClassCssMetaData() + GAP_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getCssMetaData() = getClassCssMetaData() First, we have more than just Pseudo Class stuff in our companion object, so the name has been changed to something more appropriate.\nWe create GAP_META_DATA as a (essentially static) constant of an anonymous inner class extending CssMetaData. That\u0026rsquo;s a mouthful but it\u0026rsquo;s actually really simple.\nThe = object : AbstractClass(){} structure is how an anonymous inner class is made in Kotlin. It essentially says, \u0026ldquo;\u0026hellip; is an object which is an extension of the CssMetaData class\u0026rdquo;. We call the abstract class constructor, then extend it with the stuff in the {}. In this case, we\u0026rsquo;re going to provide implementations for the two abstract methods: isSettable() and getStyleableProperty().\nThe constructor sets the name of the property in the style sheet, it\u0026rsquo;s going to be \u0026ldquo;-wfx-gap\u0026rdquo; (\u0026ldquo;wfx\u0026rdquo; for WidgetsFX and to avoid collisions with the \u0026ldquo;-fx\u0026rdquo; namespace). The second constructor parameter specifies the converter to use to translate the value from the String in the style sheet to a data value in the StyleableProperty. In this case, we\u0026rsquo;re using the standard converter for sizes, which should deal with all the possible ways that it could be expressed in the style sheet - like \u0026ldquo;px\u0026rdquo; or \u0026ldquo;em\u0026rdquo;.\nNext, we have the implementations of the two abstract methods. Our tagGap property is exposed to the client code, so it could be bound to something, which would cause an error if the layout manager attempts to set it.\nThe last implementation is the \u0026ldquo;getter\u0026rdquo; for the property in the CSS metadata. It just returns our property.\nThe last piece is publishing our metadata to the Layout Manager. This is where Kotlin really shines because of the List handling stuff:\nStep 1 is to effectively inherit the metadata from the class that we extended, which is Region. This is a List of some sort. Then we add our metadata with the \u0026ldquo;+\u0026rdquo; operator, generating a new List and we cast it to MutableList because that maintains compatibility with the Java based functions (Kotlin defaults to immutable Lists).\nStep 2 is to override the InputActionWidget.ClassMetaData() function to return our new List. Providing this static function means that if we extend InputActionWidget, the subclass will also inherit our gap metadata.\nStep 3 is to override the member function, getCssMetaData() to delegate to the static method we just created. This is the method that is actually going to be called by the Layout Manager.\nAnd that\u0026rsquo;s it.\nIn Java # This is sufficiently different in Kotlin that it\u0026rsquo;s worth looking at the version from the JavaDocs, which is, of course, in Java:\nprivate static final CssMetaData GAP_META_DATA = new CssMetaData\u0026lt;MyWidget,Number\u0026gt;(\u0026#34;-my-gap\u0026#34;, StyleConverter.getSizeConverter(), 0d) { @Override public boolean isSettable(MyWidget node) { return node.gapProperty == null || !node.gapProperty.isBound(); } @Override public StyleableProperty\u0026lt;Number\u0026gt; getStyleableProperty(MyWidget node) { return (StyleableProperty\u0026lt;Number\u0026gt;)node.gapProperty; } }; private static final List\u0026lt;CssMetaData\u0026lt;? extends Node, ?\u0026gt;\u0026gt; cssMetaDataList; static { List\u0026lt;CssMetaData\u0026lt;? extends Node, ?\u0026gt;\u0026gt; temp = new ArrayList\u0026lt;CssMetaData\u0026lt;? extends Node, ?\u0026gt;\u0026gt;(Region.getClassCssMetaData()); temp.add(GAP_META_DATA); cssMetaDataList = Collections.unmodifiableList(temp); } public static List\u0026lt;CssMetaData\u0026lt;? extends Node, ?\u0026gt;\u0026gt; getClassCssMetaData() { return cssMetaDataList; } @Override public List\u0026lt;CssMetaData\u0026lt;? extends Node, ?\u0026gt;\u0026gt; getCssMetaData() { return getClassCssMetaData(); } One of the reasons that the Java looks more complicated is that all of the generic types of the Lists and methods need to be explicitly declared, while in Kotlin they can be inferred. You can see how the List handling is a lot simpler in Kotlin, too.\nThe StyleSheet # Here\u0026rsquo;s the selector for the InputActionWidget with the gap specified:\n.input-action-widget { -wfx-gap: 8.0; } And that\u0026rsquo;s all it takes. It\u0026rsquo;s not really that complicated, especially when you understand how the mechanism works, and how the steps involved set up the data.\nAdding a Dynamic Layout # One of the reasons that we didn\u0026rsquo;t want to expose the outer HBox in our layout was because it adds a dependency to the implementation of our layout. What if we wanted to give the client code the ability to style the widget with the Label either beside or above the combined TextField/Button?\nThe way this component is designed, we can achieve this by simply swapping out the outer HBox with a VBox. You can\u0026rsquo;t really just swap it though, you have to create new container, configure it and populate it with the other components. Then put the new container in as the only child of the outer Region.\nConveniently, JavaFX has an Enum type called Orientation which has the two values, \u0026ldquo;HORIZONTAL\u0026rdquo; and \u0026ldquo;VERTICAL\u0026rdquo;, and we can use this for out StyleableProperty. Let\u0026rsquo;s take a look at the code we need to implement that:\nval orientation: StyleableObjectProperty\u0026lt;Orientation\u0026gt; = SimpleStyleableObjectProperty(ORIENTATION_META_DATA, this, \u0026#34;orientation\u0026#34;, Orientation.HORIZONTAL) companion object CssStuff { val TEXTFIELD_FOCUSED: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;textfield-focused\u0026#34;); val GAP_META_DATA: CssMetaData\u0026lt;InputActionWidget4, Number\u0026gt; = object : CssMetaData\u0026lt;InputActionWidget4, Number\u0026gt;(\u0026#34;-wfx-gap\u0026#34;, StyleConverter.getSizeConverter(), 6.0) { override fun isSettable(styleable: InputActionWidget4) = !styleable.tagGap.isBound override fun getStyleableProperty(styleable: InputActionWidget4) = styleable.tagGap } val ORIENTATION_META_DATA: CssMetaData\u0026lt;InputActionWidget4, Orientation\u0026gt; = object : CssMetaData\u0026lt;InputActionWidget4, Orientation\u0026gt;(\u0026#34;-wfx-orientation\u0026#34;, EnumConverter(Orientation::class.java)) { override fun isSettable(styleable: InputActionWidget4) = !styleable.orientation.isBound override fun getStyleableProperty(styleable: InputActionWidget4) = styleable.orientation } private val cssMetaDataList = (Region.getClassCssMetaData() + GAP_META_DATA + ORIENTATION_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } That\u0026rsquo;s all it takes. We just add the StyleableProperty as a field, then add the metadata in the companion object and add it to the List along with the gap metadata. Notice that we\u0026rsquo;re using EnumConverter, which is easy enough to find if you poke around in the JavaDocs.\nThe next thing is to incorporate the new property into the layout build:\nprivate fun createLayout() { children.clear() children += if (orientation.value == Orientation.HORIZONTAL) createHBox() else createVBox() } private fun createHBox() = HBox(4.0).apply { children += listOf(label, HBox(0.0, textField, button)) spacingProperty().bind(tagGap) alignment = Pos.CENTER_LEFT minWidth = 200.0 } private fun createVBox() = VBox(4.0).apply { children += listOf(label, HBox(0.0, textField, button)) spacingProperty().bind(tagGap) alignment = Pos.CENTER_LEFT } The configuration of the individual components was separated out from the layout code to keep it cleaner.\nSince the layout build is an \u0026ldquo;action\u0026rdquo;, and orientation is an observable value, we need to trigger the action from a change in the observable value. This means adding a Listener to orientation:\ninit { styleClass += \u0026#34;input-action-widget\u0026#34; configureComponents() createLayout() orientation.addListener(InvalidationListener { createLayout() }) } We can get away with an InvalidationListener because createLayout calls the getter for the value of orientation.\nThis is controlled via the style sheet entry for the widget:\n.input-action-widget { -wfx-gap: 8.0; -wfx-orientation: vertical; } And it looks like this when it\u0026rsquo;s set to vertical:\nThe Label felt like it needed a little bit of styling to make it look more like an integrated part of the widget. So it was made bigger, bold and coloured in the same tone as the focus colour for the style sheet using this entry:\n.input-action-widget .label { -fx-text-fill: derive(-fx-focus-color, -30%); -fx-font-size: 14px; -fx-font-weight: bold } Exposing the Styleable Properties to Client Code # If we want our custom class to work the same way as any other JavaFX Node, then we should give the client code the ability to manipulate the StyleableProperty directly. And this means we should present it in the Bean format that all of the other Node properties use. We just need the following change (here both the StyleableProperties have been updated):\nfun tagGapProperty() = tagGapImpl val tagGapImpl: StyleableDoubleProperty = SimpleStyleableDoubleProperty(GAP_META_DATA, this, \u0026#34;tagGap\u0026#34;) var tagGap: Double get() = tagGapImpl.value set(value) = tagGapImpl.set(value) fun orientationPropery() = orientationImpl val orientationImpl: StyleableObjectProperty\u0026lt;Orientation\u0026gt; = SimpleStyleableObjectProperty(ORIENTATION_META_DATA, this, \u0026#34;orientation\u0026#34;, Orientation.HORIZONTAL) var orientation: Orientation get() = orientationImpl.get() set(value) = orientationImpl.set(value) All we\u0026rsquo;ve done here is change the name of the actual properties to tagGapImpl and orientationImpl and make them private, then set up the usual JavaFX Bean structure for the client code. Now setOrientation(), getOrientation() and orientationProperty() all work as they should from Java code. The same for tagGap.\nFinal Code # That\u0026rsquo;s it! Here\u0026rsquo;s a look at the complete code for our custom InputActionWidget:\nclass InputActionWidget4(private val boundProperty: StringProperty, labelText: String = \u0026#34;Prompt:\u0026#34;, buttonText: String = \u0026#34;Start\u0026#34;) : Region() { private var buttonAction: ButtonRunner = {} private val actionRunning: BooleanProperty = SimpleBooleanProperty(false) private val label = Label(labelText) private val textField = TextField() private val button = Button(buttonText) fun labelTextProperty() = label.textProperty() var labelText: String get() = label.text set(value) { label.text = value } fun buttonTextProperty() = button.textProperty() var buttonText: String get() = button.text set(value) { button.text = value } fun textFieldTextProperty() = textField.textProperty() var textFieldText: String get() = textField.text set(value) { textField.text = value } fun textFormatterProperty() = textField.textFormatterProperty() var textFormatter: TextFormatter\u0026lt;*\u0026gt;? get() = textField.textFormatter set(value) { textField.textFormatter = value } fun tagGapProperty() = tagGapImpl private val tagGapImpl: StyleableDoubleProperty = SimpleStyleableDoubleProperty(GAP_META_DATA, this, \u0026#34;tagGap\u0026#34;) var tagGap: Double get() = tagGapImpl.value set(value) = tagGapImpl.set(value) fun orientationPropery() = orientationImpl private val orientationImpl: StyleableObjectProperty\u0026lt;Orientation\u0026gt; = SimpleStyleableObjectProperty(ORIENTATION_META_DATA, this, \u0026#34;orientation\u0026#34;, Orientation.HORIZONTAL) var orientation: Orientation get() = orientationImpl.get() set(value) = orientationImpl.set(value) companion object CssStuff { val TEXTFIELD_FOCUSED: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;textfield-focused\u0026#34;); val GAP_META_DATA: CssMetaData\u0026lt;InputActionWidget4, Number\u0026gt; = object : CssMetaData\u0026lt;InputActionWidget4, Number\u0026gt;(\u0026#34;-wfx-gap\u0026#34;, StyleConverter.getSizeConverter(), 6.0) { override fun isSettable(styleable: InputActionWidget4) = !styleable.tagGapImpl.isBound override fun getStyleableProperty(styleable: InputActionWidget4) = styleable.tagGapImpl } val ORIENTATION_META_DATA: CssMetaData\u0026lt;InputActionWidget4, Orientation\u0026gt; = object : CssMetaData\u0026lt;InputActionWidget4, Orientation\u0026gt;(\u0026#34;-wfx-orientation\u0026#34;, EnumConverter(Orientation::class.java)) { override fun isSettable(styleable: InputActionWidget4) = !styleable.orientationImpl.isBound override fun getStyleableProperty(styleable: InputActionWidget4) = styleable.orientationImpl } private val cssMetaDataList = (Region.getClassCssMetaData() + GAP_META_DATA + ORIENTATION_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getCssMetaData() = getClassCssMetaData() init { styleClass += \u0026#34;input-action-widget\u0026#34; configureComponents() createLayout() orientationImpl.addListener(InvalidationListener { createLayout() }) } private fun configureComponents() { textFieldTextProperty().bindBidirectional(boundProperty) configureButton() textField.focusedProperty().addListener { _, _, newValue -\u0026gt; button.pseudoClassStateChanged(TEXTFIELD_FOCUSED, newValue); } } private fun createLayout() { children.clear() children += if (orientationImpl.value == Orientation.HORIZONTAL) createHBox() else createVBox() } private fun createHBox() = HBox(4.0).apply { children += listOf(label, HBox(0.0, textField, button)) spacingProperty().bind(tagGapImpl) alignment = Pos.CENTER_LEFT minWidth = 200.0 } private fun createVBox() = VBox(4.0).apply { children += listOf(label, HBox(0.0, textField, button)) spacingProperty().bind(tagGapImpl) alignment = Pos.CENTER_LEFT } private fun configureButton() { button.apply { onAction = EventHandler\u0026lt;ActionEvent\u0026gt; { actionRunning.value = true buttonAction { actionRunning.value = false } } isFocusTraversable = false defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind((textField.textProperty().isEmpty).or(actionRunning)) } } fun setButtonAction(newAction: ButtonRunner) = this.apply { buttonAction = newAction } } The only extra thing in this is that we now have some code to provide access to the TextFormatter property of the TextField.\nHere\u0026rsquo;s the complete set of style sheet entries for this class:\n.input-action-widget { -wfx-gap: 8.0; -wfx-orientation: horizontal; } .input-action-widget .button { -fx-background-radius: 0 7 7 0px, 0 7 7 0px, 0 6 6 0px, 0 5 5 0px; } .input-action-widget .button:textfield-focused { -fx-background-color: -fx-focus-color, -fx-outer-border, -fx-inner-border, -fx-body-color; -fx-background-insets: -0.5, 0.7,1.3, 2; } .input-action-widget .text-input { -fx-background-radius: 3 0 0 3, 2 0 0 2; } .input-action-widget .label { -fx-text-fill: derive(-fx-focus-color, -30%); -fx-font-size: 14px; -fx-font-weight: bold } Conclusion # By my count, this is about 120 lines of code to implement a complete custom control that can be utilized in almost exactly the same as any other standard JavaFX Node. The only thing that\u0026rsquo;s missing is some JavaDocs and you could publish it in a library.\nThe important thing to learn from all of this is that there\u0026rsquo;s really nothing magic or mysterious about creating your own custom Node classes. When you look at it, it\u0026rsquo;s just a simple layout wrapped up in a class to control access to its properties and styling. It\u0026rsquo;s easy to do, and if you have patterns that you use repeatedly in your layouts should really be thinking about implementing them as custom controls.\nI think the only thing involved in this that\u0026rsquo;s a bit challenging to understand are the StyleableProperties and the CssMetaData structure. But even here, it\u0026rsquo;s mostly difficult because the standard documentation is appalling.\n","date":"15 March 2023","externalUrl":null,"permalink":"/javafx/techniques/custom_controls/customizing/","section":"JavaFX","summary":"Once you’ve decided extend Region to create a custom control, there are some basic things you should do to make your new control look professional and unified.  Here’s how to do that.","title":"JavaFX: How to Customize Custom Controls","type":"javafx"},{"content":" Introduction # OK, first off I know I\u0026rsquo;m swimming against the stream here, and at odds with things like this found in some of the tutorial pages from Oracle for JavaFX:\nQuote JavaFX enables you to design with Model-View-Controller (MVC), through the use of FXML and Java. The \u0026ldquo;Model\u0026rdquo; consists of application-specific domain objects, the \u0026ldquo;View\u0026rdquo; consists of FXML, and the \u0026ldquo;Controller\u0026rdquo; is Java code that defines the GUI\u0026rsquo;s behavior for interacting with the user.\nThis is really what we\u0026rsquo;re going to focus on, how this is wrong, and how to do it right.\nI think that if you want a more definitive discussion of MVC, you can\u0026rsquo;t do better than Martin Fowler\u0026rsquo;s Article. It\u0026rsquo;s pretty abstract and can be heavy reading, but he gets it right. Here\u0026rsquo;s his intro to MVC:\nQuote Probably the widest quoted pattern in UI development is Model View Controller (MVC) - it\u0026rsquo;s also the most misquoted. I\u0026rsquo;ve lost count of the times I\u0026rsquo;ve seen something described as MVC which turned out to be nothing like it.\nI think the quote from the Oracle tutorial is a perfect example of the problem he mentions.\nThe Structure of MVC # Before we can go any further, let\u0026rsquo;s look at the parts of MVC and what they do. First, here\u0026rsquo;s a diagram from somewhere out in the web:\nThis diagram shows a particular use case (user adds something to a \u0026ldquo;cart\u0026rdquo;), but has the essentials down.\nView The View is the GUI. It\u0026rsquo;s the only part of the framework that the user interacts with. Controller The Controller\u0026rsquo;s job is to turn user input into actions. It also is the part that interacts with the other GUI parts of your application. Model The Model is the data and the business logic. Note that the \u0026ldquo;data\u0026rdquo; comes in two parts: Presentation Data and Domain Data. The Model is the only component that deals with Domain Data, and a big part of its job is to keep Domain Data away from the View. What the View Does # The View is layout, but it\u0026rsquo;s also much more than that in MVC. It\u0026rsquo;s an independent element that handles all the user interaction.\nLet\u0026rsquo;s take a look at some of the things that the View is solely responsible for:\nLayout Styling and configuration Connections between layout elements Animations Data binding to the Presentation Model Detecting user interactions such as mouse clicks and keystrokes Enabling and disabling layout elements Triggering actions Responding to changes in the Presentation Model I don\u0026rsquo;t think that there\u0026rsquo;s anything controversial about this list. All of these items are clearly the responsibility of the View in an MVC framework.\nIf I was building an application without FXML, these are all things that I\u0026rsquo;d put into my View or my ViewBuilder (which creates the View). For every element of the layout, I\u0026rsquo;d configure and style it and I\u0026rsquo;d bind it to the Presentation Model if necessary. Any user interactions would be defined and I\u0026rsquo;d create EventHandlers for Buttons, Toggles and other things might generate Events.\nMost importantly though, no other element of the framework would have any knowledge of the inner structure of the View. There\u0026rsquo;s zero dependencies on any aspect of the implementation of the View. It\u0026rsquo;s a \u0026ldquo;black box\u0026rdquo; to the rest of the framework.\nNow let\u0026rsquo;s look at how we\u0026rsquo;d do it with FXML\u0026hellip;\nWe can do the three two items with the FXML file, but that\u0026rsquo;s about it. The rest of the responsibilities have to be handled by the FXML Controller.\nBut if the FMXL Controller is doing things like animations and binding Node value properties to the Presentation Model - things that are absolutely the sole responsibility of the View, then what role is the FXML Controller really playing?\nIt\u0026rsquo;s pretty obvious, when you look at it this way, that the FXML Controller is actually part of the View.\nBack to That Oracle Quote # I\u0026rsquo;ll repeat it again here:\nQuote JavaFX enables you to design with Model-View-Controller (MVC), through the use of FXML and Java. The \u0026ldquo;Model\u0026rdquo; consists of application-specific domain objects, the \u0026ldquo;View\u0026rdquo; consists of FXML, and the \u0026ldquo;Controller\u0026rdquo; is Java code that defines the GUI\u0026rsquo;s behavior for interacting with the user.\nThe big problem that we have here are their definitions of the parts of MVC. First:\nQuote The \u0026ldquo;Model\u0026rdquo; consists of application-specific domain objects\nWell, yes, the Model does contain domain objects, but it also contains the Presentation Model and the application logic.\nQuote the \u0026ldquo;View\u0026rdquo; consists of FXML\nExcept that the FXML file simply describes the layout, styling and some configuration. This is not a View, this just layout.\nQuote and the \u0026ldquo;Controller\u0026rdquo; is Java code that defines the GUI\u0026rsquo;s behavior for interacting with the user\nThis is just dead wrong. That\u0026rsquo;s not role of the Controller in MVC, but it is the role of the FXML Controller as part of the View.\nSeparation of Concerns # Every time I ask someone what they think the value is of FXML (aside from using SceneBuilder) they\u0026rsquo;ll inevitably respond with:\nQuote It ensures separation of concerns.\nIt must be out there in some popular tutorial somewhere.\nWell, to be sure, separation of concerns is an awesome concept. Where I came from we called it \u0026ldquo;loose coupling\u0026rdquo;, but it\u0026rsquo;s all the same, really. We should all strive to have loose coupling between the components of our applications. It\u0026rsquo;s main purpose of using a framework like MVC - to keep the business logic far, far away from the GUI and visa versa.\nQuestion But does FXML give you \u0026ldquo;separation of concerns?\u0026rdquo;\nWell, it does split the layout from the other parts of the View. So that\u0026rsquo;s something.\nBut if you treat the FXML Controller as an MVC Controller, then you immediately start to squash all of those concerns together again. This is because the FXML Controller is full of View stuff, and the MVC Controller is supposed to be full of action stuff. Now you have your View stuff sitting in the same place as your action stuff and it very quickly gets hopelessly entangled.\nAnd then it gets worse.\nIt\u0026rsquo;s that \u0026ldquo;The Model consists of domain objects\u0026rdquo; bit from the Oracle tutorial. Many FXML programmers think that they\u0026rsquo;ve made a \u0026ldquo;Model\u0026rdquo; when they\u0026rsquo;ve created a few POJO domain objects. So they\u0026rsquo;ve created a \u0026ldquo;Customer\u0026rdquo; object, and maybe an \u0026ldquo;Order\u0026rdquo; object and these will hold the data that\u0026rsquo;s retrieved from the database, and that\u0026rsquo;s it - they\u0026rsquo;re done - Model created.\nQuestion But where does the application logic go??????\nAnd, you know, there\u0026rsquo;s only one place to put it. The FXML Controller!\nAnd what about the Presentation Model? It becomes fields in the FXML Controller.\nAnd where do those domain objects get stored? Once again\u0026hellip;as fields in the FXML Controller.\nSo off they go, and now there\u0026rsquo;s database access code sitting in the FXML Controller - probably in some code that defines an EventHandler for a Button.\nYou can\u0026rsquo;t get more coupled than that.\nThe result is that they\u0026rsquo;ve created a monolithic, single-class application with everything muddled together.\nImportant This isn\u0026rsquo;t an exaggeration, virtually every example of an FXML application that you\u0026rsquo;ll find out there on the internet does exactly this.\nBut there\u0026rsquo;s separation of concerns! The layout is tucked away in the FXML file!\nHow It Should Look # The key thing to understand is that the FXML and the FXML Controller, together with the FXML Loader, create the View. It\u0026rsquo;s that simple.\nYou\u0026rsquo;re on your own to create the Controller and the Model, and you need to understand the framework if you\u0026rsquo;re going to be able to do it.\nLet\u0026rsquo;s take a look at a simple MVC application and see how the parts work together. I think that FXML complicates the issue at the start, so I\u0026rsquo;ll just create a Region subclass to be my View, and then we\u0026rsquo;ll look at how you\u0026rsquo;d implement FXML later on.\nLet\u0026rsquo;s look at the View first:\npublic class View extends VBox { private final Model.PresentationModel viewModel; private final BooleanProperty showProgress = new SimpleBooleanProperty(false); private final Consumer\u0026lt;Runnable\u0026gt; actionHandler; public View(Model.PresentationModel viewModel, Consumer\u0026lt;Runnable\u0026gt; actionHandler) { this.viewModel = viewModel; this.actionHandler = actionHandler; initializeLayout(); } private void initializeLayout() { getChildren().addAll(createTopBox(), createButton()); setPadding(new Insets(30)); } private Node createButton() { Button button = new Button(\u0026#34;Start\u0026#34;); button.setOnAction(evt -\u0026gt; { showProgress.set(true); button.setDisable(true); actionHandler.accept(() -\u0026gt; { showProgress.set(false); button.setDisable(false); }); }); return button; } private Region createTopBox() { StackPane results = new StackPane(); results.getChildren().addAll(createDataBox(), createProgressIndicator()); return results; } private Node createDataBox() { Label prompt = new Label(\u0026#34;Number of Cycles:\u0026#34;); prompt.getStyleClass().add(\u0026#34;label-text\u0026#34;); HBox inputBox = new HBox(6, prompt, createTextField()); inputBox.setAlignment(Pos.CENTER); VBox results = new VBox(20, inputBox, createDataLabel()); results.setAlignment(Pos.CENTER); results.visibleProperty().bind(showProgress.not()); return results; } @NotNull private Label createDataLabel() { Label dataLabel = new Label(); dataLabel.textProperty().bind(viewModel.theResultProperty()); dataLabel.getStyleClass().add(\u0026#34;data-text\u0026#34;); return dataLabel; } private Node createTextField() { TextField textField = new TextField(); TextFormatter\u0026lt;Long\u0026gt; textFormatter = new TextFormatter\u0026lt;\u0026gt;(new LongStringConverter()); textField.setTextFormatter(textFormatter); textField.setMaxWidth(120.0); textFormatter.valueProperty().bindBidirectional(viewModel.cycleCountProperty()); return textField; } private Node createProgressIndicator() { ProgressIndicator progressIndicator = new ProgressIndicator(); progressIndicator.progressProperty().bind(viewModel.progressProperty()); progressIndicator.setMinSize(200, 200); progressIndicator.visibleProperty().bind(showProgress); progressIndicator.visibleProperty().addListener(observable -\u0026gt; { if (progressIndicator.isVisible()) { Transition transition = new Transition() { { setCycleDuration(Duration.millis(2000)); } @Override protected void interpolate(double v) { progressIndicator.setOpacity(v); } }; transition.play(); } }); return progressIndicator; } } It\u0026rsquo;s just a VBox with a StackPane above a Button. In the StackPane are a ProgressIndicator and HBox holding a Label and a TextField. Either the ProgressIndicator or the HBox is visible at any given time, but never both at the same time.\nNow, let\u0026rsquo;s look at the Model:\npublic class Model { private final PresentationModel presentationModel = new PresentationModel(); private String domainObject = \u0026#34;Nothing Yet\u0026#34;; public Model() { presentationModel.setTheResult(domainObject); } void doSomethingComplicated(BiConsumer\u0026lt;Long, Long\u0026gt; progressUpdater) { for (long idx = 0; idx \u0026lt; presentationModel.getCycleCount(); idx++) { try { Thread.sleep(1000); } catch (InterruptedException e) { throw new RuntimeException(e); } progressUpdater.accept(idx, presentationModel.getCycleCount()); } domainObject = \u0026#34;Something was found\u0026#34;; } void integrateComplicatedResults() { presentationModel.setTheResult(domainObject); } public PresentationModel getPresentationModel() { return presentationModel; } public static class PresentationModel { private final DoubleProperty progress = new SimpleDoubleProperty(0.0); private final ObjectProperty\u0026lt;Long\u0026gt; cycleCount = new SimpleObjectProperty\u0026lt;\u0026gt;(5L); private final StringProperty theResult = new SimpleStringProperty(\u0026#34;\u0026#34;); public double getProgress() { return progress.get(); } public DoubleProperty progressProperty() { return progress; } public void setProgress(double progress) { this.progress.set(progress); } public long getCycleCount() { return cycleCount.get(); } public ObjectProperty\u0026lt;Long\u0026gt; cycleCountProperty() { return cycleCount; } public void setCycleCount(long cycleCount) { this.cycleCount.set(cycleCount); } public String getTheResult() { return theResult.get(); } public StringProperty theResultProperty() { return theResult; } public void setTheResult(String theResult) { this.theResult.set(theResult); } } } And the Controller:\npublic class Controller { private final View view; private Model model = new Model(); public Controller(long initialCycleCount) { model.getPresentationModel().setCycleCount(initialCycleCount); view = new View(model.getPresentationModel(), this::startSomethingBig); } private void startSomethingBig(Runnable postRunGUIAction) { Task\u0026lt;Void\u0026gt; bigTask = new Task\u0026lt;Void\u0026gt;() { @Override protected Void call() { model.doSomethingComplicated(this::updateProgress); return null; } }; bigTask.setOnSucceeded(evt -\u0026gt; { model.getPresentationModel().progressProperty().unbind(); model.integrateComplicatedResults(); postRunGUIAction.run(); }); Thread bigTaskThread = new Thread(bigTask); model.getPresentationModel().progressProperty().bind(bigTask.progressProperty()); bigTaskThread.start(); } public View getView() { return view; } } Here\u0026rsquo;s the Main class, and you can see how it interacts with the Controller to get things running:\npublic class Main extends Application { @Override public void start(Stage stage) throws Exception { Scene scene = new Scene(new Controller(6L).getView()); scene.getStylesheets().add(getClass().getResource(\u0026#34;/css/default.css\u0026#34;).toExternalForm()); stage.setScene(scene); stage.show(); } } When it runs, it looks like this at first:\nWhen the \u0026ldquo;complicated process\u0026rdquo; is running, it looks like this:\nAnd when it\u0026rsquo;s all done, it looks like this:\nThe View class is clearly doing all of the View stuff and nothing but the View stuff. It handles the layout, the View related Button interaction, styling, the configuration of the TextField and binding to the Presentation Model. I even put a fade-in transition on the ProgressIndicator so that you could see how it\u0026rsquo;s all handled by the View.\nThe View reveals nothing of its inner workings to any of the other components.\nThe View has only two dependencies, and they are clearly declared in its constructor: it needs the Presentation Model, and the action handler for the Button (or whatever triggers the action). It takes a Consumer\u0026lt;Runnable\u0026gt; for this action handler, because it needs to perform the cleanup of the GUI after the action has completed. Note that there\u0026rsquo;s nothing in this action handler that requires the GUI element to be a Button, or even something that generates an ActionEvent. It could be a mouse-over or a keystroke, for example.\nThe Model is very specifically designed and named in this example to make the intent of the structure clear. The Presentation Data is split out into an enclosed class called Model.PresentationModel, and instantiated into a field called presentationModel. There\u0026rsquo;s a field called domainData, to make a point about how domain data can be handled.\nHowever, the PresentationModel is split out so that it can be passed to the View without the need to expose all of the other public methods of the Model to the View. In this way, the View is ignorant of the Model itself, and only has knowledge of the PresentationModel on which it depends.\nThe Controller does just what it is supposed to do, it turns the user interaction into an action. That\u0026rsquo;s the Controller.startSomethingBig() method. It handles the threading, and provides the connection back to the View (through the Presentation Model) to handle the progress monitoring of the process.\nThere\u0026rsquo;s also a constructor parameter for the Controller, to illustrate how the other parts of the GUI would interact with each other through their Controllers. In this case, it\u0026rsquo;s just the Application class itself, but you get the idea.\nOne Small Issue # If you are being technically nit-picky (and there\u0026rsquo;s nothing wrong with that), you\u0026rsquo;ll notice that this isn\u0026rsquo;t quite MVC.\nIn MVC, the View is allowed to read from the Presentation Model directly but it\u0026rsquo;s not allowed to update it directly. In this example, we\u0026rsquo;re using bi-directional binding to connect the TextField value to the Presentation Model. That means that anything typed in by the user, immediately updates the Presentation Model without the intervention of the Controller.\nHonestly, I don\u0026rsquo;t know a way around this that isn\u0026rsquo;t really clumsy. That quote from Martin Fowler\u0026rsquo;s website continues with:\nQuote Frankly a lot of the reason for this is that parts of classic MVC don\u0026rsquo;t really make sense for rich clients these days.\nMVC does seem to be aware of binding-like capabilities, but it predates the JavaFX Reactive library of Observables, Properties and Bindings by decades. So, I think that in this respect we\u0026rsquo;ll have to accept that JavaFX works best with something that\u0026rsquo;s close to MVC, but not quite MVC.\nIn any case, nothing about the structure of this example, using the bi-directional binding, compromises the fundamental goal of MVC - which is to provide for loose coupling between the View and the application logic. So I think we\u0026rsquo;re still good here.\nCleaning This Up a Bit # This example was very deliberately designed to point out the MVC structure. There\u0026rsquo;s a class called Controller, another called Model and one called View. Model has an encapsulated class called PresentationModel and a field called domainData so that you can see how Presentation Data is different from the Domain Data.\nBut there\u0026rsquo;s a problem with having a View that extends VBox. There\u0026rsquo;s nothing stopping the Controller from defining its view field as VBox, and then, if we were to change the structure of the View class, we\u0026rsquo;d have to modify the Controller class as well.\nIt\u0026rsquo;s far better to have \u0026ldquo;Builder\u0026rdquo; for the View that returns something very generic like Region. It wasn\u0026rsquo;t done this way originally, because then we\u0026rsquo;d have a class called ViewBuilder instead of View. But now you\u0026rsquo;ve seen it the first way, we can refactor this. Here\u0026rsquo;s the ViewBuilder:\npublic class ViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final Model.PresentationModel viewModel; private final BooleanProperty showProgress = new SimpleBooleanProperty(false); private final Consumer\u0026lt;Runnable\u0026gt; actionHandler; public ViewBuilder(Model.PresentationModel viewModel, Consumer\u0026lt;Runnable\u0026gt; actionHandler) { this.viewModel = viewModel; this.actionHandler = actionHandler; } public Region build() { VBox results = new VBox(10); results.getChildren().addAll(createTopBox(), createButton()); results.setPadding(new Insets(30)); return results; } private Node createButton() { Button button = new Button(\u0026#34;Start\u0026#34;); button.setOnAction(evt -\u0026gt; { showProgress.set(true); button.setDisable(true); actionHandler.accept(() -\u0026gt; { showProgress.set(false); button.setDisable(false); }); }); return button; } private Region createTopBox() { StackPane results = new StackPane(); results.getChildren().addAll(createDataBox(), createProgressIndicator()); return results; } private Node createDataBox() { Label prompt = new Label(\u0026#34;Number of Cycles:\u0026#34;); prompt.getStyleClass().add(\u0026#34;label-text\u0026#34;); HBox inputBox = new HBox(6, prompt, createTextField()); inputBox.setAlignment(Pos.CENTER); VBox results = new VBox(20, inputBox, createDataLabel()); results.setAlignment(Pos.CENTER); results.visibleProperty().bind(showProgress.not()); return results; } @NotNull private Label createDataLabel() { Label dataLabel = new Label(); dataLabel.textProperty().bind(viewModel.theResultProperty()); dataLabel.getStyleClass().add(\u0026#34;data-text\u0026#34;); return dataLabel; } private Node createTextField() { TextField textField = new TextField(); TextFormatter\u0026lt;Long\u0026gt; textFormatter = new TextFormatter\u0026lt;\u0026gt;(new LongStringConverter()); textField.setTextFormatter(textFormatter); textField.setMaxWidth(120.0); textFormatter.valueProperty().bindBidirectional(viewModel.cycleCountProperty()); return textField; } private Node createProgressIndicator() { ProgressIndicator progressIndicator = new ProgressIndicator(); progressIndicator.progressProperty().bind(viewModel.progressProperty()); progressIndicator.setMinSize(200, 200); progressIndicator.visibleProperty().bind(showProgress); progressIndicator.visibleProperty().addListener(observable -\u0026gt; { if (progressIndicator.isVisible()) { Transition transition = new Transition() { { setCycleDuration(Duration.millis(2000)); } @Override protected void interpolate(double v) { progressIndicator.setOpacity(v); } }; transition.play(); } }); return progressIndicator; } } The changes here are that instead of extending VBox this class now implements Builder\u0026lt;Region\u0026gt; and the initializeLayout() method has been renamed to build(), made public and returns Region. It\u0026rsquo;s also no longer called from the constructor. Everything else in the class remains exactly the same.\nNow the new Controller class:\npublic class Controller { private final ViewBuilder viewBuilder; private Model model = new Model(); public Controller(long initialCycleCount) { model.getPresentationModel().setCycleCount(initialCycleCount); viewBuilder = new ViewBuilder(model.getPresentationModel(), this::startSomethingBig); } private void startSomethingBig(Runnable postRunGUIAction) { Task\u0026lt;Void\u0026gt; bigTask = new Task\u0026lt;Void\u0026gt;() { @Override protected Void call() { model.doSomethingComplicated(this::updateProgress); return null; } }; bigTask.setOnSucceeded(evt -\u0026gt; { model.getPresentationModel().progressProperty().unbind(); model.integrateComplicatedResults(); postRunGUIAction.run(); }); Thread bigTaskThread = new Thread(bigTask); model.getPresentationModel().progressProperty().bind(bigTask.progressProperty()); bigTaskThread.start(); } public Region getView() { return viewBuilder.build(); } } The main change here is that Controller.getView() now delegates to ViewBuilder.build().\nImplementing FXML # At this point it should be clear that the nature of the View has been completely isolated from the Controller and the Model, and we can implement it any way we want - just so long as we can use the PresentationModel and the actionHandler that come from the Controller.\nThis means we could even implement it as FXML! So that\u0026rsquo;s what we\u0026rsquo;re going to do.\nI\u0026rsquo;m not going to invest a lot of time and effort into creating a cool design that has all of the functionality of the original View in FXML. We just need something that will work. I\u0026rsquo;m also not installing SceneBuilder, so I\u0026rsquo;m hand creating a simple FXML and it\u0026rsquo;s going to be absolutely minimal.\nThe FXML File:\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;?import javafx.scene.control.Button?\u0026gt; \u0026lt;?import javafx.scene.layout.BorderPane?\u0026gt; \u0026lt;BorderPane fx:id=\u0026#34;borderPane\u0026#34; xmlns=\u0026#34;http://javafx.com/javafx/19\u0026#34; xmlns:fx=\u0026#34;http://javafx.com/fxml/1\u0026#34;\u0026gt; \u0026lt;center\u0026gt; \u0026lt;Button fx:id=\u0026#34;button\u0026#34;\u0026gt;\u0026lt;/Button\u0026gt; \u0026lt;/center\u0026gt; \u0026lt;/BorderPane\u0026gt; Pretty simple, just a button in a BorderPane. I\u0026rsquo;m not doing any styling here either, it\u0026rsquo;s pure layout. We have fx:id for both the BorderPane and the Button, so we\u0026rsquo;ll do the rest in the FXML Controller:\npublic class FxmlController implements Initializable { private final Model.PresentationModel viewModel; private final Consumer\u0026lt;Runnable\u0026gt; actionHandler; @FXML BorderPane borderPane; @FXML Button button; public FxmlController(Model.PresentationModel viewModel, Consumer\u0026lt;Runnable\u0026gt; actionHandler) { this.viewModel = viewModel; this.actionHandler = actionHandler; } @Override public void initialize(URL url, ResourceBundle resourceBundle) { button.textProperty().bind(viewModel.theResultProperty()); borderPane.setPadding(new Insets(20)); borderPane.setMinWidth(200); button.setOnAction(evt -\u0026gt; { button.setDisable(true); actionHandler.accept(() -\u0026gt; { button.setDisable(false); }); }); } } The only component of the MVC structure that we need to change is the ViewBuilder:\npublic class ViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final Model.PresentationModel viewModel; private final Consumer\u0026lt;Runnable\u0026gt; actionHandler; public ViewBuilder(Model.PresentationModel viewModel, Consumer\u0026lt;Runnable\u0026gt; actionHandler) { this.viewModel = viewModel; this.actionHandler = actionHandler; } @Override public Region build() { FXMLLoader loader = new FXMLLoader(ViewBuilder.class.getResource(\u0026#34;/fxml/view.fxml\u0026#34;)); loader.setController(new FxmlController(viewModel, actionHandler)); try { return loader.load(); } catch (IOException e) { return new BorderPane(); } } } When it starts up, it looks like this:\nWhile the process is running, it looks like this:\nAnd ends up like this:\nKey Take-Aways # First, and most importantly, is that the FXML based View is way different from the hand coded View, but NOTHING needed to be changed in the Controller or the Model. The coupling is completely defined by the constructor parameters of the ViewBuilder, and those did not change.\nImportant This is the key thing for you to understand because it is the entire point of MVC.\nSecondly, the FXML based View doesn\u0026rsquo;t display or allow the user to change the cycle count, nor does it use the progress property. This is OK. There\u0026rsquo;s no rule that says that every possible View has to use the entire data set in the Presentation Model. Perhaps we just want a minimalist widget to stick in the corner of a screen - it\u0026rsquo;s a valid implementation of the View component.\nThird, just as with the hand coded View, there\u0026rsquo;s no need, or even temptation, to put calls to external services or API\u0026rsquo;s in the View code. This FXML Controller exists solely to work with the FXML file to create the View. Nothing more.\nFinally, the fact that this View is FXML is utterly irrelevant to the rest of the MVC framework. It doesn\u0026rsquo;t change the structure of the framework, and it doesn\u0026rsquo;t change the approach.\nConclusion # I think this exercise shows very clearly that FXML + FXMLController = MVC View, if you want to implement MVC. Nothing else makes sense.\nIt also shows that you can, if you want implement your View with FXML + FXMLController, and it will work just fine. But you need to consider both the FXML and the FXMLController as a tightly coupled, integrated pair. Once again, nothing else make sense.\nIn this example, the FXML version of the View was extremely simple. What you will find with a more complex layout that uses more elements of the Presentation Model is that the artificial separation of pure layout from layout logic into two components becomes more of a headache than it\u0026rsquo;s worth. Putting them back together into a coded layout can actually be simpler.\nTo me, though, it\u0026rsquo;s the fact that you can simply swap out the View Builder with a version that calls FXMLLoader and not touch a stitch of code anywhere else in the framework that tells you everything you need to know.\nAnd that\u0026rsquo;s how you achieve \u0026ldquo;separation of concerns\u0026rdquo; with FXML.\n","date":"20 February 2023","externalUrl":null,"permalink":"/javafx/techniques/nofxml/fxml_isnt_mvc/","section":"JavaFX","summary":"Lots of programmers and websites think that FXML instantly gives them a step up on Model-View-Controller, with the FXML File yielding the View and the FXML Controller acting as the Controller.  We’ll look at why this isn’t correct, and how you should implement FXML in the context of MVC.","title":"FXML is NOT Model-View-Controller","type":"javafx"},{"content":" Introduction # Bindings are the \u0026ldquo;meat and potatoes\u0026rdquo; of Reactive JavaFX application design. They are the basic mechanism that allows you to link your View to a Presentation Model so that changes in the View are instantly reflected in your data and visa versa.\nThere are three techniques to create Bindings in JavaFX:\nThe \u0026ldquo;Fluent\u0026rdquo; API This is a set of methods included in interfaces like, NumberBinding which allow ObservableValues to be modified and transformed in a decorator style. Create a Custom Binding This involves extending one of the abstract Binding classes in the JavaFX library. You can read about it here. Using the Methods in the Bindings Class This is what this article is all about. Read on to find out more\u0026hellip; First, Some Nomenclature: # This can get a little bit confusing. The word \u0026ldquo;bindings\u0026rdquo; is used in so many different ways here, that we need to make sure that everyone understands what that word means when it\u0026rsquo;s used in this article:\n\u0026ldquo;Binding\u0026rdquo; the Action We\u0026rsquo;re going to talk a lot about binding things together. It will always be used in lower case (unless it\u0026rsquo;s the first word in a sentence), and will always be used like a verb. \u0026ldquo;Binding\u0026rdquo; the Result When you bind to properties together, the thing that joins them is a binding. As long as we\u0026rsquo;re not talking about a specific class of Binding I\u0026rsquo;ll refer to it as \u0026ldquo;a binding\u0026rdquo;, \u0026ldquo;the binding\u0026rdquo; or \u0026ldquo;the bindings\u0026rdquo;. I\u0026rsquo;ll use lower case and no fancy formatting. \u0026ldquo;Binding\u0026rdquo; the Classes There are a number of binding classes. These are things like IntegerBinding, BooleanBinding and so on. These specific classes are pretty clear, but when talking about them in general I\u0026rsquo;ll be consistent in referring to them as,\u0026ldquo;Binding\u0026rdquo;, \u0026ldquo;a Binding\u0026rdquo; or, \u0026ldquo;the Binding\u0026rdquo;, \u0026ldquo;the Bindings\u0026rdquo; so that you\u0026rsquo;ll were talking in general about an example of one of those classes. \u0026ldquo;Bindings\u0026quot;the Library The subject of this article. It will always be referred to as Bindings, or perhaps \u0026ldquo;the Bindings class\u0026rdquo;. Understand Custom Bindings First # The best way to understand what bindings are and how they work is to look at how to create custom Binding classes. I strongly recommend that you read my tutorial on Custom Bindings.\nWhat is the \u0026ldquo;Bindings\u0026rdquo; Class? # The Bindings class is a utility library consisting of around 200 static methods that create or manipulate Bindings. I\u0026rsquo;ve made an effort divide them up into a number of different types, and we\u0026rsquo;ll look at these with examples of how they can be used. Once you understand what\u0026rsquo;s in this library, you\u0026rsquo;ll probably never need to create a Custom Binding again, unless you\u0026rsquo;re doing something really off beat.\nThe \u0026ldquo;Create\u0026rdquo; Methods # Possibly the most useful methods are the \u0026ldquo;Create\u0026rdquo; methods, and there are 7 of them. One for each of the most common Property types you might use. So we have createBooleanBinding(), createStringBinding and so on. Let\u0026rsquo;s look at createStringBinding() as an example. The method signature looks like this:\nstatic StringBinding createStringBinding​(Callable\u0026lt;String\u0026gt; func, Observable... dependencies) Obviously, it\u0026rsquo;s a static method and it returns a StringBinding. The first parameter is a Callable that returns a String.\nWhat\u0026rsquo;s Callable?\nCallable is a Functional Interface which is virtually identical to Supplier, except that it\u0026rsquo;s single method is call() instead of get(). It\u0026rsquo;s a method that takes no parameters and returns a value - in this case a String. Since it\u0026rsquo;s a Functional Interface, it can be implemented via a lambda function or a method reference. Unfortunately, they chose to call this parameter func, which is really confusing for beginners. It\u0026rsquo;s not a Function.\nNow it\u0026rsquo;s a parameter to createStringBinding(), which means that you have to create a Callable\u0026lt;String\u0026gt; and pass it to this method. Before we look at this more closely, we\u0026rsquo;ll take a look at the remaining parameters.\nThe next parameter is Observable..., which means that it\u0026rsquo;s a vararg, and you can pass it as many arguments as you like, just so long as they are all Observable types. It calls these parameters, dependencies, which is a good clue as to what they do.\nAt this point, I\u0026rsquo;m going to suggest that you look at my article on custom Bindings one last time, because the rest of this explanation relies on it.\nWith a custom Binding, you need to specify two things. The first is an implementation of computeValue(), which is a method which takes no parameters and returns a value - and for a custom StringBinding, that would be a String.\nYou can probably see that computeValue() just about fits the definition of how Callable\u0026lt;String\u0026gt; works. And that\u0026rsquo;s exactly what it is. However you define that Callable\u0026lt;String\u0026gt;, it\u0026rsquo;s going to function exactly the same way that computeValue() would in a Custom Binding.\nThe second thing you need to do with a custom Binding is to make a call to super.bind() in its constructor. This establishes the dependencies for the Binding. And you\u0026rsquo;ll notice that this second parameter just happens to be called, \u0026ldquo;dependencies\u0026rdquo;. What a coincidence!\nNow you can see that createStringBinding() is really just a short-hand way to create a custom Binding in a single line of code. I find that I very rarely create custom Binding classes manually, especially as anonymous inner classes, because it\u0026rsquo;s just so much easier to use one of these Bindings methods. It looks cleaner too.\nEvery suggestion I make about custom Bindings holds true for any Bindings created via these methods. Make sure that you call the get() method of each of the dependencies in your func parameter, just as you would in computeValue()\nAn Example # public class BindingsCLassDemo1 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 620, 340); stage.setScene(scene); stage.show(); } private int divisor = 1; private Region createContent() { ObjectProperty\u0026lt;Integer\u0026gt; number1 = new SimpleObjectProperty\u0026lt;\u0026gt;(100); ObjectProperty\u0026lt;Integer\u0026gt; number2 = new SimpleObjectProperty\u0026lt;\u0026gt;(30); BooleanProperty evensOnly = new SimpleBooleanProperty(false); Label label1 = new Label(); label1.textProperty().bind(number1.asString()); Label label2 = new Label(); label2.textProperty().bind(number2.asString()); Label results = new Label(); results.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; calculateAmountAsString(number1, number2, evensOnly), number1, number2, evensOnly)); HBox labelBox = new HBox(10, new Label(\u0026#34;Number 1:\u0026#34;), label1, new Label(\u0026#34;Number2: \u0026#34;), label2, new Label(\u0026#34;Results: \u0026#34;), results); Button button1 = new Button(\u0026#34;Increase Value 1\u0026#34;); button1.setOnAction(evt -\u0026gt; number1.set(number1.get() + 1)); Button button2 = new Button(\u0026#34;Increase Value 2\u0026#34;); button2.setOnAction(evt -\u0026gt; number2.set(number2.get() + 1)); CheckBox checkBox = new CheckBox(\u0026#34;Evens Only\u0026#34;); evensOnly.bind(checkBox.selectedProperty()); HBox controlBox = new HBox(5, button1, button2, checkBox); VBox vBox = new VBox(40, labelBox, controlBox); vBox.setPadding(new Insets(30)); return vBox; } private String calculateAmountAsString(ObservableObjectValue\u0026lt;Integer\u0026gt; property1, ObservableObjectValue\u0026lt;Integer\u0026gt; property2, ObservableBooleanValue evenOnly) { int result = (property1.get() + property2.get()) / divisor; System.out.println(\u0026#34;computing \u0026#34; + property1.get() + \u0026#34; \u0026#34; + property2.get() + \u0026#34; divisor: \u0026#34; + divisor + \u0026#34; result: \u0026#34; + result); divisor++; if (evenOnly.get()) { return Integer.toString(((result % 2) == 0) ? result : 0); } return Integer.toString(result); } } This is a bit goofy, and it certainly doesn\u0026rsquo;t do anything too useful, but it does demonstrate all the key points you need to understand about the create methods in Bindings. When it runs, it looks like this:\nand the console output looks like this:\ncomputing 100 30 divisor: 1 result: 130 computing 100 30 divisor: 2 result: 65 computing 101 30 divisor: 3 result: 43 computing 102 30 divisor: 4 result: 33 computing 102 31 divisor: 5 result: 26 computing 102 32 divisor: 6 result: 22 computing 102 33 divisor: 7 result: 19 computing 102 33 divisor: 8 result: 16 computing 103 33 divisor: 9 result: 15 computing 104 33 divisor: 10 result: 13 computing 105 33 divisor: 11 result: 12 This example is very, very similar to the first example from the Custom Binding class tutorial, and the results should be identical to that example.\nWhich Method to Use # Here we use Bindings.createStringBinding() because we are going to bind the results to the Text property of a Label, which needs a StringBinding. This is useful here because is shows how we can handle the conversion from numbers to the correct type for the application right in the method call.\nWith the custom Binding example of this, we left the Binding as an IntegerBinding and then use the Fluent API method asString() to convert it to a StringBinding. This made sense as the custom Binding was dealing with numbers and, as a stand-alone class, there isn\u0026rsquo;t any context to suggest that the result should be anything other than some sort of numeric Binding.\nIn this case though, we\u0026rsquo;re calling the Bindings method as part of the .bind() method call, meaning that the context is well defined. We need a StringBinding, so we should use a method that returns that.\nHow it Works # The key point to remember here is that, just like with computeValue(), the code in the Callable parameter func is dealing with concrete data classes, not Property classes. When it does deal with Property classes, it\u0026rsquo;s just to extract the current value of the data that they contain via their get() or getValue() methods. And the Callable returns a concrete data class, not a Property or Observable.\nThis makes the code simpler, but it\u0026rsquo;s also counter-intuitive and trips up a lot of beginners.\nYou should follow the general rules for using lambdas in any place. Readability drops dramatically when they have more than one line. So stick to expression lambdas (instead of statement lambdas), and call methods when your lambda gets too big. That\u0026rsquo;s what\u0026rsquo;s happening here, so we\u0026rsquo;ve got a separate method to do the calculation.\nThis should underscore how close this is to a custom Binding: To create this code, I just copy/pasted the computeValue() code from the custom Binding example I already had and renamed it to calculateAmountAsString(), and then inserted the parameter list from the custom Binding\u0026rsquo;s constructor. Some small modifications to add calls to Integer.toString() and it was all done.\nHow Important Are These Methods # These 7 methods can do just about anything you need to do. The rest of the methods described in this tutorial\u0026hellip; I don\u0026rsquo;t use them too much myself. For me, it\u0026rsquo;s often just as clear to use create{type}Binding(), as it is to use any of the other methods in this library, and there\u0026rsquo;s a little bit of extra consistency.\nLet\u0026rsquo;s look at an example:\nIntegerBinding binding = Bindings.createIntegerBinding(() -\u0026gt; propertyA.get() + propertyB.get(), propertyA, propertyB); That\u0026rsquo;s pretty clear. The two values are just added together. The alternative is:\nNumberBinding binding = Bindings.add(propertyA, propertyB); Which is, admittedly a little bit simpler. However, if the requirements change and now it needs to be \u0026ldquo;average\u0026rdquo; instead of \u0026ldquo;sum\u0026rdquo;. Then the second example becomes:\nNumberBinding binding = Bindings.divide(Bindings.add(propertyA, propertyB), 2); or alternatly using the Fluent API:\nNumberBinding binding = Bindings.add(propertyA, propertyB).divide(2); Both of which start to become a little bit more complicated. Now maybe this starts to become a better way:\nIntegerBinding binding = Bindings.createIntegerBinding(() -\u0026gt; (propertyA.get() + propertyB.get())/2, propertyA, propertyB); or possibly:\nIntegerBinding binding = Bindings.createIntegerBinding(() -\u0026gt; findAverage(propertyA.get(),propertyB.get()), propertyA, propertyB); Well\u0026hellip; maybe not so much in this case, but you get the idea.\nPersonally, I find that the Fluent API is great as long as the relationships and manipulations are simple and straight-forward. But when it starts to get more complicated, the Fluent API makes it even more complicated. When you find yourself scratching your head trying to figure out how to do something with the Fluent API, you probably should just stop and use Bindings.create{type}Binding() instead.\nThe Operation Methods # This set of methods is aimed pretty squarely at the functionality provided through the Fluent API. Basically, these methods allow you to manipulate and combine both Observable and concrete values into a Binding of some type. You\u0026rsquo;ll find that these methods often overloaded, so that you can generally just pass them the parameters that you have and you\u0026rsquo;ll automatically invoke the right method and get an appropriate Binding type back.\nWe have the following groups of operations:\nThe \u0026ldquo;Manipulation\u0026rdquo; Methods # Concat\nYou pass this method a vararg of Strings and Observable String types and it will create an StringExpresson, which implements ObservableStringValue. Format\nThis method takes a format string and a vararg of Strings and Observables and it will create a StringExpression. Size\nThis method will create an IntegerBinding from some kind of Observable Collection, binding to the size of the Collection. When\nThis method takes a ObservableBooleanValue and turns it into an instance of When. This is a whole subject in itself that is best discussed in the context of the Fluent API. The \u0026ldquo;Boolean\u0026rdquo; Methods # There\u0026rsquo;s a set of methods that return a BooleanBinding. For each of these you can supply a mixture of Observable and concrete data types and the method will create a BooleanBinding:\nLessThan and GreaterThan (including \u0026hellip;OrEqual versions) IsEmpty and IsNotEmpty Equal and NotEqual And and Or The \u0026ldquo;Mathematical\u0026rdquo; Methods # There are a set of methods that return DoubleBinding or NumberBinding and perform mathematical operations on concrete number types and numeric ObservableValues:\nMax and Min Multiply and Divide Add and Subtract Negate The \u0026ldquo;Extraction\u0026rdquo; Methods # Sometimes you need to create a relationship between an ObservableIntegerValue and an ObservableList of some nature. This is potentially tricky to do, but these methods make it relatively easy.\nList and Map Extractors # These are the valueAt methods. For ObservableLists you can either supply an ObservableIntegerValue or an int and these methdos will return an ObjectBinding of whatever type is in the ObservableList.\nFor ObservableMaps, you supply either a key value or on ObservableValue as the same type as the ObservableMap's keys and these methods will return an ObjectBinding of whatever type make up the values in the ObservableMap.\nMember Extractors # These are a set of methods that are all named like select{Type}(), so selectInteger() or selectBoolean().\nSometimes you have ObservableObjectValues that are composed of other ObservableObjectValues, which may be composed of other ObservableObjectValues in turn. If those ObservableObjectValues have their components implemented as JavaFX Beans, then you can use these methods to extract the member values.\nLet\u0026rsquo;s imagine that we have an ObservableObjectValue\u0026lt;CustomerOrder\u0026gt;, where CustomerOrder has a member which is ObservableObjectValue\u0026lt;Customer\u0026gt;, called customer. And Customer has a member which is ObservableObjectValue\u0026lt;Address\u0026gt; called address. And Address has a member which is ObservableStringValue called streetName.\nLet\u0026rsquo;s also assume that all of the members are implemented as JavaFX Beans. So there\u0026rsquo;s a method CustomerOrder.customerProperty(), and one called Customer.addressProperty(), and one called Address.streeNameProperty.\nIn this case, you can do the following:\nObservableObjectValue\u0026lt;CustomerOrder\u0026gt; order = ... StringBinding binding = Bindings.selectString(order, \u0026#34;customer.address.streetname\u0026#34;) The \u0026ldquo;Bind\u0026rdquo; Methods # The last few methods are used to bind (or effectively bind) two elements together.\nFirst, we have bindContent(). This allows you to bind a normal List to an ObservableList, a normal Set to an ObservableSet, or a normal Map to an ObservableMap. Once you\u0026rsquo;ve bound them, the normal Collection will be kept in sync with observable Collection, but you won\u0026rsquo;t be able to manually update the normal collection any more.\nThen there is bindContentBidirectional(). This allows you to synchronize the elements of two observable Collections.\nFinally, there is bindBidirectional() which is used to bind a StringProperty to some other kind of Property through either a Format or StringConverter.\n","date":"8 February 2023","externalUrl":null,"permalink":"/javafx/elements/bindings/bindings_class/","section":"JavaFX","summary":"The Bindings class is a utility library that provides a huge number of helper methods that can create and manipulate Bindings.  Understanding how to use this library can make your code much simpler and easier to read.","title":"Understanding the JavaFX Bindings Class","type":"javafx"},{"content":" Using Sprites # Sprites are an old concept. I remember that Commodore 64's and the Atari 800's in the early 1980's had the concept of Sprites included in their operating systems. A Sprite is just stand-alone graphic element that can be independently manipulated within a larger image environment. In the context of this tutorial, we're going to concentrate on the idea of a Sprite as an image container with a set of related images which are displayed within that container. By rapidly changing the image displayed in the container, we can give the illusion of motion. Single Image Sprites # Nowadays, and especially where CSS and web pages are concerned, a Sprite is usually a single image which contains a number of related images. For web design, this is a good technique as it avoids loading multiple images and improves page performance.\nFor JavaFX, it can also handy. As an example, let\u0026rsquo;s look at a single image file containing a number of images for a running troll:\nThis is one of the trolls from the CraftPix.net free troll series. I think they\u0026rsquo;re pretty cool, and the images are actually huge, with lots of details. The guy at the top of the page is another one of them.\nIt\u0026rsquo;s important to note that your individual images all have to be the same size, and are positioned identically relative to each other, and regularly spaced out.\nTo start with, let\u0026rsquo;s just get the Image on the screen in an ImageView, then we can move on from there:\npublic class SpriteDemo1 extends Application { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { BorderPane results = new BorderPane(); ImageView sprite = new ImageView(this.getClass().getResource(\u0026#34;Troll.png\u0026#34;).toExternalForm()); results.setCenter(sprite); return results; } } Which yields this: Okay, now we have the image loaded properly. It\u0026rsquo;s always a good idea to start off super simple, that way you know you don\u0026rsquo;t have problems locating the resources or anything else. Now, let\u0026rsquo;s use it as a Sprite.\npublic class SpriteDemo2 extends Application { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { ObjectProperty\u0026lt;Integer\u0026gt; indexProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(1); BorderPane results = new BorderPane(); results.setMinWidth(500); results.setMinHeight(400); ImageView sprite = new ImageView(this.getClass().getResource(\u0026#34;Troll.png\u0026#34;).toExternalForm()); Button button = new Button(\u0026#34;Click Me!\u0026#34;); results.setCenter(sprite); results.setBottom(button); button.setOnAction(evt -\u0026gt; { int currentValue = indexProperty.get(); indexProperty.set((currentValue \u0026lt; 9) ? currentValue + 1 : 1); }); Rectangle2D viewPort = new Rectangle2D(0, 0, 100, 140); sprite.setViewport(viewPort); indexProperty.addListener(observable -\u0026gt; { int column = indexProperty.get() % 5; if (column == 0) { column = 5; } int xOffset = (column - 1) * 100; int yOffset = (indexProperty.get() / 5) * 150; sprite.setViewport(new Rectangle2D(xOffset, yOffset, 100, 140)); }); return results; } } The individual troll images occupy a 100 x 140 pixel space, so that\u0026rsquo;s the size of or ViewPort rectangle.\nHere we\u0026rsquo;ve added on ObjectProperty\u0026lt;Integer\u0026gt; to hold the index of the current troll image. The Button \u0026ldquo;click\u0026rdquo; action just increments the value from 1 to 10 and then back around to start over.\nThere\u0026rsquo;s an InvalidationListener on the Property, which handles the display swapping inside the Sprite. Since there are two rows of trolls, there\u0026rsquo;s a little bit of calculation to turn a single index number into a row and column. There\u0026rsquo;s also a 10 pixel gap between the rows, so the vertical offset needs to be a bit bigger than the image height.\nWhich gives this:\nHow This Works # Now the ImageView has a ViewPort set on it. Essentially, this is rectangular area of the Image in the ImageView which will be displayed on the screen.\nAnd then we move the origin of the ViewPort rectangle over 1 troll to the right, like this:\nSince the ViewPort is locked onto the screen in a single position, it looks to the user like the Image behind it is snapping around to put a new troll under the window.\nAnimating the Sprite # Animating the Sprite is just a matter of automating the changing of the index value in the Property. The InvalidationListener doesn\u0026rsquo;t need to be changed at all, although it has been split off into its own method to keep the layout code looking clean.\nInstead of using Button clicks to trigger the change to the index value, we\u0026rsquo;re going to use an Animation, which is the JavaFX tool for automating things like this.\nThe Animation that we\u0026rsquo;re using is a Transition. What Transition is going to do is to increment a Double counter from 0.0 to 1.0 over the time span of a cycle. In this case the cycle is going to be 1,200 ms, or 1.2 s. Periodically, it\u0026rsquo;s going to fire the interpolate() method, passing the current value of the counter to it. We don\u0026rsquo;t know how often it\u0026rsquo;s going to fire, but we can be fairly sure that it\u0026rsquo;s going to be many, many times during the cycle.\nWe need our Transition to turn these calls to interpolate() into a set of discrete changes to the ViewPort of our Sprite, evenly spaced out over the cycle. To do this, we multiple the Double counter by the number of trolls inside the Sprite, and then take the integer value (which rounds down). Since we have 10 trolls in the Sprite, this will yield an integer between 0 and 9, which we shift over to 1 -10 by adding 1.\nEven if the interpolate() method is called 100 times before the index moves from 1 to 2, this won\u0026rsquo;t trigger the InvalidationListener until it actually changes to 2. And so on. This turns the interpolate() calls into discrete events.\npublic class SpriteDemo3 extends Application { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { ObjectProperty\u0026lt;Integer\u0026gt; indexProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(1); BorderPane results = new BorderPane(); results.setMinWidth(500); results.setMinHeight(400); ImageView sprite = new ImageView(this.getClass().getResource(\u0026#34;Troll.png\u0026#34;).toExternalForm()); results.setCenter(sprite); setViewPort(sprite, 1); indexProperty.addListener(observable -\u0026gt; setViewPort(sprite, indexProperty.get())); createAnimation(indexProperty).play(); return results; } private void setViewPort(ImageView sprite, int value) { int column = value % 5; if (column == 0) { column = 5; } int xOffset = (column - 1) * 100; int yOffset = (value \u0026lt;= 5) ? 0 : 155; sprite.setViewport(new Rectangle2D(xOffset, yOffset, 100, 140)); } private Animation createAnimation(ObjectProperty\u0026lt;Integer\u0026gt; indexProperty) { Animation animation = new Transition() { private int oldValue = 99; { setCycleDuration(Duration.millis(1200)); setInterpolator(Interpolator.LINEAR); } protected void interpolate(double frac) { int value = ((int) (frac * 10)) + 1; indexProperty.set(value); } }; animation.setCycleCount(Animation.INDEFINITE); return animation; } } A key thing to notice here is that you need to use Linear interpolation, not the default interpolation. Otherwise, he speeds up and slows down as it cycles, which is annoying (and took me a while to figure out).\nScrolling Background # Now we\u0026rsquo;re going to add a scrolling background to the application. The background will be this image:\nThe important thing about this image is that the extreme right edge of the image is identical to the left edge, which means that we can paste a copy of it at either side and it will look like a continuous scene. Even more importantly, the second half will be identical to the first half (obviously).\nNow the technique is very similar to the one with the Sprites. We\u0026rsquo;ll create a rectangular ViewPort, and then move it across the image, a few pixels at a time. Just like with the Sprites, since the ViewPort is fixed on the screen, it will look like the image is moving through the ViewPort, not the other way around.\nInfinite Scrolling # In order to handle the infinite scrolling aspect, we need to reset the offset every time the ViewPort is fully into the second half of the image:\nHere there\u0026rsquo;s a big line showing the join between the images, and you can see how the part of image inside the blue ViewPort is identical to the content shown in the pink ViewPort. So you can flip between the two of them and the user wouldn\u0026rsquo;t see any difference. This means that we can vary the X offset between 0 and the width of one of the images, resetting it back to zero each time, and it will look like a continuous scroll.\nIn reality, it would never get that far over to the right, you\u0026rsquo;ll see\u0026hellip;\nHere\u0026rsquo;s the code:\npublic class SpriteDemo4 extends Application { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { ObjectProperty\u0026lt;Integer\u0026gt; indexProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(1); ObjectProperty\u0026lt;Integer\u0026gt; backgroundOffsetProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(0); ImageView sprite = new ImageView(this.getClass().getResource(\u0026#34;Troll.png\u0026#34;).toExternalForm()); ImageView background = new ImageView(this.getClass().getResource(\u0026#34;Background.png\u0026#34;).toExternalForm()); background.setViewport(new Rectangle2D(0, 0, 400, 238)); setViewPort(sprite, 1); sprite.setTranslateY(50); indexProperty.addListener(observable -\u0026gt; setViewPort(sprite, indexProperty.get())); backgroundOffsetProperty.addListener(observable -\u0026gt; { background.setViewport(new Rectangle2D(backgroundOffsetProperty.get(), 0, 400, 238)); }); createSpriteAnimation(indexProperty).play(); createBackgroundAnimation(backgroundOffsetProperty).play(); StackPane results = new StackPane(background, sprite); results.setMinWidth(500); results.setMinHeight(400); return results; } private void setViewPort(ImageView sprite, int value) { int column = value % 5; if (column == 0) { column = 5; } int xOffset = (column - 1) * 100; int yOffset = (value \u0026lt;= 5) ? 0 : 155; sprite.setViewport(new Rectangle2D(xOffset, yOffset, 100, 140)); } private Animation createSpriteAnimation(ObjectProperty\u0026lt;Integer\u0026gt; indexProperty) { Animation animation = new Transition() { private int oldValue = 99; { setCycleDuration(Duration.millis(900)); setInterpolator(Interpolator.LINEAR); } protected void interpolate(double frac) { int value = ((int) (frac * 10)) + 1; if (value != oldValue) { if (value == 11) { value = 1; } oldValue = value; indexProperty.set(value); } } }; animation.setCycleCount(Animation.INDEFINITE); return animation; } private Animation createBackgroundAnimation(ObjectProperty\u0026lt;Integer\u0026gt; backgroundOffsetProperty) { Animation animation = new Transition() { { setCycleDuration(Duration.millis(36000)); setInterpolator(Interpolator.LINEAR); } protected void interpolate(double frac) { int backgroundOffset = (int) ((frac * 6350)) % 635; backgroundOffsetProperty.set(backgroundOffset); } }; animation.setCycleCount(Animation.INDEFINITE); return animation; } } This is very similar to the previous example, but we\u0026rsquo;ve swapped out the BorderPane for a StackPane since we want our running troll to be on top of the scrolling background. We also had to apply a vertical translation to the troll so that his feet lined up with the grass at the bottom, so he didn\u0026rsquo;t look like he was running in the air.\nAll of the scrolling is done be manipulating the offset Property in the Animation. The 0.0 -\u0026gt; 1.0 frac is multiplied by an even multiple of the single image width (635 pixels), and then the modulo when divided by the single image width is taken as the new offset. In this case, the multiplier is 6350, which means that each complete cycle of the Transition will go across the image 10 times. In needs to be an even multiplier of the width so that the cycle ends at the right edge of the single image and the next cycle - starting at the left edge - will be a smooth transition.\nThe only trick here is getting the cycle times right for both Animations, so that the troll looks like he\u0026rsquo;s running at the same speed as the background is moving. Otherwise he looks like he\u0026rsquo;s sliding.\nNow it looks like this:\nNow let\u0026rsquo;s look at Sprites again\u0026hellip;\nMultiple Image Sprites # When I started out writing this tutorial, I was under the impression that most Sprites were packaged up into single image files the way I\u0026rsquo;ve shown above. But when I started looking for good example Sprites to use, I found that many of them are packaged up as individual images.\nIn fact, this is the way that the trolls were packaged up - a Zip file with individual images. I wasn\u0026rsquo;t going to use them at first, but they are just too awesome not to use. So I ended up creating a composite image Sprite file myself to show how that works.\nBut now, let\u0026rsquo;s look at how to use individual files. There were three different trolls in the Zip file, so let\u0026rsquo;s use a different troll, but we\u0026rsquo;ll stick to the \u0026ldquo;running\u0026rdquo; Sprites.\nThe Technique # In many ways, this is simpler than the single image file Sprite. There are ten images, all named \u0026ldquo;Run_00{x}.png\u0026rdquo;, where the \u0026ldquo;x\u0026rdquo; is an index. So we load them into a Queue. At this point we aren\u0026rsquo;t even really interested in the numbering of the images at all. They are just a sequence that we\u0026rsquo;ll rotate through. To keep the Queue going, we add each Image that we remove from the ImageView to the end of the Queue, then load the one from the front into the ImageView.\nI\u0026rsquo;ve kept the index Property, in part just to keep the separation between the layout and the animation control. But the Listener doesn\u0026rsquo;t even look at the value of the Property. There is a call to get(), but it\u0026rsquo;s just there to revalidate the Property. However, the mechanics of the index Property still convert the continuum of values for frac into 10 discrete, fairly evenly spaced out, events.\npublic class SpriteDemo5 extends Application { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { ObjectProperty\u0026lt;Integer\u0026gt; indexProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(1); ObjectProperty\u0026lt;Integer\u0026gt; backgroundOffsetProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(0); Queue\u0026lt;Image\u0026gt; imageQueue = getImages(); ImageView sprite = new ImageView(imageQueue.remove()); ImageView background = new ImageView(this.getClass().getResource(\u0026#34;Background.png\u0026#34;).toExternalForm()); background.setViewport(new Rectangle2D(0, 0, 400, 238)); sprite.setTranslateY(18); indexProperty.addListener(observable -\u0026gt; { indexProperty.get(); changeTrollImage(sprite, imageQueue); }); backgroundOffsetProperty.addListener(observable -\u0026gt; { background.setViewport(new Rectangle2D(backgroundOffsetProperty.get(), 0, 400, 238)); }); createSpriteAnimation(indexProperty).play(); createBackgroundAnimation(backgroundOffsetProperty).play(); StackPane results = new StackPane(background, sprite); results.setMinWidth(500); results.setMinHeight(400); return results; } private Queue\u0026lt;Image\u0026gt; getImages() { Queue\u0026lt;Image\u0026gt; imageQueue = new ArrayDeque\u0026lt;\u0026gt;(); for (int x = 0; x \u0026lt; 10; x++) { URL url = this.getClass().getResource(\u0026#34;Run_00\u0026#34; + x + \u0026#34;.png\u0026#34;); imageQueue.add(new Image(url.toExternalForm(), 160.0, 0.0, true, true)); } return imageQueue; } private void changeTrollImage(ImageView sprite, Queue\u0026lt;Image\u0026gt; imageQueue) { imageQueue.add(sprite.getImage()); sprite.setImage(imageQueue.remove()); } private Animation createSpriteAnimation(ObjectProperty\u0026lt;Integer\u0026gt; indexProperty) { Animation animation = new Transition() { { setCycleDuration(Duration.millis(900)); setInterpolator(Interpolator.LINEAR); } protected void interpolate(double frac) { int value = ((int) (frac * 10)); indexProperty.set(value); } }; animation.setCycleCount(Animation.INDEFINITE); return animation; } private Animation createBackgroundAnimation(ObjectProperty\u0026lt;Integer\u0026gt; backgroundOffsetProperty) { Animation animation = new Transition() { { setCycleDuration(Duration.millis(36000)); setInterpolator(Interpolator.LINEAR); } protected void interpolate(double frac) { int backgroundOffset = (int) ((frac * 6350)) % 635; backgroundOffsetProperty.set(backgroundOffset); } }; animation.setCycleCount(Animation.INDEFINITE); return animation; } } One thing I did was to scale the Images to the correct size, rather than have the ImageView scale them for display. This might make some difference in performance, but it\u0026rsquo;s hard to tell with such a simple example. In a busy screen with many Sprites, it could become important.\nI also scaled this troll a bit bigger than the last one. So his vertical positioning needed adjustment.\nIt looks like this:\nConclusion # I hope that you can see that both animated Sprites and scrolling backgrounds are fairly easy to implement in JavaFX.\nMost Sprites are bundled up with a variety of different action sequences. The trolls have walking, jumping, dying, running, attacking and idle. Some others might have \u0026ldquo;firing\u0026rdquo;, or \u0026ldquo;exploding\u0026rdquo;. I can see a strong case for packaging everything up into a single class that extends ImageView, and having methods to start up different animations. So you could have a class called Troll, and then call troll.run(), or troll.attack() to trigger the appropriate animation. That would allow you to have different kinds of trolls on the screen and your layout code wouldn\u0026rsquo;t contain any of the mechanics about the animation - which would be very clean.\n","date":"10 April 2022","externalUrl":null,"permalink":"/javafx/elements/nodes/images/sprites/","section":"JavaFX","summary":"How to handle image animation with Sprites and scrolling backgrounds","title":"Sprites and Scrolling Images","type":"javafx"},{"content":" What is a Button? # A Button is the basic control to allow the user trigger an action in a screen. JavaFX has two Button classes; Button and ToggleButton. In this article, we\u0026rsquo;re going to look at the Button class itself.\nHow Button Fits in the JavaFX Hierarchy # Button inherits methods and properties from these important classes:\nNode Everything in JavaFX inherits from Node, which gives most of its methods for styling, event handling and responding to mouse actions as well as location and transformation. Control This is the base class for user interaction controls on a screen. This base class adds skinning, tool-tips and context menus to Node. Region All Control classes are Regions, which means that they can contain other Nodes and have layout properties like padding and borders. Labeled In JavaFX, the Label class contains both a graphical Node and a text property, and the Labeled classes also support this same idea as the contents of the Control as a Region. The version 16 JavaDocs for Button can be found here\nLayout # You can think of a Button as styled Region with a background and a border containing a Label (which, of course, contains a Graphic and/or some text). All of the button-like feel of a Button is just the styling responding to different states of the properties in the class.\nThese states change when:\nThe Button is disabled. Gains or loses focus. Becomes or stops being default. When the cursor enters or leaves the Region. When a mouse button is clicked and released when the cursor is in the region. Of course, you can change the way that the Button responds to all of these changes.\nCSS Elements # Modena # To understand how the styling works with the properties on Buttons, we\u0026rsquo;ll look at the Modena stylesheet, which is the standard stylesheet that ships with the latest versions of JavaFX.\nThe basic colours in Modena are as follows:\n-fx-base -fx-background -fx-outer-border -fx-default-button -fx-focus-color The rest of the colours you see referenced are more complicated gradients and ladders based on these fundamental colours, but this should give you the idea.\nIt\u0026rsquo;s important to note that -fx-color is initially set be -fx-base. Most of the transitions are accomplished by changing -fx-color to another value for the Button.\nHere\u0026rsquo;s what the various stylings look like:\nThe following selectors control Buttons in the CSS, these are the definitions from \u0026ldquo;Modena\u0026rdquo;:\nBase layout for Button # .button { -fx-background-insets: 0 0 -1 0, 0, 1, 2; -fx-background-radius: 3px, 3px, 2px, 1px; -fx-padding: 0.333333em 0.666667em 0.333333em 0.666667em; /` 4 8 4 8 `/ -fx-text-fill: -fx-text-base-color; -fx-alignment: CENTER; -fx-content-display: LEFT; } The first three lines here create the border and shadow effects by layering 4 background colours on top of each other with differing insets and corner radii. There\u0026rsquo;s no actual \u0026ldquo;border\u0026rdquo; defined. The colours are standard colours for the Modena theme.\nThe text colour is set to be the standard theme text colour.\nMouse Hover # This is the styling added when the cursor hovers over the Button. It simply lightens it up a little.\n{% highlight css %} .button:hover { -fx-color: -fx-hover-base; } {% endhighlight %}\n-fx-hover-base is supposed to be just a little bit lighter than -fx-base.\nArmed # When the mouse button is pressed when the cursor is over the Button it appears to push down a little bit. This is accomplished by darkening it a by 6%. JavaFX refers to this as \u0026ldquo;armed\u0026rdquo;.\nIt\u0026rsquo;s virtually impossible to have an armed Button that doesn\u0026rsquo;t also have focus. So the example in the image above shows both states.\n.button:armed { -fx-color: -fx-pressed-base; } Focused # When the Button has focus, the border colour scheme changes to a \u0026ldquo;glowing\u0026rdquo; blue ring:\n.button:focused { -fx-background-color: -fx-focus-color, -fx-inner-border, -fx-body-color, -fx-faint-focus-color, -fx-body-color; -fx-background-insets: -0.2, 1, 2, -1.4, 2.6; -fx-background-radius: 3, 2, 1, 4, 1; } Disabled # Disabled Buttons are a quite a bit translucent. Personally, I find this amount to be extreme and it makes the buttons almost difficult to see.\n.button:disabled { -fx-opacity: 0.4; } Mnemonic Underline # This is specifically the colour of the underline which is shown under key letters when the mnemonics are activated (usually by pressing the \u0026lt;Alt\u0026gt; key). Ordinarily the colour of the underline is \u0026ldquo;transparent\u0026rdquo;.\n.button:show-mnemonics \u0026gt; .mnemonic-underline { -fx-stroke: -fx-text-base-color; } Default # When a Button has been set as the \u0026ldquo;default\u0026rdquo;, the base colour changes to a light blue.\n.button:default { -fx-base: -fx-default-button; } Properties # Button has all of the standard properties of Node and Region, which define things like MaxHeight, MaxWidth, Visible, Managed and so on. In addition, it has a few other properties which are interesting:\nLet\u0026rsquo;s take a look at what some of those properties actually do:\nGraphic # Since Button extends Labeled, it has two properties holding the contents. The Graphic is simply any kind of Node that you want to put on the Button. It can be an ImageView, an icon, or something bigger and complex, like a TableView, BorderPane, or Chart. That would probably end up with a really big Button, but it would still work.\nHere\u0026rsquo;s a sample showing how the Graphic property can be manipulated:\npublic class ButtonsGraphicExample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setScene(new Scene(createView())); primaryStage.show(); } private Region createView() { HBox results = new HBox(10); Button button1 = new Button(\u0026#34;\u0026#34;, new ImageView(new Image(\u0026#34;/images/JavaFXLogo.png\u0026#34;))); ImageView logo = new ImageView(new Image(\u0026#34;/images/JavaFXLogo.png\u0026#34;)); logo.setFitHeight(32); Button button2 = new Button(\u0026#34;Click Me!\u0026#34;, logo); Shape circle1 = createCircle(Color.TEAL); Shape circle2 = createCircle(Color.TEAL); Shape circle3 = createCircle(Color.RED); Button button3 = new Button(\u0026#34;Button\u0026#34;, circle1); Button button4 = new Button(\u0026#34;Focused?\u0026#34;); button4.graphicProperty().bind(Bindings.createObjectBinding(() -\u0026gt; button4.isFocused() ? circle3 : circle2, button4.focusedProperty())); results.getChildren().addAll(button1, button2, button3, button4); results.setPadding(new Insets(30)); return results; } private Shape createCircle(Paint colour) { Circle results = new Circle(40); results.setFill(colour); results.setStroke(Color.BLACK); results.setStrokeWidth(2.0); return results; } } The first two Buttons show an ImageView loaded in as the graphic, with and without a Text value. The last two Buttons have a Shape loaded into the Graphic.\nThe last Button, \u0026ldquo;button4\u0026rdquo; has its Graphic property bound to its Focused property, swapping out a red circle when the Button has focus.\nThis is what it looks like with the focus somewhere other than the last Button:\nAnd when the last Button has focus:\nText # The other part of Labeled is a StringProperty called Text, and is simply any text you want to show on the Button and is what people generally think of as the Button \u0026ldquo;label\u0026rdquo;. Since it\u0026rsquo;s a property, it can be bound to other properties in your screen and Model and change dynamically.\nHere\u0026rsquo;s an example where the Text is bound to the results of an animation loop, so that the Button displays the number of seconds (almost) since the Button was clicked:\npublic class ButtonBoundText extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { HBox pane = new HBox(20, createTimerButton(), createTimerButton()); pane.setPadding(new Insets(30)); primaryStage.setScene(new Scene(pane)); primaryStage.show(); } @NotNull private Button createTimerButton() { Button timerButton = new Button(\u0026#34;Timer\u0026#34;); timerButton.setOnAction(evt -\u0026gt; createTimer(timerButton.textProperty())); return timerButton; } private void createTimer(StringProperty boundProperty) { IntegerProperty counter = new SimpleIntegerProperty(0); Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(10000), new KeyValue(counter, 10000))); boundProperty.bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(counter.get()), counter)); timeline.play(); } } This is what it looks like with the timer on the second Button running:\nDefaultButton # Straight from the JavaDocs for Button, as it says it all:\nA default button is the button that receives a keyboard VK_ENTER press, if no other node in the scene consumes it.\nThis can be super useful if you have a screen with multiple Buttons but you want your UI to respond to \u0026ldquo;Enter\u0026rdquo; key in an appropriate manner. Or if you have several data entry controls that need to be completed in order to activate a Button to respond to \u0026ldquo;Enter\u0026rdquo;.\nFor instance, you can bind the DefaultButton property of a Button to the Focused property of a TextField. So whenever a user hits \u0026ldquo;Enter\u0026rdquo; from that TextField, it activates the Button, but not if they are not in that TextField.\nEvents # The OnAction Event # OnAction is really the only event you\u0026rsquo;re likely to use with a Button. It\u0026rsquo;s the one that\u0026rsquo;s fired when you click and release the Button.\nFor practical purposes, there\u0026rsquo;s no useful information in event itself, and just the fact that it\u0026rsquo;s been fired is the main piece of information you\u0026rsquo;ll need in order to do anything that\u0026rsquo;s triggered by the event. My recommendation is to keep the Event Handler itself as short as possible, and create a method to handle the actual work that the Button is going to do, possibly passing the Button itself as a parameter so that you can access its properties to temporarily disable it.\nA Simple example # In this example, we\u0026rsquo;re just going to update a Label on the screen with contents from a TextField. The entire action of the Button is set with just this one line:\nbutton.setOnAction(evt -\u0026gt; target.setText(textField.getText())); That\u0026rsquo;s pretty simple, but you can see that there\u0026rsquo;s no need to reference any data from the Event itself, it\u0026rsquo;s just a trigger for the action to happen.\npublic class ButtonActionExample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setScene(new Scene(createView())); primaryStage.show(); } private Region createView() { Label actionTarget = new Label(\u0026#34;Button Has Not Yet Been Clicked\u0026#34;); VBox results = new VBox(10, inputLine(actionTarget), new HBox(6, new Label(\u0026#34;Results: \u0026#34;), actionTarget)); results.setPadding(new Insets(20)); return results; } private Node inputLine(Label target) { TextField textField = new TextField(); Button button = new Button(\u0026#34;Fetch\u0026#34;); button.defaultButtonProperty().bind(textField.focusedProperty()); button.setOnAction(evt -\u0026gt; target.setText(textField.getText())); return new HBox(6, new Label(\u0026#34;Enter Some Text Here: \u0026#34;), textField, button); } } Before you click on the Button, the screen looks like this:\nAnd then it looks like this:\nTips # Disable vs Disabled Properties # This confuses everyone. Disable is the read/write property that you can change to control whether or not the Button is to be disabled. Disabled is a read-only property that will tell you if the Button is currently disabled. Disabled should always be used for querying, as it will change if a containing Node of the Button has been disabled, while querying Disable will only tell you if the Button has been specifically disabled.\nAvoid Double-Clicks # If a Button's action takes more than a couple of milliseconds to complete it\u0026rsquo;s a good idea to immediately set Disable to true in the OnAction event so that the user can\u0026rsquo;t trigger it a second or third time while it\u0026rsquo;s running.\nThis can get a little tricky if Disable has been bound to other elements of the GUI. In that case you should create a boolean property called \u0026ldquo;xyzRunning\u0026rdquo; (or something similiar) and add it to your binding. Then toggle that property instead of directly messing with the Disable property.\nBinding the Default Property # Being able to just hit \u0026lt;Enter\u0026gt; can be a huge convenience for users, and is often the most intuitive behaviour. But there might be multiple Buttons that could be candidates for the \u0026ldquo;default\u0026rdquo;, and the correct one might change depending on what\u0026rsquo;s happening in the GUI. Here\u0026rsquo;s an example where there are two lookup buttons, and the one that is default is determined by which TextField has the focus at any given time:\npublic class DefaultButtonExample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setScene(new Scene(createView())); primaryStage.show(); } private Region createView() { VBox results = new VBox(10, inputLine(\u0026#34;Customer Name:\u0026#34;), inputLine(\u0026#34;Account Number:\u0026#34;)); results.setPadding(new Insets(20)); return results; } private Node inputLine(String label) { TextField textField = new TextField(); Button button = new Button(\u0026#34;Fetch\u0026#34;); button.defaultButtonProperty().bind(textField.focusedProperty()); return new HBox(6, new Label(label), textField, button); } } This looks like this:\nIt\u0026rsquo;s also easier than trying to capture \u0026lt;Enter\u0026gt; in the keystrokes in the TextField!\nUse Multiple Buttons - Don\u0026rsquo;t Reconfigure a Single Button # Sometimes you\u0026rsquo;ll have multiple contexts in which a Button could operate, and it might need to have different labels and actions depending on the context. For instance, in a CRUD screen the Button could read \u0026ldquo;Create\u0026rdquo; for a new record, or \u0026ldquo;Update\u0026rdquo; to save an existing record. Unless the differences are entirely cosmetic, it\u0026rsquo;s usually cleaner to have two or more Buttons and simply hide any Buttons that don\u0026rsquo;t apply, rather that keeping track of the context and having the OnAction Event Handler figure which action code it should be running.\nHere\u0026rsquo;s an example where the Button that\u0026rsquo;s shown depends on whether or not a TextField has anything entered into it or not. There are four versions of it on the same screen, to show both states and confirm which action was run when clicked:\npublic class ButtonHideExample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { HBox row1 = new HBox(20, createExample(), createExample()); HBox row2 = new HBox(20, createExample(), createExample()); primaryStage.setScene(new Scene(new VBox(20, row1, row2))); primaryStage.show(); } private Region createExample() { TextField textField = new TextField(); Label targetLabel = new Label(\u0026#34;Nothing has been clicked\u0026#34;); HBox inputRow = new HBox(6, new Label(\u0026#34;Number: \u0026#34;), textField); HBox outputRow = new HBox(6, new Label(\u0026#34;Results:\u0026#34;), targetLabel); VBox results = new VBox(10, inputRow, outputRow, createButtons(textField.textProperty(), targetLabel.textProperty())); results.setPadding(new Insets(20)); return results; } private Node createButtons(StringProperty triggerProperty, StringProperty targetProperty) { Button saveButton = new Button(\u0026#34;Save\u0026#34;); saveButton.visibleProperty().bind(triggerProperty.isEmpty().not()); saveButton.managedProperty().bind(saveButton.visibleProperty()); saveButton.setOnAction(evt -\u0026gt; targetProperty.set(\u0026#34;Save was clicked\u0026#34;)); Button generateButton = new Button(\u0026#34;Generate\u0026#34;); generateButton.visibleProperty().bind(triggerProperty.isEmpty()); generateButton.managedProperty().bind(generateButton.visibleProperty()); generateButton.setOnAction(evt -\u0026gt; targetProperty.set(\u0026#34;Generate was clicked\u0026#34;)); return new StackPane(saveButton, generateButton); } } Which looks like this:\nThings to note about this:\nYou need to set both the Visible and Managed properties.\nIf you don\u0026rsquo;t do this then the invisible Buttons will still take up space on the screen. There\u0026rsquo;s no connection between the two Buttons Each Button is fully configured in 4 lines of code. The Button creation method is essentially stand-alone.\nIt\u0026rsquo;s given a pair of properties to work with, but there\u0026rsquo;s no global variables or any other connection to the rest of the screen. In fact, the same method is called 4 times, and the results of each are completely independent. Content Drives Minimum Size # If you have large content in your Button, the Button will grow to accommodate it. Setting MaxHeight or MaxWidth won\u0026rsquo;t cause the button to shrink if it\u0026rsquo;s smaller than the size of the contents. You\u0026rsquo;ll need to apply some sort of scaling transformation on your content to make the Button smaller.\nHowever, setting MinHeight and MinWidth can make a Button larger.\n","date":"3 January 2022","externalUrl":null,"permalink":"/javafx/elements/buttons/","section":"JavaFX","summary":"Buttons are the basic component that triggering actions in your applications. While there is just one basic Button class, it can be styled and configured in a multitude of ways that make it look and behave quite differently to suit a variety of uses.","title":"All About Buttons","type":"javafx"},{"content":" What is an Event? # An Event is a data object propagated by JavaFX in response to a trigger in the UI. It\u0026rsquo;s a signal that something of significance has happened, and gives the application a chance to react to that event.\nEvents are useful because they offer an opportunity to create imperative code that responds to something in the UI. As a Reactive framework, the functionality provided by Bindings and ChangeListeners provide the primary tools for connecting the View to the State in an application. However, there are times when imperative code is appropriate, such as when connecting to an external API, and Events provide that mechanism in JavaFX.\nHow Events Fit in the JavaFX Hierarchy # Event classes inherit methods and properties from these important classes:\nEventObject All Events extend from EventObject which supplies the Source property. Event All Events extend from Event which provides the fire() and consume() methods. InputEvent This is the base class for input type Events, like MouseEvent The version 16 JavaDocs for Event can be found here\nFields # Source \u0026amp; Target # Source is the Node that generated the EventHandler that\u0026rsquo;s running. Target is the Node that triggered the Event. In virtually all real use cases, these will be the same Node since the EventHandler is usually attached to the Node that triggered the Event.\nEventType # This is a little more complicated.\nWhile Events have Java types (such as ActionEvent and InputEvent), there is also a field called EventType and a getter, getEventType() that you should use to make decisions about handling different kinds of Events. This holds true even if the Java type of the Event has only one possible EventType - it will make your code cleaner.\nEventType, however, is a generic class, parameterized over the Java type of the Event. This means that the actual class of the EventType will be something like EventType\\\u0026lt;ActionEvent\\\u0026gt;, or EventType\\\u0026lt;MouseEvent\\\u0026gt;. That can get a little ugly when you are passing the EventType to a method which accepts EventType from a variety of different Event classes. You\u0026rsquo;ll get warnings about using raw classes if you just define the method parameter as EventType.\nFinally, the actual possible values of EventType are stored as static fields in the Event classes themselves. To check for a type of mouse Event you\u0026rsquo;ll need:\nif (event.getEventType().equals(MouseEvent.MOUSE_CLICKED)) {} Event Handlers # So, what do you do with an Event?\nAn Event indicates, as its name suggests, that something - some kind of event - has occurred, and it\u0026rsquo;s passed around as an object. In order to deal with an Event, you need to create an EventHandler.\nAn EventHandler is just a snippet of code which is designed to consume an Event of a specific type. It\u0026rsquo;s a functional interface that works very much like Consumer, it accepts a value and doesn\u0026rsquo;t return anything. Instead of having an accept() method, it has a handle() method, where it is passed the Event.\nSubmitted to the FXAT # The other really important aspect to remember is that all of the code inside, or called from an EventHandler is going to be executed on the FXAT. Furthermore, it\u0026rsquo;s going to be executed by submitting it to an \u0026ldquo;Event Queue\u0026rdquo;, meaning that its execution may be delayed. There is only one FXAT and the jobs are all executed in the order of submission, so there may be some delay before your EventHandler is executed, and significant code may run on the FXAT between the moment that the Event is fired, and the time that the EventHandler is executed. If multiple EventHandlers are defined to respond to the same event, there are specific rules which govern which will be executed first.\nSo how do you implement an EventHandler? There are a few ways:\nCreate a Custom class # EventHandler is just an Interface, so any class which implements it and has a handle() method can be used as an EventHandler. Probably the most useful case for this would be if you had some kind of data that was persistent between invocations of handle() or that needed to be initialized via a constructor when the EventHandler was defined.\nHere\u0026rsquo;s an example that uses a custom class to track a counter as part of the EventHandler class. At instantiation, the EventHandler is passed an IntegerProperty as the counter value, and an int as the increment value. Each time that handle() is invoked it adds the increment to the property, which is bound to the Text property of the Button.\npublic class EventClassExample extends Application { public static void main(String[] args) { launch(args); } private final IntegerProperty count1 = new SimpleIntegerProperty(10); private final IntegerProperty count2 = new SimpleIntegerProperty(20); @Override public void start(Stage primaryStage) { HBox hbox = new HBox(10, createCounterButton(count1, 1), createCounterButton(count2, 2)); hbox.setPadding(new Insets(30)); primaryStage.setScene(new Scene(hbox)); primaryStage.show(); } private Node createCounterButton(IntegerProperty count, int increment) { Button button = new Button(\u0026#34;Fetch\u0026#34;); Counter counter = new Counter(count, increment); button.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(count.get()), count)); button.setOnAction(counter); return button; } } class Counter implements EventHandler\u0026lt;ActionEvent\u0026gt; { private final IntegerProperty count; private final int increment; public Counter(IntegerProperty count, int increment) { this.count = count; this.increment = increment; } @Override public void handle(ActionEvent event) { count.set(count.get() + increment); } public ReadOnlyIntegerProperty counterProperty() { return count; } } And when it runs, it looks like this:\nCreate an Anonymous Inner class # Prior to the introduction of Lambdas, this was the \u0026ldquo;normal\u0026rdquo; way to create an EventHandler. It can get a bit bulky if you in-line the class definition inside the method assigning it to the Node.\nHere\u0026rsquo;s the same example as before, but using an anonymous inner class:\npublic class InnerClassExample extends Application { public static void main(String[] args) { launch(args); } private final IntegerProperty count1 = new SimpleIntegerProperty(10); private final IntegerProperty count2 = new SimpleIntegerProperty(20); @Override public void start(Stage primaryStage) { HBox hbox = new HBox(10, createCounterButton(count1, 1), createCounterButton(count2, 2)); hbox.setPadding(new Insets(30)); primaryStage.setScene(new Scene(hbox)); primaryStage.show(); } private Node createCounterButton(IntegerProperty count, int increment) { Button button = new Button(\u0026#34;Fetch\u0026#34;); button.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(count.get()), count)); EventHandler\u0026lt;ActionEvent\u0026gt; counter = new EventHandler\u0026lt;\u0026gt;() { @Override public void handle(ActionEvent event) { count.set(count.get() + increment); } }; button.setOnAction(counter); return button; } } Of course, you can define the inner class in-line when the ActionHandler is set:\nprivate Node createCounterButton(IntegerProperty count, int increment) { Button button = new Button(\u0026#34;Fetch\u0026#34;); button.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(count.get()), count)); button.setOnAction(new EventHandler\u0026lt;ActionEvent\u0026gt;() { @Override public void handle(ActionEvent event) { count.set(count.get() + increment); } }); return button; } Use a Lambda Expression # Since EventHandler is a Functional Interface, you can define it with a Lambda expression. This can provide a really compact way to declare a handler, especially if you stick to the in-line form of lambda and just use it as a way to invoke code in a separate method.\nHere\u0026rsquo;s the same example from the Anonymous Inner Class section, but turned into a Lambda\npublic class LambdaExample1 extends Application { public static void main(String[] args) { launch(args); } private final IntegerProperty count1 = new SimpleIntegerProperty(10); private final IntegerProperty count2 = new SimpleIntegerProperty(20); @Override public void start(Stage primaryStage) { HBox hbox = new HBox(10, createCounterButton(count1, 1), createCounterButton(count2, 2)); hbox.setPadding(new Insets(30)); primaryStage.setScene(new Scene(hbox)); primaryStage.show(); } private Node createCounterButton(IntegerProperty count, int increment) { Button button = new Button(\u0026#34;Fetch\u0026#34;); button.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(count.get()), count)); button.setOnAction(event -\u0026gt; count.set(count.get() + increment)); return button; } } Slightly better, IMHO, is to put the actual work into a method and simply invoke it from the EventHandler:\npublic class LambdaExample1 extends Application { public static void main(String[] args) { launch(args); } private final IntegerProperty count1 = new SimpleIntegerProperty(10); private final IntegerProperty count2 = new SimpleIntegerProperty(20); @Override public void start(Stage primaryStage) { HBox hbox = new HBox(10, createCounterButton(count1, 1), createCounterButton(count2, 2)); hbox.setPadding(new Insets(30)); primaryStage.setScene(new Scene(hbox)); primaryStage.show(); } private Node createCounterButton(IntegerProperty count, int increment) { Button button = new Button(\u0026#34;Fetch\u0026#34;); button.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(count.get()), count)); button.setOnAction(event -\u0026gt; incrementCounter(count, increment)); return button; } private void incrementCounter(IntegerProperty counter, int increment) { counter.set(counter.get() + increment); } } Types of Events # In addition to the types listed below, there are a handful of very specific Event types, like TreeTableView.EditEvent, which are best examined in the context of an article about specific JavaFX Node classes. The following are the common Event types that you might need to deal with:\nThe ActionEvent # The ActionEvent is probably the mostly commonly encountered event, as it\u0026rsquo;s the one that\u0026rsquo;s triggered by Buttons and CheckBoxes and their ilk. It\u0026rsquo;s also the simplest kind of event, as it doesn\u0026rsquo;t have any data beyond its Source property. ActionEvents are installed on Nodes by calling their setOnAction() method.\nLook at the samples from the EventHandlers section above. These all show how an ActionEvent triggers a handler that does work without any specific reference to the Event that triggered it.\nThe MouseEvent # For MouseEvent and KeyEvent handlers, you are probably going to need to look at the Event itself since there is information in the Event which is going to be required in order to handle the Event. There are a number of different types of MouseEvent, and you can see the complete list in the JavaDocs.\n\u0026ldquo;Drag and Drop\u0026rdquo; is a more complicated topic which requires its own article, so we\u0026rsquo;ll leave it for now.\nMouse Enter and Leave Events # These are the Events that are generated when the cursor enters or leaves a Node on the screen. There are two EventTypes, each with two flavours. They are MOUSE_ENTERED or MOUSE_ENTERED_TARGET and MOUSE_EXITED or MOUSE_EXITED_TARGET. The differences between the two flavours is only meaningful if you read and apply, \u0026ldquo;The Complicated Stuff\u0026rdquo;, below. Otherwise, you can use either flavour without seeing any differences in functionality.\nLike most mouse based Events, there\u0026rsquo;s probably a better way in JavaFX to do whatever you are trying to do with MOUSE_ENTERED and MOUSE_EXITED. The \u0026ldquo;hover\u0026rdquo; pseudo-class in CSS might do what you need, or maybe a ToolTip.\nMouse Click and Button Events # There are three types of Events in this category, MouseClicked, MousePressed and MouseReleased. MouseClick is the Event generated by a complete press-release cycle on a mouse button. The source is whatever Node the cursor was over when the Event happened.\nThe most common use for these MouseEvents is when a Node doesn\u0026rsquo;t have an OnAction() method, or where you need to check for alternate mouse button usage. To that end, there are two important methods in the MouseEvent\ngetButton() This will tell you which button was clicked/pressed/released. It is of type MouseButton, with the most important values being PRIMARY, SECONDARY and MIDDLE. getClickCount() This will tell you how many times the button was clicked to generate the Event. There are also methods to tell if \u0026lt;Ctrl\u0026gt;, \u0026lt;Alt\u0026gt;, \u0026lt;Shift\u0026gt; and a few other modifying keys were pressed when the Event was generated.\nThe KeyEvent # KeyEvents are used to capture keystrokes that occur in a Node. Just like the mouse based Events, there are three kinds of events one for key pressed, one for key released and one for key typed - which is a press/release cycle on a key.\nAnd also just like with mouse based Events there are methods that will tell you if \u0026lt;Ctrl\u0026gt;, \u0026lt;Alt\u0026gt;, \u0026lt;Shift\u0026gt; and a few other modifying keys were pressed when the Event was generated.\nThe main piece of information is the KeyCode, from the getCode() method in the KeyEvent. It will return you a value that can be found in this JavaDocs page.\nThe WorkerStateEvent # WorkerStateEvents are not called by Nodes but by Tasks and Services which are generally used to run code on a background thread. For the most part, these Events are used to get your code running back on the FXAT once the Task has been completed.\nHow to deal with Tasks and WorkerStateEvents will be covered in its own article.\nThe WindowEvent # WindowEvents are triggered by Window subclasses, the most important of which is Stage. There are three basic classes of WindowEvent types: Showing, Hiding and Closing. \u0026ldquo;Showing\u0026rdquo; and \u0026ldquo;Hiding\u0026rdquo; each have a before and after Event, while \u0026ldquo;Closing\u0026rdquo; has a single type WINDOW_CLOSE_REQUEST.\nWIDOW_CLOSE_REQUEST is arguably the most useful as it is triggered whenever an external element (including the user) attempts to close the window. If not consumed by an EventHandler, this event will cause the window to close. The most common use of an EventHandler here is to perform operations like checking for unsaved data, showing an \u0026ldquo;Are you sure?\u0026rdquo; alert and possibly interrupting the window close.\nTips # View EventHandler as a Trigger # The presence of an EventHandler in your code seems to automatically make it more complex. Get your code out of the handle() method as soon as possible. If possible, treat the EventHandler as nothing more than a trigger to call some \u0026ldquo;regular\u0026rdquo; Java code outside of the EventHandler.\nThere\u0026rsquo;s rarely a reason for an ActionEvent handler to be anything other than an Expression Lambda. If you need to do something more complicated, then call a method and pass it any parameters it might need. Other types of Events may require a few lines of code to extract supplementary information from the Event, but any involved processing should happen in some other method.\nDon\u0026rsquo;t Write \u0026ldquo;Dispatch\u0026rdquo; Event Handlers # I don\u0026rsquo;t know if there is something about FXML that makes this necessary, or if it\u0026rsquo;s just copypasta from some early (bad) tutorial that\u0026rsquo;s become ubiquitous. There are also lots of articles and tutorials answering the question, \u0026ldquo;How do I know which button was clicked?\u0026rdquo;.\nIn this pattern (anti-pattern, actually), the idea is to treat the triggering Node as data, extract it from the Event and then use if or switch statements to determine what to do.\nNow, take a look at this horrible code:\npublic class DispatchHandlerExample extends Application { public static void main(String[] args) { launch(args); } private final IntegerProperty count = new SimpleIntegerProperty(10); private final EventHandler\u0026lt;ActionEvent\u0026gt; counter = new EventHandler\u0026lt;\u0026gt;() { @Override public void handle(ActionEvent event) { Button button = ((Button) (event.getTarget())); switch (button.getText()) { case \u0026#34;Ten\u0026#34; -\u0026gt; count.set(count.get() + 10); case \u0026#34;Twenty\u0026#34; -\u0026gt; count.set(count.get() + 20); } } }; @Override public void start(Stage primaryStage) { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { VBox results = new VBox(20); Label countLabel = new Label(); countLabel.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(count.get()), count)); results.setPadding(new Insets(30)); Button buttonTen = new Button(\u0026#34;Ten\u0026#34;); Button buttonTwenty = new Button(\u0026#34;Twenty\u0026#34;); buttonTen.setOnAction(counter); buttonTwenty.setOnAction(counter); results.getChildren().addAll(countLabel, new HBox(10, buttonTen, buttonTwenty)); return results; } } In this code there is just one EventHandler for the screen and all of the triggered Events are going to be funnelled through this single handler. The handler then figures out which Button triggered it and it will then update the counter accordingly.\nIn this case, the Text property of the Buttons is being used to determine which button was clicked. The cast to Button is problematic, also. It would also be possible to use Labeled instead, but this still excludes any Control that fires ActionEvent but doesn\u0026rsquo;t implement Labeled.\nShockingly, this is offered as a \u0026ldquo;good\u0026rdquo; method to achieve this in many tutorials, even though it creates a huge amounts of problems. It\u0026rsquo;s also possible to use the UserData field of Node to store a unique identifier which you can access from the EventHandler. This still ties the EventType to ActionEvent, so if you wanted to trigger the increment some other way, like by detecting each time the mouse moved into a Node, you\u0026rsquo;d need to create a new EventHandler.\nNo matter how it\u0026rsquo;s done, this is the most complicated approach to implementing the button clicks.\nConsider this instead:\npublic class NotDispatchHandlerExample extends Application { public static void main(String[] args) { launch(args); } private final IntegerProperty count = new SimpleIntegerProperty(10); @Override public void start(Stage primaryStage) { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); } private Region createContent() { VBox results = new VBox(20); Label countLabel = new Label(); countLabel.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(count.get()), count)); results.setPadding(new Insets(30)); Button buttonTen = new Button(\u0026#34;Ten\u0026#34;); buttonTen.setOnAction(evt -\u0026gt; incrementCount(10)); Button buttonTwenty = new Button(\u0026#34;Twenty\u0026#34;); buttonTwenty.setOnAction(evt -\u0026gt; incrementCount(20)); results.getChildren().addAll(countLabel, new HBox(10, buttonTen, buttonTwenty)); return results; } private void incrementCount(int increment) { count.set(count.get() + increment); } } So the correct answer to, \u0026ldquo;How do I know which button triggered an EventHandler?\u0026rdquo;, is \u0026ldquo;Because each button has its own, unique, EventHandler\u0026rdquo;\nDon\u0026rsquo;t Pass EventHandlers Between Classes # This is an easy trap to fall into, especially if you\u0026rsquo;re trying to add structure to your application and implement MVC or some other framework that separates processing from the UI.\nAn EventHandler is \u0026ldquo;processing\u0026rdquo;, right? So shouldn\u0026rsquo;t it be defined in the Controller and passed to the View?\nWell, not so much.\nAn EventHandler is a UI element which provides an interface to facilitate processing. It belongs in the View.\nThe Complicated Stuff # This part is at the bottom because you shouldn\u0026rsquo;t even read it if any of the information above confused you. 99% of the time you don\u0026rsquo;t need to know anything below this line, and you can happily build JavaFX applications without even being aware of it.\nGenerally speaking, the source of an Event is usually the Node at which the EventHandler is defined. In other words, if you have, say, a Button and you want to capture its ActionEvent, you\u0026rsquo;ll call the setOnAction() method of that Button to install the EventHandler.\nBut it doesn\u0026rsquo;t have to be done that way!\nThe Event Chain # In virtually any case where you are going to encounter Events, the source of each Event is going to be contained inside of something else. And that something else might be contained in another Node, and so on, with everything eventually contained inside of a Scene which is in turn, contained inside a Stage.\nNow, any number of those containing objects can have an EventHandler installed to respond to any Event generated by any of the Nodes contained inside them.\nIt\u0026rsquo;s even possible that one of those Nodes can swallow up the Event so that it never triggers the source Node's EventHandler!\nThe documentation makes a big deal about \u0026ldquo;Target Selection Phase\u0026rdquo;, but you can probably just assume that the \u0026ldquo;target\u0026rdquo; is going to be the source Node. It\u0026rsquo;s a fair bet.\nEvent Capturing Phase # The Event is actually passed to the containing Stage first, then to the Scene and then to the root Node of the Scene. After that it is passed to through the levels of containers until it reaches the target Node. Each one of those Nodes might have an EventHandler that would respond to the Event.\nFor the \u0026ldquo;Event Capturing Phase\u0026rdquo;, the EventHandlers are installed on the Nodes using the addEventFilter() method. A Node can have multiple EventFilters defined, and each EventFilter specifies the type of Event to which it will respond.\nTo make matters more complicated, the handlers for EventFilters use the same interface, EventHandler. You\u0026rsquo;ll see how this is confusing in the next section\u0026hellip;\nEvent Bubbling Phase # If you image the \u0026ldquo;Event Capturing Phase\u0026rdquo; as travelling down the containing Nodes from the top, the \u0026ldquo;Event Bubbling Phase\u0026rdquo; is the reverse (I guess it \u0026ldquo;bubbles up\u0026rdquo;, back to the Stage). Starting with the target Node, it\u0026rsquo;s EventHandler will be executed (if there is one), and then the Event is passed to the Node containing the target, and so on, right back up to the Stage.\nFor the \u0026ldquo;Event Bubbling Phase\u0026rdquo;, EventHandlers are installed on Nodes using the addEventHandler() and setEventHandler(), as well methods like setOnAction(). If the target Node has both an EventFilter and an EventHandler, the EventFilter will be executed first.\nAn Example # public class ChainedEvents extends Application { public static void main(String[] args) { launch(args); } private final StringProperty text = new SimpleStringProperty(\u0026#34;\u0026#34;); @Override public void start(Stage primaryStage) { Scene scene = new Scene(createScene()); primaryStage.setScene(scene); scene.addEventFilter(Event.ANY, evt -\u0026gt; logHandler(\u0026#34;Filter\u0026#34;, \u0026#34;Scene\u0026#34;, evt)); scene.addEventHandler(Event.ANY, evt -\u0026gt; logHandler(\u0026#34;Handler\u0026#34;, \u0026#34;Scene\u0026#34;, evt)); primaryStage.addEventFilter(Event.ANY, evt -\u0026gt; logHandler(\u0026#34;Filter\u0026#34;, \u0026#34;Stage\u0026#34;, evt)); primaryStage.addEventHandler(Event.ANY, evt -\u0026gt; logHandler(\u0026#34;Handler\u0026#34;, \u0026#34;Stage\u0026#34;, evt)); primaryStage.show(); } private Region createScene() { BorderPane results = new BorderPane(); addHandlers(results, \u0026#34;BorderPane\u0026#34;); TextArea textArea = new TextArea(); textArea.setStyle(\u0026#34;-fx-font-family: \u0026#39;monospaced\u0026#39;;\u0026#34;); textArea.textProperty().bind(text); textArea.setMinWidth(700); results.setRight(textArea); Button button = new Button(\u0026#34;Click Me!\u0026#34;); addHandlers(button, \u0026#34;Button\u0026#34;); StackPane stackPane = new StackPane(button); addHandlers(stackPane, \u0026#34;StackPane\u0026#34;); results.setCenter(stackPane); results.setMinHeight(800); return results; } private void addHandlers(Node node, String name) { node.addEventFilter(Event.ANY, evt -\u0026gt; logHandler(\u0026#34;Filter\u0026#34;, name, evt)); node.addEventHandler(Event.ANY, evt -\u0026gt; logHandler(\u0026#34;Handler\u0026#34;, name, evt)); } private void logHandler(String handlerType, String name, Event event) { if (event.getEventType().equals(MouseEvent.MOUSE_MOVED)) return; if (event.getEventType().equals(MouseEvent.MOUSE_ENTERED_TARGET)) return; if (event.getEventType().equals(MouseEvent.MOUSE_EXITED_TARGET)) return; String time = LocalTime.now().format(DateTimeFormatter.ofPattern(\u0026#34;hh:mm:ss:SSS\u0026#34;)); String className = event.getTarget().getClass().getSimpleName(); String formatted = String.format(\u0026#34;%-13s %-15s %-20s %-10s %-15s\u0026#34;, time, name, event.getEventType(), handlerType, className); System.out.println(formatted); text.set(formatted + \u0026#34;\\n\u0026#34; + text.get()); } } Which looks like this, when it runs and button is clicked:\nThis is what\u0026rsquo;s going on here:\nWe have a Stage which holds a Scene which holds a BorderPane which holds a StackPane which holds a Button. That\u0026rsquo;s the chain of containers from the top down to the ActionEvent source. There are EventFilters and EventHandlers on every one of those objects, so that we can see how the Events are propagated and handled. The output in the TextArea is in reverse chronological order.\nYou should notice:\nThere\u0026rsquo;s a lot of Events, not just the Button ActionEvent. The code to remove the mouse movement Events was necessary to prevent there being just too much output. At 5:27:58 the chain for the click on the Button starts, with MOUSE_PRESSED caught by the Stage filter. The Button ActionEvent starts when the mouse button is released. The MOUSE_RELEASED handler on the Button seems to happen after the ActionEvent has been handled by all of the objects. There are three mouse based Events: MOUSE_PRESSED, MOUSE_RELEASED and MOUSE_CLICKED. They appear to be independent from the Button's ActionEvent. All of mouse based Events appear to be consumed by the EventHandler on the Button, since they don\u0026rsquo;t bubble up any further. The mouse based Events all have a target of LabeledText. This is because I clicked right on the middle of the Button, on the words \u0026ldquo;Click Me!\u0026rdquo;. Button is just a Region and it contains Nodes inside it to hold the contents. The mouse clicks targeted these inner Nodes of Button. Look at the MOUSE_ENTERED Events. The mouse enters all of the Nodes from Scene to Button in one movement, and it seems to handle each of those separately, without passing through a successively longer chain for each source, which makes sense. Event Consumption # The last twist is that any one of those EventFilters or EventHandlers on any of those Nodes can invoke the consume() method on the Event and swallow it up. This prevents any other Nodes further along the \u0026ldquo;Capture\u0026rdquo; and \u0026ldquo;Bubbling\u0026rdquo; phases from seeing the Event.\nI took the code above and changed this line:\nscene.addEventFilter(Event.ANY, evt -\u0026gt; logHandler(\u0026#34;Filter\u0026#34;, \u0026#34;Scene\u0026#34;, evt)); to:\nscene.addEventFilter(Event.ANY, evt -\u0026gt; { logHandler(\u0026#34;Filter\u0026#34;, \u0026#34;Scene\u0026#34;, evt); if (evt.getEventType().equals(ActionEvent.ACTION)) { evt.consume(); } }); This will cause the Scene to consume the ActionEvent in the \u0026ldquo;Event Capturing Phase\u0026rdquo;, so that the Event never gets to the Button (or anything contained in the Stage).\nThe output now looks like this:\nWhich is what you would expect. All of the Events propagate through all of the Nodes except the ActionEvent from the Button which is seen by just the Stage, and then the Scene, which consumes it.\nConclusion # There are some practical applications of this technique. But if you are building a standard desktop business application and your design depends on deep understanding of \u0026ldquo;Event Capturing and Bubbling Up\u0026rdquo; you\u0026rsquo;re probably deep in the weeds and heading in the wrong direction. Chances are you don\u0026rsquo;t know about some feature of JavaFX that you should be using instead. You should look for alternatives.\n","date":"3 January 2022","externalUrl":null,"permalink":"/javafx/elements/events/","section":"JavaFX","summary":"Everything you need to know about Events.","title":"All About Events","type":"javafx"},{"content":"In Part 1 of this series, we looked at how to use TextFormatter to customize the behaviour of a TextField to handle specialized data entry. In this part we\u0026rsquo;ll build a filter and converter which will handle decimal data input with a fixed number of decimal places.\nThe Objective # The best way to see how TextFormatter works is to walk through the creation of a TextField customized to accept only a certain kind of input. For this example, we\u0026rsquo;ll build a decimal input field with a fixed number of decimal places. To make it interesting, we\u0026rsquo;ll stipulate the following requirements:\nOnly a set amount of decimal places are allowed If the data entry would result in too many decimal places then some will be trimmed out: If the data entry is at the very end of the number, then the first decimal place will be removed If the data entry is not at the very end of the number, then the last decimal place will be removed If the data entry would result in too few decimal places, a \u0026ldquo;0\u0026rdquo; will be appended to the end of the number When the TextField gains focus, the whole number portion of the field will be selected. When the decimal point key is pressed: If the cursor is currently in the whole number portion, then the decimal portion will be selected. If the cursor is currently in the decimal portion, then the whole number portion will be selected. When the \u0026ldquo;-\u0026rdquo; key is pressed, no matter where the cursor is: If the number is currently negative, then the \u0026ldquo;-\u0026rdquo; will be removed from the beginning of the number and the cursor will remain where it is. If the number is currently positive, then a \u0026ldquo;-\u0026rdquo; will be placed at the beginning of the number, and the cursor will remain in the same position. In other words, this will functionally split the contents of the TextField into two parts with the \u0026lt;.\u0026gt; key used to toggle between them. The \u0026lt;-\u0026gt; key can be pressed when the cursor is in any place in either portion and it will toggle a negative sign at the beginning of the number. Any data entry that would cause the number to have the wrong number of decimal places will be automatically corrected by scrolling off one of the digits, or appending a \u0026ldquo;0\u0026rdquo; to the number.\nIs this a good way to design a fixed place decimal field? # I\u0026rsquo;m not sure.\nOn one hand, it\u0026rsquo;s highly functional and works smoothly. If someone just types \u0026ldquo;1234.56\u0026rdquo; when there are two decimal places allowed, it will result in \u0026ldquo;1234.56\u0026rdquo;, as expected.\nThe rest of the actions, however, are a little counter-intuitive and would require explanation or some exploration on the part of the user. That\u0026rsquo;s probably not the best design, especially if it\u0026rsquo;s deployed in an application with a lot of single-time users.\nCaution Any UI design that requires explanation or training to use is probably not awesome.\nAll that aside, it is a good example to explore how you can use TextFormatter to create a much more sophisticated input control out of a simple TextField.\nThe Converter # We\u0026rsquo;ll start with the converter since it\u0026rsquo;s the easiest part:\npublic class FixedDecimalConverter extends DoubleStringConverter { private final int decimalPlaces; public FixedDecimalConverter(int decimalPlaces) { this.decimalPlaces = decimalPlaces; } @Override public String toString(Double value) { return String.format(\u0026#34;%.\u0026#34; + decimalPlaces + \u0026#34;f\u0026#34;, value); } @Override public Double fromString(String valueString) { if (valueString.isEmpty()) { return 0d; } return super.fromString(valueString); } } The two things that make this different from the DoubleStringConverter is that it always forces the toString() result to have the correct number of decimal places, and that it turns an empty input string into a zero in the fromString() method.\nThe Filter # The Filter is an implementation of UnaryOperator\u0026lt;TextFormatter.Change\u0026gt;, which is used to validate and modify any change that the user makes in the TextField. UnaryOperator is just a Function which returns the same data type as it takes. So in the case of the Filter, it takes TextFormatter.Change as the input value, and returns a TextFormatter.Change as its result.\nThe Structure of TextFormatter.Change # You can think of TextFormatter.Change as clean way to capture each key presses and mouse actions in the TextField, with JavaFX itself handling all of the troublesome details about capturing and applying the keystrokes.\nNote Using TextFormatter.Change means you don\u0026rsquo;t need to fuss about capturing KeyEvents.\nThe Filter is a step between capturing the user action and applying it, and that allows us to write logic that can not just filter out actions, but change them and the way that they impact the TextField.\nTextFormatter.Change contains a lot more information than just the keystroke itself. Here are some of the essential components of TextFormatter.Change:\nThe \u0026ldquo;Change\u0026rdquo; Part of the Change:\nThese are the elements of the change itself. All of these can be manipulated to alter the impact of the change:\nThe text of the change. The range that the change applies to in the TextField contents The new positions of the anchor and caret The current TextField value:\nThese are accessed through methods that are called, getControl...(). TextFormatter.Change refers to the TextField as the \u0026ldquo;Control\u0026rdquo;, because it can be used with more than just TextField.\nThe current text in the TextField The current position of the caret and the anchor The new TextField value. This is access through the getControlNewText() method.\nYou can see what the impact of the change will be on the text value held in the TextField.\nThere is one caveat when manipulating the change:\nWarning The value returned by getControlNewText() is updated immediately when the change has been modified.\nSo if you need to reference the impact of the original change after you\u0026rsquo;ve modified it, you\u0026rsquo;ll need to make a copy before you start.\nFiltering the Change # The first thing to do is to ensure that the results of the change are a valid decimal string, so we\u0026rsquo;ll use regex to check that:\npublic class FixedDecimalFilter implements UnaryOperator\u0026lt;TextFormatter.Change\u0026gt; { @Override public TextFormatter.Change apply(TextFormatter.Change change) { if (change.getControlNewText().matches(\u0026#34;-?([0-9]*)?(\\\\.[0-9]*)?\u0026#34;)) { return change; } return null; } } Here you can see we\u0026rsquo;ve created a new class which implements UnaryOperator, and then supplies the logic for the apply() method. The code in there now will act as the backstop to the filter, making sure that whatever the final version of the change looks like, it won\u0026rsquo;t violate the requirement to have a decimal string. This code will allow any number of decimal places, but we\u0026rsquo;ll see how the rest of the controls in the Filter will handle this nicely.\nSo that we can test it, here\u0026rsquo;s a \u0026ldquo;Main\u0026rdquo; class which will launch a window with some TextFields:\npublic class FixedDecimalMain extends Application { private ObjectProperty\u0026lt;Double\u0026gt; valueProperty = new SimpleObjectProperty\u0026lt;\u0026gt;(0d); public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(new FixedDecimalMain.TestPane(), 300, 100); valueProperty.addListener(((observable, oldValue, newValue) -\u0026gt; { System.out.println(\u0026#34;Value changed -\u0026gt; Old Value: \u0026#34; + oldValue + \u0026#34;, New Value: \u0026#34; + newValue); })); primaryStage.setScene(scene); primaryStage.show(); } public class TestPane extends BorderPane { public TestPane() { TextField textField = new TextField(); TextFormatter\u0026lt;Double\u0026gt; textFormatter = new TextFormatter(new FixedDecimalConverter(2), 78, new FixedDecimalFilter()); textFormatter.valueProperty().bindBidirectional(valueProperty); textField.setTextFormatter(textFormatter); setCenter(new VBox(10, new HBox(6, new Text(\u0026#34;TextField 1\u0026#34;), textField), new HBox(6, new Text(\u0026#34;TextField 2\u0026#34;), new TextField()))); } } } Now, if you run this you\u0026rsquo;ll see that you can only enter characters which result in a valid decimal string. Otherwise, your keystrokes are swallowed up by the Filter.\nSelecting the Whole Number Part on Focus # The first thing you\u0026rsquo;ll notice if you run this is that the entire contents of the TextField are selected when the control gains focus. This isn\u0026rsquo;t what are requirements state, only the whole number part of the string should be selected. How to do this?\nIn order to investigate this, let\u0026rsquo;s add some console output to monitor what\u0026rsquo;s coming into the Filter:\npublic class FixedDecimalFilter implements UnaryOperator\u0026lt;TextFormatter.Change\u0026gt; { @Override public TextFormatter.Change apply(TextFormatter.Change change) { System.out.print(\u0026#34;Change: \u0026gt;\u0026#34; + change.getText() + \u0026#34;\u0026lt; Range: [\u0026#34; + change.getRangeStart() + \u0026#34;, \u0026#34; + change.getRangeEnd() + \u0026#34;]\u0026#34;); System.out.print(\u0026#34; Selection: [\u0026#34; + change.getSelection().getStart() + \u0026#34;, \u0026#34; + change.getSelection().getEnd() + \u0026#34;]\u0026#34;); System.out.println(\u0026#34; Anchor: \u0026#34; + change.getAnchor() + \u0026#34; Caret: \u0026#34; + change.getCaretPosition()); System.out.println(\u0026#34; TextField: \u0026gt;\u0026#34; + change.getControlText() + \u0026#34;\u0026lt;, L: \u0026#34; + change.getControlText().length()); if (change.getControlNewText().matches(\u0026#34;-?([0-9]*)?(\\\\.[0-9]*)?\u0026#34;)) { return change; } return null; } } No when you launch the application, you get the following console output:\nChange: \u0026gt;\u0026lt; Range: [0, 0] Selection: [0, 4] Anchor: 4 Caret: 0 TextField: \u0026gt;0.00\u0026lt;, L: 4 And if you hit Tab twice, to toggle the focus to the second TextField and back, you\u0026rsquo;ll end up with the following output:\nChange: \u0026gt;\u0026lt; Range: [0, 0] Selection: [0, 4] Anchor: 4 Caret: 0 TextField: \u0026gt;0.00\u0026lt;, L: 4 Change: \u0026gt;\u0026lt; Range: [0, 0] Selection: [0, 0] Anchor: 0 Caret: 0 TextField: \u0026gt;0.00\u0026lt;, L: 4 Change: \u0026gt;\u0026lt; Range: [0, 0] Selection: [0, 4] Anchor: 4 Caret: 0 TextField: \u0026gt;0.00\u0026lt;, L: 4 So it appears that when focus is gained on the TextField, it generates a Change with empty text and with the anchor set at the end of the string and the caret at the beginning. When focus is lost, it sends another Change with empty text and both the anchor and the caret set to the beginning of the string.\nIt\u0026rsquo;s also possible to select the contents of a TextField by double-clicking on it. This is the output that generates:\nChange: \u0026gt;\u0026lt; Range: [0, 0] Selection: [0, 0] Anchor: 0 Caret: 0 TextField: \u0026gt;0.00\u0026lt;, L: 4 Change: \u0026gt;\u0026lt; Range: [4, 4] Selection: [0, 4] Anchor: 0 Caret: 4 TextField: \u0026gt;0.00\u0026lt;, L: 4 The first line is when it responds to the first click, and the second when it processes it as a double click. It\u0026rsquo;s almost the same as Tab, but the caret and the anchor are reversed.\nIt looks like the best way to detect this is to check for a Change with empty text and a range which includes the entire contents of the TextField. Let\u0026rsquo;s put some code in the filter to catch that and change it to select just the whole number portion:\npublic class FixedDecimalFilter implements UnaryOperator\u0026lt;TextFormatter.Change\u0026gt; { @Override public TextFormatter.Change apply(TextFormatter.Change change) { System.out.print(\u0026#34;Change: \u0026gt;\u0026#34; + change.getText() + \u0026#34;\u0026lt; Range: [\u0026#34; + change.getRangeStart() + \u0026#34;, \u0026#34; + change.getRangeEnd() + \u0026#34;]\u0026#34;); System.out.print(\u0026#34; Selection: [\u0026#34; + change.getSelection().getStart() + \u0026#34;, \u0026#34; + change.getSelection().getEnd() + \u0026#34;]\u0026#34;); System.out.println(\u0026#34; Anchor: \u0026#34; + change.getAnchor() + \u0026#34; Caret: \u0026#34; + change.getCaretPosition()); System.out.println(\u0026#34; TextField: \u0026gt;\u0026#34; + change.getControlText() + \u0026#34;\u0026lt;, L: \u0026#34; + change.getControlText().length()); if (change.getText().isEmpty() \u0026amp;\u0026amp; isEverythingSelected(change)) { change.selectRange(0, change.getControlText().indexOf(\u0026#34;.\u0026#34;)); return change; } if (change.getControlNewText().matches(\u0026#34;-?([0-9]*)?(\\\\.[0-9]*)?\u0026#34;)) { return change; } return null; } private boolean isEverythingSelected(TextFormatter.Change change) { return (change.getSelection().getStart() == 0) \u0026amp;\u0026amp; (change.getSelection().getEnd() == change.getControlText().length()); } } This is our first example of manipulating the Change to alter the behaviour of the TextField. If any action attempts to select the entire string, it will intercept it and select the whole number portion instead. Also, since this can never alter the TextField string, there\u0026rsquo;s no point in running it through the regex to see if the Change is valid, so we return the altered Change immediately.\nHandling the \u0026ldquo;.\u0026rdquo; Key # Now we want the \u0026ldquo;.\u0026rdquo; key to toggle the input between the whole number and decimal portions of the string, selecting the entire part. So we\u0026rsquo;ll need to capture the decimal point Change and manipulate it. Here\u0026rsquo;s the logic that will do that:\nint decimalPos = change.getControlText().indexOf(\u0026#34;.\u0026#34;); if (change.getText().equals(\u0026#34;.\u0026#34;)) { change.setText(\u0026#34;\u0026#34;); change.setRange(0, 0); if (change.getControlCaretPosition() \u0026lt;= decimalPos) { change.setCaretPosition(decimalPos + 1); change.setAnchor(change.getControlText().length()); } else { change.setCaretPosition(decimalPos); change.setAnchor(0); } return change; } We\u0026rsquo;ve introduced decimalPos because it we\u0026rsquo;re going to be using it in two places now.\nFirst, we clear the text in the Change, since we don\u0026rsquo;t actually want to add a \u0026ldquo;.\u0026rdquo; to the string. Then we set the Change range to (0,0) since we don\u0026rsquo;t want to be removing any text either. So now the Change can\u0026rsquo;t actually change the TextField string.\nNext, we need to determine which portion of the number the caret is currently in, the whole number part or the decimal part. We do this by comparing it\u0026rsquo;s position to the position of the \u0026ldquo;.\u0026rdquo; in the string. Note that we care about where the caret was BEFORE the change would have been applied, so we\u0026rsquo;re going to get it\u0026rsquo;s position from getControlText().\nFinally, we set the caret just to either side of the \u0026ldquo;.\u0026rdquo;, and then set anchor to either the beginning or the end of the string. This will create a selection in the TextField.\nOnce again, since we\u0026rsquo;re not changing the contents of the TextField string, there\u0026rsquo;s no need to run it through the regex to see if it\u0026rsquo;s in the correct format.\nNow the Filter looks like this, with the new logic inserted and the console output removed:\npublic class FixedDecimalFilter implements UnaryOperator\u0026lt;TextFormatter.Change\u0026gt; { @Override public TextFormatter.Change apply(TextFormatter.Change change) { int decimalPos = change.getControlText().indexOf(\u0026#34;.\u0026#34;); if (change.getText().isEmpty() \u0026amp;\u0026amp; isEverythingSelected(change)) { change.selectRange(0, decimalPos); return change; } if (change.getText().equals(\u0026#34;.\u0026#34;)) { change.setText(\u0026#34;\u0026#34;); change.setRange(0, 0); if (change.getControlCaretPosition() \u0026lt;= decimalPos) { change.setCaretPosition(decimalPos + 1); change.setAnchor(change.getControlText().length()); } else { change.setCaretPosition(decimalPos); change.setAnchor(0); } return change; } if (change.getControlNewText().matches(\u0026#34;-?([0-9]*)?(\\\\.[0-9]*)?\u0026#34;)) { return change; } return null; } private boolean isEverythingSelected(TextFormatter.Change change) { return (change.getSelection().getStart() == 0) \u0026amp;\u0026amp; (change.getSelection().getEnd() == change.getControlText().length()); } } That\u0026rsquo;s all for this part. We still need to handle the scrolling of the decimal portion as digits are added or removed, and the \u0026ldquo;-\u0026rdquo; sign. We\u0026rsquo;ll do that in Part 3.\n","date":"11 May 2021","externalUrl":null,"permalink":"/javafx/elements/nodes/textformatter/part-2/","section":"JavaFX","summary":"A more in-depth look into how to build a TextFormatter that can be used to turn a TextField into sophisticated input field. We’ll look at designing a TextFormatter that is tailored to handle fixed place decimal input, using the decimal point itself as a control to toggle between the ordinal and mantissa of the number.","title":"TextFields and TextFormatter - Part 2","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # Layout classes like HBox and VBox Some basic concepts about configuring layouts. We\u0026rsquo;ll cover spacing, alignment and padding in this part. The Pos and Insets classes. A little about TextField Adding Some Simple User Input # The screen from Part 1 is really simple, and doesn\u0026rsquo;t do much except display \u0026ldquo;Hello World\u0026rdquo; in a tiny little window. Let\u0026rsquo;s add some elements that will actually do something.\nThe Code So Far # Our code at the end of Part 1 looked like this:\npublic class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContents()); primaryStage.setScene(scene); primaryStage.show(); } private Region createContents() { return new Label(\u0026#34;Hello World\u0026#34;); } } With this output:\nThe Screen is Too Cramped # You can barely read the, \u0026ldquo;Hello World\u0026rdquo; since the screen is so tiny. We can fix that by setting the initial size of the Scene in it\u0026rsquo;s constructor. We\u0026rsquo;ll change the line to:\nScene scene = new Scene(createContents()); to:\nScene scene = new Scene(createContents(),400, 200); This sets the initial window size to 400 pixels wide, and 200 pixels tall. When you run this it looks like this:\nThat\u0026rsquo;s somewhat of an improvement, even though the \u0026ldquo;Hello World\u0026rdquo; is still tucked over way to the left. We\u0026rsquo;ll look at that kind of thing in a little bit.\nThat\u0026rsquo;s about all \u0026ldquo;Hello World\u0026rdquo; is good for. Let\u0026rsquo;s move on to something more interesting\u0026hellip;\nAdding User Input # Over the next few parts of this guide we\u0026rsquo;ll change this screen to let the user enter their name and click on a Button. When the Button is clicked, we\u0026rsquo;ll update the contents of the Label to be \u0026ldquo;Hello {name}\u0026rdquo;. The first step is to provide a way for the user to enter their name.\nAdding a Prompt and an Input Field # The prompt is just going to be another Label that says \u0026ldquo;Name:\u0026rdquo;, and the input field is a control called a \u0026ldquo;TextField\u0026rdquo;. TextField is the basic control for simple text input in a screen. We want the prompt and the input to be in a single row, so we are going to put them in an HBox. For now, we\u0026rsquo;ll scrap the output label:\npublic class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContents(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContents() { return new HBox(new Label(\u0026#34;Name:\u0026#34;), new TextField(\u0026#34;\u0026#34;)); } } What\u0026rsquo;s an HBox? # HBox is an example of a \u0026ldquo;layout\u0026rdquo; class. It\u0026rsquo;s a Region subtype Node that holds other Nodes inside it. There are a number of different layout classes, like BorderPane, ScrollPane, GridPane, VBox and HBox. Each one has different characteristics and is designed for different methods of organizing their contents.\nHBox presents its contents in a horizontal row, with the first item at the far left and each new item added to the right. VBox is similar except that it shows it\u0026rsquo;s contents arranged vertically, from the top down. In this case, I\u0026rsquo;ve chosen to populate the contents of the HBox in its constructor.\nHBox and VBox are probably the most common, versatile and easy to use layout classes in JavaFX. Get used to seeing them because they are used a lot.\nMaking it Look Nicer # If you run this code, you\u0026rsquo;ll see it looks like this:\nWell, it\u0026rsquo;s all squished up in the top left corner, and the \u0026ldquo;Name:\u0026rdquo; prompt seems to be a little higher than the TextField. So let\u0026rsquo;s fix that.\nThis is done by configuring the layout of the containing HBox. The code ends up looking like this:\npublic class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContents(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContents() { HBox results = new HBox(new Label(\u0026#34;Name:\u0026#34;), new TextField(\u0026#34;\u0026#34;)); results.setSpacing(6); results.setPadding(new Insets(0,0,0,50)); results.setAlignment(Pos.CENTER_LEFT); return results; } } The first thing that you\u0026rsquo;ll notice is that we\u0026rsquo;re now instantiating the HBox as a variable so that we can configure it through some of its configuration methods. Let\u0026rsquo;s look at what we\u0026rsquo;ve done:\nsetSpacing() : This defines the space between the Nodes contained inside the HBox\nsetPadding() : This defines the space between the edges of the HBox and its contents.\nInsets : This class is used to define space around or inside a Node. Insets has two constructors. One takes a single number which is applied to all sides of the Node. In this case, we\u0026rsquo;ve used the second constructor, which takes a separate number for each side in the order: Top, Right, Bottom and Left.\nsetAlignment() and Pos : This defines how the Nodes contained in the HBox will be oriented in relation to the HBox. In this case, they are going to be left aligned and vertically centred. The default is Pos.TOP_LEFT, which explains why the Label looked higher than the TextField. TextField is slightly taller than Label in its default styling.\nWhen you run this, you\u0026rsquo;ll see it looks almost like this:\nI\u0026rsquo;ve added an extra line to put a red border around the HBox. It\u0026rsquo;s not included in the code above because it uses concepts we haven\u0026rsquo;t looked at yet. But since HBox is usually invisible, it helps to have a border to understand how it works. We\u0026rsquo;ll get to the border later on.\nWhen you see the red border it becomes clear that the HBox is occupying all of the space available to it, not just the amount of space required to hold its contents. This explains why everything was originally up in the top left corner with the default Pos.TOP_LEFT alignment, and why it\u0026rsquo;s now moved to the middle of the window. It\u0026rsquo;s not all the way over at the left because we set a 50 pixel padding on the left side of the HBox.\nUnderstanding how and when certain of the layout classes will expand in one direction or another to fill all of the space available is one of the more advanced aspects of JavaFX that you\u0026rsquo;ll eventually have to be comfortable with. In the meantime, you can be assured that all of the layout classes act consistently and in predictable ways.\n","externalUrl":null,"permalink":"/beginners/part2/","section":"JavaFX","summary":"Some basics about building layouts and making your screens look the way you want.","title":"Part 2 - Configuring Layouts","type":"javafx"},{"content":" Introduction # This is the third part of an article describing how to set up whole-home audio using Mopidy and SnapCast. If you haven\u0026rsquo;t already read them should should probably read SnapCast Part I and SnapCast Part II first.\nSo far, we\u0026rsquo;ve installed and configured the SnapCast server, which will broadcast the music to the SnapCast clients. We\u0026rsquo;ve looked at how to set up a RasberryPi Zero (RPi0), install a Digital to Analog Converter (DAC) on it, install the SnapCast client service, and then configure the whole thing so that it can receive a stream from the SnapCast server and play it through the DAC.\nWhat we need now is a way to pull an audio stream from the Internet, and transfer it over to our SnapCast server to broadcast internally. For this we\u0026rsquo;ll use an application called Mopidy.\nMopidy is a streaming client designed to be used on a \u0026ldquo;headless\u0026rdquo; server. It has a variety of plugins to provide a browser interface and to allow it to connect to diffent kinds of streaming services. It also has a REST API that we will be using to provide some simple automation for the whole-home audio.\nIn my house we are mostly streaming from SomaFM.com, which is an excellent service and has a variety of stations to choose from. Personally, we like the one called \u0026ldquo;Groove Salad\u0026rdquo;. That\u0026rsquo;s what I\u0026rsquo;ll be showing how to set up. However, if you are interested in streaming from any other service, this guide should get you going on that too.\nMopidy Server # Mopidy is pretty easy to install, but there are a few pain points along the way\u0026hellip;\nStart by using apt to install Mopidy. This seems to give the latest version:\n$ sudo apt update $ sudo apt install mopidy You can check the version that you get:\n$ mopidy --version Mopidy 3.4.2 Which, at the time of writing, was the latest version, although from October 2023.\nYou can use apt to see all of the Mopidy extensions that are available that way:\n$ sudo apt search mopidy We are going to need a couple of extensions, otherwise we won\u0026rsquo;t be able to do much with Mopidy. The first we will install is the extension for SomaFM. If you are going to be using some other service for a source, you\u0026rsquo;ll need to install the extension for it. However, I would still recommend that you install the SomaFM add-on because then you\u0026rsquo;ll have something that you can test before you go trying your own thing.\n$ sudo apt install mopidy-somafm Next, we\u0026rsquo;ll need a web interface so that we can control everything. I prefer \u0026ldquo;Muse\u0026rdquo; because it integrates some of the controls for SnapCast as well.\nMany of the extensions, like Muse, cannot be installed via apt. You\u0026rsquo;ll need to use a Python utility to do it, and the first thing you\u0026rsquo;ll need to do is install that utility. Here\u0026rsquo;s how you get Muse installed:\n$ sudo apt install python3-pip $ sudo python3 -m pip install Mopidy-Muse $ sudo python3 -m pip install --break-system-packages Mopidy-Muse $ sudo python3 -m pip install --break-system-packages Mopidy-Jellyfin You can see that I\u0026rsquo;ve also installed the Jellyfin add-on as well.\nThat --break-system-packages option is going to generate some ugly warning messages. If you leave it out, you\u0026rsquo;ll get this:\n$ sudo python3 -m pip install Mopidy-Muse error: externally-managed-environment × This environment is externally managed ╰─\u0026gt; To install Python packages system-wide, try apt install python3-xyz, where xyz is the package you are trying to install. If you wish to install a non-Debian-packaged Python package, create a virtual environment using python3 -m venv path/to/venv. Then use path/to/venv/bin/python and path/to/venv/bin/pip. Make sure you have python3-full installed. If you wish to install a non-Debian packaged Python application, it may be easiest to use pipx install xyz, which will manage a virtual environment for you. Make sure you have pipx installed. See /usr/share/doc/python3.13/README.venv for more information. note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages. hint: See PEP 668 for the detailed specification. I\u0026rsquo;m not a Python programmer, and I have absolutely no knowledge of or experience with \u0026ldquo;Python virtual environments\u0026rdquo;. When I first encountered this, I tried to figure it out. I really tried. In the end, I gave up and just went with the --break-system-packages, even though it made me feel uneasy. It hasn\u0026rsquo;t caused any problems, though.\nThe last bit of installation that you\u0026rsquo;ll need to do is to install some decoders for the streaming engine. Mopidy uses something called GStreamer1.0 to do the work. You\u0026rsquo;ll already have GStreamer1.0 installed, but it will need some additional packages to make it work. There\u0026rsquo;s three of them; \u0026ldquo;Good\u0026rdquo;, \u0026ldquo;Bad\u0026rdquo;, and \u0026ldquo;Ugly\u0026rdquo;. I don\u0026rsquo;t know which ones you absolutely need, so I installed all of them:\n$ sudo apt install gstreamer1.0-plugins-good $ sudo apt install gstreamer1.0-plugins-bad $ sudo apt install gstreamer1.0-plugins-ugly That\u0026rsquo;s it for the installation. The last thing you need is the configuration file to make everything work. It\u0026rsquo;s located at /etc/mopidy/mopidy.conf, and when you are done it should look something like this:\n# For information about configuration values that can be set in this file see: # # https://docs.mopidy.com/en/latest/config/ # # Run `sudo mopidyctl config` to see the current effective config, based on # both defaults and this configuration file. [core] restore_state = true [http] hostname=10.93.50.208 [audio] output = audioresample ! audioconvert ! audio/x-raw,rate=48000,channels=2,format=S16LE ! wavenc ! filesink location=/var/local/snapserver/snapfifo mixer_volume = 22 buffer_time = 4000 [somafm] encoding = aac quality = highest [muse] enabled = true mopidy_host = 10.93.50.208 mopidy_port = 6680 mopidy_ssl = false snapcast_host = 10.93.50.208 snapcast_port = 1780 snapcast_ssl = false [jellyfin] hostname = 192.168.1.211 username = mopidy password = PYo78u.p%.MChCTMROE83M\u0026amp;#867F max_bitrate = 100 Obviously, you\u0026rsquo;ll have to change the IP addresses to match you\u0026rsquo;re own setup. The restore_state flag just tells Mopidy to resume doing whatever it was doing before it was restarted.\nThe output tag is every important. Make sure that the \u0026ldquo;filesink location\u0026rdquo; matches the fifo pipe that you created for SnapCast. If you followed my instructions, it should be /var/local/snapserver/snapfifo.\nIf you added any other extensions, you\u0026rsquo;ll probably have to add a section in this file for that extension. Generally, if you follow a link from the Mopidy extension listing, you\u0026rsquo;ll get to a page that has a GitHub link listed just above the section called \u0026ldquo;Installation\u0026rdquo;. Follow that link and the GitHub page should have a sample configuration block on it.\nAt this point, you can restart the Mopidy service to get it to load all of the extensions and read the configuration.\nYou should use systemctl status mopidy -n60 to make sure that it\u0026rsquo;s working properly. If you\u0026rsquo;ve muddled up the configuration, you should get some messages in that display that will tell you that it had problems.\nTesting It All # I\u0026rsquo;d suggest opening up two browser tabs: one with the SnapCast page at port 1780, and the other at the Mopidy page at port 6680, then click into the \u0026ldquo;Muse\u0026rdquo; page.\nOn the SnapCast tab, click on the \u0026ldquo;Play\u0026rdquo; button. Then go to the Muse page, pick \u0026ldquo;Browse\u0026rdquo;, then select \u0026ldquo;SomaFM\u0026rdquo; which will expand the station list. Click on one of the stations, then \u0026ldquo;Play\u0026rdquo;. If all goes well, you should hear music through your browser.\nAutomation # In our house, we want the whole home audio streaming SomaFM\u0026rsquo;s \u0026ldquo;Groove Salad\u0026rdquo; from the time we wake up until we go to bed. When our current dog was just a puppy and we had her spending the night in a crate beside the bed, we needed some background noise so that she wouldn\u0026rsquo;t wake up and start whining whenever someone rolled over or made some small sound. So we started using a Google Home device to play \u0026ldquo;Forest Sounds\u0026rdquo; at night. Over time, we became used to this, and found it filters out street noises and other nighttime distractions.\nWith this new setup, I was looking for a way to have a completely automated, \u0026ldquo;hands-free\u0026rdquo;, solution that would turn on Groove Salad in the morning, and turn on some kind of forest sounds at night. Something that just worked, and that I didn\u0026rsquo;t have to even think about.\nMopidy has a REST API that you can use to control it remotely via HTML POST transactions. It\u0026rsquo;s not particularly well documented. At least, it looks like documentation was started, but never completed. However, there is enough there to figure out how to do some interesting and useful things.\nIn order to investigate how to use the API, on my workstation I downloaded an application called, \u0026ldquo;Bruno\u0026rdquo;, which is an open source alternative to \u0026ldquo;Postman\u0026rdquo;. This application allows you to create REST API calls that you can test and see the results in real time.\nThat\u0026rsquo;s what I did with Mopidy. I poked around trying various API calls to see what would happen, and to figure out the correct syntax and structure. Eventually, I had a number of tests written that would control the Mopidy server, and that would start and stop both GrooveSalad and my forest sounds MP3.\nOne of the nice things about Bruno is that you can export the tests as stand-alone Bash scripts (as well as code in a few programming languages). I copied them out and then put them in /usr/local/bin/mopidy on the Mopidy/Snapcast LXC. Let\u0026rsquo;s have a look at a couple of them:\n$ cat clearTrackList curl --request POST \\ --url http://mopidy.mydomain:6680/mopidy/rpc \\ --header \u0026#39;content-type: application/json\u0026#39; \\ --data \u0026#39;{ \u0026#34;jsonrpc\u0026#34;: \u0026#34;2.0\u0026#34;, \u0026#34;id\u0026#34;: 1, \u0026#34;method\u0026#34;: \u0026#34;core.tracklist.clear\u0026#34; }\u0026#39; This script clears the tracklist out of Mopidy.\n$ cat addGrooveSalad curl --request POST \\ --url http://mopidy.mydomain:6680/mopidy/rpc \\ --header \u0026#39;content-type: application/json\u0026#39; \\ --data \u0026#39;{ \u0026#34;jsonrpc\u0026#34;: \u0026#34;2.0\u0026#34;, \u0026#34;id\u0026#34;: 1, \u0026#34;method\u0026#34;: \u0026#34;core.tracklist.add\u0026#34;, \u0026#34;params\u0026#34;: { \u0026#34;uris\u0026#34;: [ \u0026#34;somafm:channel:/groovesalad\u0026#34; ] } }\u0026#39; This puts the GrooveSalad channel from SomaFM into the playlist.\n$ cat startPlayback curl --request POST \\ --url http://mopidy.mydomain:6680/mopidy/rpc \\ --header \u0026#39;content-type: application/json\u0026#39; \\ --data \u0026#39;{ \u0026#34;jsonrpc\u0026#34;: \u0026#34;2.0\u0026#34;, \u0026#34;id\u0026#34;: 1, \u0026#34;method\u0026#34;: \u0026#34;core.playback.play\u0026#34; }\u0026#39; This script starts the playback.\n$ cat startGrooveSalad /usr/local/bin/mopidy/clearTrackList /usr/local/bin/mopidy/addGrooveSalad /usr/local/bin/mopidy/startPlayback Finally, this one calls the other three back-to-back to start GrooveSalad playing.\nHow to run these? Well, cron of course! [Note: On a later project that I used ChatGPT to troubleshoot, it told me that cron is old-fashioned and that there is a more modern approach using systemd. You may want to pursue that, but I find that cron works just fine. What can I say? I\u0026rsquo;m a dinosaur.]\nI\u0026rsquo;m not going to go into cron in depth, but for those of you who have never heard of it, cron is a service that will run whatever you want on a schedule that you set up. You can specify certain days of the week or of the month, certain minutes of the hour or particular hours that you want your action to run.\nI needed two actions, one to start up GrooveSalad in the morning, and one to start up the forest sounds at night:\n$ sudo crontab -l # Edit this file to introduce tasks to be run by cron. # # Each task to run has to be defined through a single line # indicating with different fields when the task will be run # and what command to run for the task # # To define the time you can provide concrete values for # minute (m), hour (h), day of month (dom), month (mon), # and day of week (dow) or use \u0026#39;*\u0026#39; in these fields (for \u0026#39;any\u0026#39;). # # Notice that tasks will be started based on the cron\u0026#39;s system # daemon\u0026#39;s notion of time and timezones. # # Output of the crontab jobs (including errors) is sent through # email to the user the crontab file belongs to (unless redirected). # # For example, you can run a backup of all your user accounts # at 5 a.m every week with: # 0 5 * * 1 tar -zcf /var/backups/home.tgz /home/ # # For more information see the manual pages of crontab(5) and cron(8) # # m h dom mon dow command 30 7 * * * /usr/local/bin/mopidy/startGrooveSalad 0 21 * * * /usr/local/bin/mopidy/startNatureSounds I\u0026rsquo;ve left in the default comments so that it will look like what you\u0026rsquo;ll see if you do this on your own system.\nThe easiest way to edit /etc/crontab is to just type sudo crontab -e and it\u0026rsquo;ll get you there. We need this to run as root, so sudo is used here. The command crontab -l just lists the crontab file. These two commands run every day, the first one at 7:30 AM, and the second at 9:00 PM.\nHandling Stream Interuptions # The last issue that I had was due to connection issues from SomaFM which caused the stream to stop momentarily. This caused the Mopidy server to throw an error and stop:\nMar 31 15:24:31 snapcast mopidy[25889]: ERROR [MainThread] mopidy.audio.gst GStreamer error: Internal data stream error. or:\nMar 30 07:30:18 snapcast mopidy[25889]: ERROR [MainThread] mopidy.audio.gst GStreamer error: Server does not support seeking. I wish that Mopidy or its SomaFM plugin would be more robust to these disruptions, or have some self-recovery feature built in, but it does not. That meant it was up to me to figure out how to detect these stoppages and restart the stream.\nThe first thing I did was to look for something in the API that would tell me that the stream had stopped. I was hopeful that the \u0026ldquo;status\u0026rdquo; command would be the answer. Unfortunately, when I tested this I found that the status was still listed as \u0026ldquo;playing\u0026rdquo;, from which I assume that the error that stopped stream wasn\u0026rsquo;t causing it to go into some elegant shutdown that updated the status.\nLuckily, I had decided to connect Mopidy to SnapCast via a named pipe. Perhaps if I checked the last update time of that pipe I could see if the stream had stopped?\nThis turned out easy to check. The pipe was at /var/local/snapfifo and a listing shows that it\u0026rsquo;s date and time is right now:\n$ ll snapfifo prw-rw-rw- 1 snapserver snapserver 0 Apr 3 14:54 snapfifo If I stop the stream, then the date/time stops updating. So it looks like this is going to be an answer. I came up with this script:\n$ cat checkPlaybackState #!/bin/bash if (((`date +%s` - `date +%s -r /var/local/snapfifo`) \u0026lt; \u0026#34;5\u0026#34;)) then echo \u0026#34;`date` Still going\u0026#34; \u0026gt;\u0026gt; /var/local/playbackcheck else echo \u0026#34;`date` Not running\u0026#34; \u0026gt;\u0026gt; /var/local/playbackcheck /usr/local/bin/mopidy/startGrooveSalad fi Eventually, I removed the echo commands, since they really aren\u0026rsquo;t needed. In my running version, I have a line that submits a message to my Gotify server in the event that it detects that the stream has stopped.\nI need to run it throughout the day, which means more cron to the rescue:\n*/3 8-20 * * * /usr/local/bin/mopidy/checkPlaybackState The */3 means every 3 minutes, and the 8-20 means between 8:00 AM and 8:00 PM. These hours are inclusive, so the last run will be at 20:57, or 8:57 PM. Then, at 9:00 PM the forest sounds start and that is entirely local, so there\u0026rsquo;s no need to check for stream issues.\nConclusion # Success in these kinds of projects is ususally evaluated by how happy my wife is with how it works. She was getting quite fed up with the Google issues that we were experiencing, and frustrated with the constant drops in the service and times when it simply wouldn\u0026rsquo;t start up again.\nShe considers this to be one the biggest successes of my self-hosting project so far! Hurray!\nIt\u0026rsquo;s a success because it just works, and it never forces its way into the foreground by making you fiddle with it to get it to work. It starts up the music in the morning and the forest sounds at night and you don\u0026rsquo;t have to even think about it. It\u0026rsquo;s just there.\nTo be sure, there were some hiccoughs to start with\u0026hellip;\nThe Raspberry Pi Zero has a woefully weak WiFi tranceiver in it. This caused grief until I sorted out procurement and placement of WiFi access points around the house. Symptoms of this problem were sporadic pauses in the audio of up to 2 seconds.\nThe SnapCast system seems to be dependent on stable connectivity between the server and the clients. At first, I had one of my WiFi access points connected to the wired network through a powerline AV connector. These are notoriously quirky, and can suffer interruptions if someone runs a heavy appliance like a clothes washer or dryer in the house. Once again, the symptoms were sporadic pauses in the audio.\nWhen I shifted AP connectivity over to MoCa (Ethernet over co-ax), all of the issues with sporadic audio pauses largely vanished.\nNow it just runs. If something goes wrong, it fixes itself. Most of the time, the only reason I\u0026rsquo;m even aware that it had a problem is because I get a Gotify alert for it. But even this only happens rarely.\n","date":"22 June 2026","externalUrl":null,"permalink":"/homelab/snapcast/part3/","section":"Homelabs","summary":"The final installment of a 3 part series describing how to set up whole-home audio using SnapCast, Mopidy and Rasberry Pi’s.  In this article, we look at how to install and configure Mopidy as a streaming service, and how to connect it to a SnapCast server.","title":"Whole Home Audio With SnapCast - Part III","type":"homelab"},{"content":" Introduction # ObservableLists (and ObservableSets, and ObservableMaps, for that matter), are designed to invalidate and trigger Listeners when items are add or removed from them. Screen elements like ListView, TableView and the pop-ups in ComboBox will automatically update when the ObservableLists that back them are updated.\nBut what about those cases where your ObservableLists are made of up composed objects? Is it possible to detect that an ObservableList has changed when one of the fields inside an item has changed?\nThat\u0026rsquo;s what \u0026ldquo;extractors\u0026rdquo; are for. They allow you to detect changes to Property fields inside the items in your list, and to trigger Listeners on the ObservableList as a result.\nThe Basics # The JavaDocs for extractors is pretty thin.\nThe first thing that you\u0026rsquo;ll notice if you check out the entry for ObservableList is that there are no constructors for ObservableList because it\u0026rsquo;s an Interface. None of the \u0026ldquo;All Known Implementing Classes\u0026rdquo; section look promising either.\nTypically, you\u0026rsquo;ll get an ObservableList because it\u0026rsquo;s already part of a Node class like ListView, TableView or ComboBox. But if you want to create one yourself, you\u0026rsquo;ll need to use the static library class, FXCollections.\nIf you go to the JavaDocs for FXCollections you\u0026rsquo;ll see that there are lots and lots of ways to create different kinds of ObservableLists, some of which specify extractors. Let\u0026rsquo;s look at the details for observableArrayList(Callback\u0026lt;E,Observable[]\u0026gt; extractor):\nQuote public static \u0026lt;E\u0026gt; ObservableList\u0026lt;E\u0026gt; observableArrayList(Callback\u0026lt;E,Observable[]\u0026gt; extractor)\nCreates a new empty ObservableList that is backed by an array list and listens to changes in observables of its items.\nThe extractor returns observables (usually properties) of the objects in the created list. These observables are listened for changes and the user is notified of these through an update change of an attached ListChangeListener. These changes are unrelated to the changes made to the observable list itself using methods such as add and remove.\nFor example, a list of Shapes can listen to changes in the shapes\u0026rsquo; fill property.\nThis seems to hint at something interesting, but it doesn\u0026rsquo;t really explain much, does it?\nLet\u0026rsquo;s take a closer look at the constructor parameter: Callback\u0026lt;E,Observable[]\u0026gt; extractor.\nA Callback is just a Function, meaning that it accepts one value and then returns another. The name \u0026ldquo;Callback\u0026rdquo; implies that it is going to be used somewhere deep down in the internals of something. Somewhere that we can\u0026rsquo;t see, and probably don\u0026rsquo;t want to see. The Callback is a \u0026ldquo;hook\u0026rdquo; to allow that hidden code to get at something that we, the application programmers, define.\nIn this case, the Callback accepts something of type E. What is E? Well, ObservableList itself is generic, and E in this case refers to the type of elements that comprise our ObservableList. This means that the Callback is just going to accept a single element of our ObservableList, whatever that happens to be.\nThe output from our Callback is going to be an array of Observable. Observable is the top level Interface that all of the Properties and observable classes implement. It only specifies three methods, addListener(), removeListener() and subscribe(). That last one, subscribe() is new, and ObservableList extractors pre-dates it. So we don\u0026rsquo;t need to worry about it here.\nWhat this tells us is that at some point, deep inside the ObservableList code, JavaFX is going to add a Listener to every Observable that we return for every item in our ObservableList.\nThe only question left is: What does that Listener do?\nWhat Does the Extractor Do? # In my article about ObservableLists, part of my series on Observables, I wrote some code to see what kind of Listeners are activated when changes are made to ObservableLists. This involved adding, deleting, replacing and swapping ObservableList elements.\nWe can use some of that same code here, to see how extractors translate to the Listeners triggered the ObservableList.\n{% include notice_kotlin %}\nTo start, we\u0026rsquo;ll look at the code without an extractor, and see what it does\u0026hellip;\nprivate val obList: ObservableList\u0026lt;ExampleData\u0026gt; = FXCollections.observableArrayList() private val messages: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val totBinding = object : IntegerBinding() { init { super.bind(obList) } override fun computeValue(): Int = obList.map { it.value2.value }.sum() } override fun start(stage: Stage) { obList.subscribe { messages.value += \u0026#34;Invalidated \\n\u0026#34; } for (x in 1..5) obList.add(ExampleData(x)) messages.value += \u0026#34;Data Loaded\\n\u0026#34; messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; stage.scene = Scene(createContent()).apply { } stage.show() } private fun createContent(): Region = BorderPane().apply { top = HBox( 10.0, Label().apply { textProperty().bind(totBinding.asString()) }, Label().apply { textProperty().bind(obList[2].value2.asString()) }) center = TextArea().apply { textProperty().bind(messages) } bottom = Button(\u0026#34;Increment Item\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Button Clicked\\n\u0026#34; with(obList[2]) { value2.value = value2.value + 1 } messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; } } padding = Insets(20.0) } } class ExampleData(initialValue: Int) { val value1: IntegerProperty = SimpleIntegerProperty(initialValue) val value2: IntegerProperty = SimpleIntegerProperty(initialValue) } fun main() = Application.launch(ExtractorExample1::class.java) We have an ObservableList of DataExample, which is just an object with two IntegerProperties in it. We populate it with 5 items, where both IntegerProperties are populated with the same value and that value increments for each item that we add.\nThe GUI is a BorderPane with an HBox at the top with two Labels. One shows the total of all of DataExample.value2 in the ObservableList through a Binding and the other has the current value of obList[2].value2.\nThe centre is a TextArea which is bound to a StringProperty called messages. Various bits of code add new data to messages, and we\u0026rsquo;ll look at this.\nAt the bottom is a Button. When this Button is clicked three things happen:\n\u0026ldquo;Button Clicked\u0026rdquo; is added to messages. obList[2].value2 is incremented. A list of all of the values in value2 is added to messages We also have an InvalidationListener (through subscribe()) on obList that just adds \u0026ldquo;Invalidated\u0026rdquo; to messages. And we add \u0026ldquo;Data Loaded\u0026rdquo; to messages when all of the setup is done.\nAnd all of this looks like this:\nYou can see that obList invalidates every time a new ExampleData is added to it. Then the \u0026ldquo;Data Loaded\u0026rdquo; message is appended and we see the initial list of values in value2.\nThen we see that the Button was clicked, and that obList[2].value2 was incremented. And then again, and again, and again.\nWe can also see that our Label bound to obList[2].value2 has been updated, and it shows \u0026ldquo;7\u0026rdquo; - just as you would expect.\nHowever, the other Label hasn\u0026rsquo;t been updated. This is the one bound to the ObservableList itself. Also, out InvalidationListener never fired again.\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content = \u0026ldquo;When ObservableList items are composed objects containing Observable type fields, changes to those internal fields will not trigger Listeners on the ObservableList.\u0026rdquo; %}\nAdding an Extractor # All we are going to do is change a single line of the code from above. This:\nprivate val obList: ObservableList\u0026lt;ExampleData\u0026gt; = FXCollections.observableArrayList() will change to this:\nprivate val obList: ObservableList\u0026lt;ExampleData\u0026gt; = FXCollections.observableArrayList({ item -\u0026gt; arrayOf(item.value2) }) In this code, { item -\u0026gt; arrayOf(item.value2) } is the Callback for the extractor. It creates an array, and it includes ExampleData.value2 in that array.\nNow, when we run the program and click the Button four times, we get this:\nThe first thing that you notice is that we get the \u0026ldquo;Invalidated\u0026rdquo; message right after every \u0026ldquo;Button Clicked\u0026rdquo; message. This means that the InvalidationListener on obList has fired!\nThe other thing that you should notice is that the first Label now says \u0026ldquo;19\u0026rdquo;, which is 1+2+7+4+5. This means that the Binding on obList that sums up the values is now working!\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content = \u0026ldquo;When an ObservableList has an extractor that returns an encapsulated Observable field of the List items, then changes to that internal fields will trigger Listeners on the ObservableList.\u0026rdquo; %}\nThis tells us that, at a minumum, the InvalidationListener that the ObservableList puts on every value2 of every ExampleData in the list is used to trigger an InvalidationListener on the ObservableList itself. That\u0026rsquo;s enough to trigger the Binding on the ObservableList, too.\nBut does it do more?\nWill a ListChangeListener Work? # Let\u0026rsquo;s change the code a little bit to add a ListChangeListener to obList to see if the extractor will cause it to fire:\noverride fun start(stage: Stage) { obList.subscribe { messages.value += \u0026#34;Invalidated \\n\u0026#34; } obList.addListener(ListChangeListener { change -\u0026gt; while (change.next()) { if (change.wasPermutated()) { messages.value += \u0026#34;List was permutated\\n\u0026#34; } else { if (change.wasRemoved()) { messages.value += \u0026#34;List item was removed\\n\u0026#34; } if (change.wasAdded()) { messages.value += \u0026#34;List item was added\\n\u0026#34; } if (change.wasUpdated()) { messages.value += \u0026#34;List item ${change.from} was updated\\n\u0026#34; } } } }) for (x in 1..5) obList.add(ExampleData(x)) messages.value += \u0026#34;Data Loaded\\n\u0026#34; messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; stage.scene = Scene(createContent()).apply { } stage.show() } ListChangeListener accepts a value of ListChangeListener.Change, which itself is an iterable construct and may contain several different changes to the ObservableList that happened at the same time. These changes can be that an item (or range of items) were removed, added or permutated (which means they were replaced). We need code that will look at all of the types of changes to see what is happening.\nAgain, we\u0026rsquo;ll run the code and click on the Button a few times:\nHere, we can see that the ListChangeListener fires as each item is added to the ObservableList, but then it fires with an \u0026ldquo;update\u0026rdquo; Change when the Button is clicked. It also reports which element was updated in Change.from.\nFrom this, we can safely assume that a ListChangeListener is going to detect a change made to a field specified in an extractor.\nWill a ListProperty Work? # There is another class, called ListProperty that acts as a wrapper around an ObservableList. What if we put our obList into a ListProperty and then add a ChangeListener to that ListProperty?\noverride fun start(stage: Stage) { val listProperty = SimpleListProperty(obList) listProperty.addListener(InvalidationListener { messages.value += \u0026#34;Invalidated \\n\u0026#34; }) listProperty.subscribe { oldVal, newVal -\u0026gt; messages.value += \u0026#34;Change -\u0026gt; Old: $oldVal New: $newVal\\n\u0026#34; } for (x in 1..5) obList.add(ExampleData(x)) messages.value += \u0026#34;Data Loaded\\n\u0026#34; messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; stage.scene = Scene(createContent()).apply { } stage.show() } Notice that I\u0026rsquo;ve also moved the InvalidationListener to the ListProperty, so that we can see if it fires on there as well.\nWhen we run this, clicking the Button four times, we get:\nWe see the ChangeListener firing as the elements are added, and this not suprising. You will notice, however, thet the oldVal and newVal are both actually the newVal. This is a quirk of ChangeListeners on ListProperty, and has nothing to do with extractors.\nThen we see the ChangeListener also fires on the changes to the enclosed field through the extractor. This is interesting, although we still don\u0026rsquo;t get to see the correct value for oldVal.\nYou should also note that the InvalidationListener also fires every time for the ListProperty.\nAnd just to make sure this is the extractor that is doing this, I took it out and re-ran the program:\nHere, we just get the Listeners firing for the additions to the ObservableList. The changes to the enclosed Property do not trigger any Listeners.\nAdvanced Extractors # If you look up extractor examples on-line, you\u0026rsquo;ll never see anything more than:\nCallback\u0026lt;TestClass, Observable[]\u0026gt; extractor = obj -\u0026gt; new Observable[]{ obj.nameProperty() }; ObservableList\u0026lt;TestClass\u0026gt; list = FXCollections.observableArrayList(extractor); I just pulled this example from a StackOverflow question, but it\u0026rsquo;s pretty much the standard.\nBut the extractor is a Callback and can do much more, if you want. Let\u0026rsquo;s take a look at that:\nclass ExtractorExample4 : Application() { private val obList: ObservableList\u0026lt;ExampleData1\u0026gt; = FXCollections.observableArrayList({ item -\u0026gt; item.extractableValues() }) private val messages: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val totBinding = object : IntegerBinding() { init { super.bind(obList) } override fun computeValue(): Int = obList.map { it.value2.value }.sum() } override fun start(stage: Stage) { obList.addListener(InvalidationListener { messages.value += \u0026#34;Invalidated \\n\u0026#34; }) for (x in 0..5) obList.add(ExampleData1(x)) messages.value += \u0026#34;Data Loaded\\n\u0026#34; messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; stage.scene = Scene(createContent()).apply { } stage.show() } private fun createContent(): Region = BorderPane().apply { top = HBox( 10.0, Label().apply { textProperty().bind(totBinding.asString()) }, Label().apply { textProperty().bind(obList[2].value2.asString()) }) center = TextArea().apply { textProperty().bind(messages) } bottom = HBox( 20.0, Button(\u0026#34;Increment Item 2\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Button 1 Clicked\\n\u0026#34; with(obList[2]) { value2.value = value2.value + 1 } messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; } }, Button(\u0026#34;Increment Item 5\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Button 2 Clicked\\n\u0026#34; with(obList[5]) { value2.value = value2.value + 1 } messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; } }) padding = Insets(20.0) } } class ExampleData1(private val initialValue: Int) { val value1: IntegerProperty = SimpleIntegerProperty(initialValue) val value2: IntegerProperty = SimpleIntegerProperty(initialValue) override fun toString() = \u0026#34;[${value1.value}, ${value2.value}]\u0026#34; fun extractableValues(): Array\u0026lt;Observable\u0026gt; { return if (initialValue \u0026lt; 4) arrayOf(value1) else arrayOf(value2) } } fun main() = Application.launch(ExtractorExample4::class.java) You\u0026rsquo;ll notice that we\u0026rsquo;ve added ExampleData1, which differs from ExampleData in two ways:\nThe constructor parameter initialValue has now been changed into a private immutable field. We\u0026rsquo;ve have a new method, extractableValues. This new method will return an Array containing either value1 or value2 depending on whether initialValue is less than 4 or not.\nAdditionally, we\u0026rsquo;ve added a second Button that works very much the same way as the original Button but acts on the 5th item in the ObservableList, which should have an initialValue greater than 4.\nThe Listeners have been return to very much the way that they were before we introduced the ListProperty, which has now been removed.\nHere\u0026rsquo;s what the screen looks like when we click the first Button a few times:\nYou can see the messages from the Button click, and you can see the updated values in obList. You can also see that the second Label is still correct. However, the first Label does NOT have the correct value, and we do not have any \u0026ldquo;Invalidated\u0026rdquo; messages for those clicks.\nThis is expected, because the initialValue of the item we clicked was \u0026ldquo;2\u0026rdquo; which is less than \u0026ldquo;4\u0026rdquo; and the extractor pulls value1 for that item, while the Button is updating value2.\nHere\u0026rsquo;s what it looks like after we click the new Button a few times:\nNow we see the \u0026ldquo;Invalidated\u0026rdquo; messages, and the first Label now contains the correct total.\nUses of This Technique # I\u0026rsquo;ve never seen anyone demonstrate anything like this before, and I\u0026rsquo;m not sure how often a use would pop up for this in real life. But there are couple of things to take note of:\nThe Extraction Details are Encapsulated # When you think about it, you see that this approach puts the logic for extraction inside the ObservableList items. This means that the nature of the extraction is hidden from the ObservableList itself.\nIf you are using a framework with a formal Presentation Model, like MVCI, then you are most likely going to instantiate your ObservableLists inside the Model. Defining the extractors as part of that call to FXCollections.observableArrayList() requires that the Model knows about the structure of the objects that are put inside the ObservableList.\nThis might not be appropriate, even though those ObservableList items are part of the Presentation Model.\nStill, it\u0026rsquo;s worth considering, and I can see some value in putting the logic that dictates how the items will behave in an ObservableList right beside the actual data that comprises the items.\nThe Extraction Logic Needs to Be Immutable # You\u0026rsquo;ll notice that I very deliberately based the extraction logic on a field defined as val instead of var. This is the same as using final in Java.\nThis is because the extractor logic is only run once, when the item is added to the ObservableList.\nI checked the source code and found that the extractor code is run to get an Array of Observable and then the ObservableList loops through all of these Observables and adds an InvalidationListener to each one. This code is run when the ObservableList is created as a wrapper around an existing List, like this:\nval obList = FXCollections.observableArrayList(existingList, extractor) In this case, the interal code will loop through every item in existingList, run the extractor against them and the add the InvalidationListeners to every Observable returned by the extractor for each item.\nThe extractor code is also run when a new item is added to an existing ObservableList that has an extractor.\nThere extractor code is also run when an item is removed from an ObservableList, in order to remove the InvalidationListeners that were added by the ObservableList.\nOther than that, the extract isn\u0026rsquo;t ever run against an item.\nIf, in our example, we had made initialValue a var, and provided some way to change it after the items were in the list, there would be no change to InvalidationListeners added by the ObservableList, because they were already established. So, you could end up with a system where the apparent behaviour of the extractor doesn\u0026rsquo;t match the current state of the data.\nThe worse case scenario is that if the item is removed from the ObservableList, then some of the InvalidationListeners might not be removed because the extractor behaviour wasn\u0026rsquo;t the same as when the item was added to the ObservableList. That could give some weird results.\nExtractors in TableView Items # Probably the most common use of composed Property objects as items in ObservableLists is in TableView. So let\u0026rsquo;s take a look at how TableView columns are usually set up, and how they interact with enclosed Property fields.\nHere\u0026rsquo;s the same example code, with just a TableView added:\nclass ExtractorExample5 : Application() { private val obList: ObservableList\u0026lt;ExampleData\u0026gt; = FXCollections.observableArrayList() private val messages: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val totBinding = object : IntegerBinding() { init { super.bind(obList) } override fun computeValue(): Int = obList.map { it.value2.value }.sum() } override fun start(stage: Stage) { obList.addListener(InvalidationListener { messages.value += \u0026#34;Invalidated \\n\u0026#34; }) for (x in 0..5) obList.add(ExampleData(x)) messages.value += \u0026#34;Data Loaded\\n\u0026#34; messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; stage.scene = Scene(createContent()).apply { } stage.show() } private fun createContent(): Region = BorderPane().apply { top = HBox( 10.0, Label().apply { textProperty().bind(totBinding.asString()) }, Label().apply { textProperty().bind(obList[2].value2.asString()) }) center = TextArea().apply { textProperty().bind(messages) } left = TableView\u0026lt;ExampleData\u0026gt;().apply { items = obList columns += TableColumn\u0026lt;ExampleData, Int\u0026gt;(\u0026#34;Value 1\u0026#34;).apply { setCellValueFactory { p -\u0026gt; p.value.value1.asObject() } } columns += TableColumn\u0026lt;ExampleData, Int\u0026gt;(\u0026#34;Value 2\u0026#34;).apply { maxHeight = 100.0 setCellValueFactory { p -\u0026gt; p.value.value2.asObject() } setCellFactory { column -\u0026gt; object : TableCell\u0026lt;ExampleData, Int\u0026gt;() { override fun updateItem(newItem: Int?, empty: Boolean) { super.updateItem(newItem, empty) messages.value += \u0026#34;Table Cell Update: $newItem\\n\u0026#34; text = null graphic = null newItem?.let { if (!empty) { text = newItem.toString() } } } } } } } bottom = Button(\u0026#34;Increment Item 2\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Button 1 Clicked\\n\u0026#34; with(obList[2]) { value2.value = value2.value + 1 } messages.value += obList.map { it.value2.value }.joinToString() + \u0026#34;\\n\u0026#34; } } padding = Insets(20.0) } } fun main() = Application.launch(ExtractorExample5::class.java) This is very close to the first example, but with the TableView added. I\u0026rsquo;ve also removed the extractor from the ObservableList, so what we will see is pure TableView behaviour.\nI added a custom TableCell to the second column so that we could see when the updateItem() method gets called, and what value it receives. I\u0026rsquo;ve also limited the height of the TableView to restrict the number of TableCells instantiated because they generate a lot of messages, even if empty:\nThe Button has been clicked twice, and you can see from the TableView that it has updated correctly. You can see from the messages, that the TableCells get updated a lot just setting things up. If you look at the bottom of the messages you can see the two Button clicks, and you can see the TableCell updating right away. You do not see any invalidation messages from the ObservableList, however.\nThis tells us that the TableView puts a Listener of some sort on the Property when it identifies it by calling the setCellValueFactory. That\u0026rsquo;s the only way that this will work.\nWhat happens if we put the extractor back into the ObservableList? You get this:\nNot much, really.\nThe first Label now has the correct total, as expected, and we see that the ObservableList invalidates after each Button click. But it doesn\u0026rsquo;t cause the TableCell to update twice, or anything like that.\nWe can determine from this that adding an extractor to an ObservableList used in a TableView doesn\u0026rsquo;t hurt the performance of the TableView, but it doesn\u0026rsquo;t help it either.\nOf course this is only true if you compose your TableView items from Properties and other ObservableValues.\nExtractors in ListView Items # So now we know that TableView works fine without extractors, but what about ListView?\nWe\u0026rsquo;ll go back to our example, and change the layout so that it shows a ListView instead of a TableView:\nprivate fun createContent(): Region = BorderPane().apply { top = HBox( 10.0, Label().apply { textProperty().bind(totBinding.asString()) }, Label().apply { textProperty().bind(obList[2].value2.asString()) }) center = TextArea().apply { textProperty().bind(messages) } left = ListView\u0026lt;ExampleData\u0026gt;().apply { items = obList cellFactory = Callback { _ -\u0026gt; object : ListCell\u0026lt;ExampleData\u0026gt;() { val label1 = Label() val label2 = Label() val layout = VBox(2.0).apply { children += HBox(10.0, Label(\u0026#34;Value 1:\u0026#34;), label1) children += HBox(10.0, Label(\u0026#34;Value 2:\u0026#34;), label2) padding = Insets(8.0) } override fun updateItem(newItem: ExampleData?, isEmpty: Boolean) { super.updateItem(newItem, isEmpty) graphic = null text = null newItem?.let { if (!isEmpty) { label1.text = newItem.value1.value.toString() label2.text = newItem.value2.value.toString() graphic = layout } } } } } } bottom = Button(\u0026#34;Increment Item 2\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Button 1 Clicked\\n\u0026#34; with(obList[2]) { value1.value = value1.value + 1 } messages.value += obList.map { it.value1.value }.joinToString() + \u0026#34;\\n\u0026#34; } } padding = Insets(20.0) } Here we have a custom ListCell layout that contains a VBox holding two HBoxes. Each HBox has two Labels, one is just a title, and the other is loaded with either ExampleData.value1.value or ExampleData.value2.value. No Bindings are used, so there\u0026rsquo;s no reliance inside the ListCell on the Property nature of value1 or value2.\nThe updateItem() method does the usual stuff, and loads value1 and value2 into their respective Labels.\nThe only other change to this code from the TableView version was to change the Button action to update ExampleData.value1 instead of ExampleData.value2. The ObservableList still has an extractor that returns value2, so this change essentially disables the extractor for this example.\nThis is what it looks like when it is run and the Button is clicked a few times:\nYou can see that the values displayed in the ListView do not change at all. You can see in the TextArea that the Button has been clicked several times, yet the ObservableList never invalidated, but that value1 did increment each time.\nIf we change the extractor to return value1 instead of value2, this is what it looks like:\nNow you can see that the ListView values are correct, and that the Button click causes the ObservableList to invalidate.\nIs This a Good Approach? # This approach has the advantage that you can implement a very naive design for the ListCell, without having to worry about binding and unbinding from a changing itemProperty(). This is pretty much exactly the same approach to ListCell design that you\u0026rsquo;ll see as the copypasta example in virtually every online tutorial about ListView. It\u0026rsquo;s simple, and it\u0026rsquo;s easy, and it\u0026rsquo;s what everybody knows.\nHowever, it effectively splits the logic involved in keeping the ListCell up to date between two places, the ListCell itself, and the extractor.\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content = \u0026lsquo;Always ask yourself this question: \u0026ldquo;How much code will a maintenance programmer need to look at to fix a bug with this feature?\u0026rdquo;\u0026rsquo; %}\nIn this case, you cannot really understand how the ListCell works without looking at the instantiation of ObservableList to see that it has an extractor. That\u0026rsquo;s very likely to be in another class altogether.\nSome time back, before JavaFX 19, achieving this entirely within the ListCell would have been a bit tedious. The Labels would need to be bound to fields inside a value that itself was changing. This would have required unbinding and the rebinding the Labels each time the value in the ListCell was changed.\nBut JavaFX 19 introduced ObservableValue.flatmap(), and now we can skip all the unbinding and rebinding.\nHere\u0026rsquo;s the ListCell implemented that way:\ncellFactory = Callback { _ -\u0026gt; object : ListCell\u0026lt;ExampleData\u0026gt;() { val label1 = Label().apply { textProperty().bind(itemProperty().flatMap { item -\u0026gt; item.value1.asString() }) } val label2 = Label().apply { textProperty().bind(itemProperty().flatMap { item -\u0026gt; item.value2.asString() }) } val layout = VBox(2.0).apply { children += HBox(10.0, Label(\u0026#34;Value 1:\u0026#34;), label1) children += HBox(10.0, Label(\u0026#34;Value 2:\u0026#34;), label2) padding = Insets(8.0) } init { graphicProperty().bind( Bindings.createObjectBinding\u0026lt;Region\u0026gt;( { if (item != null) layout else null }, itemProperty() ) ) } } } The Labels are bound to fields in itemProperty() through flatMap(). Now, if the value in the field changes, or the itemProperty() itself changes, the binding will handle it automatically. By also creating a Binding to control whether or not the graphic displays in the init{} block, we can now do away with updateItem() entirely. Everything is simply dependent on itemProperty().\nIn order to test this, I removed the extractor from the ObservableList. The result looks like this:\nYou can see that the items in the ListView are still correct, and the messages show the Button clicks that do not trigger an \u0026ldquo;Invalidated\u0026rdquo; message from the Listener.\nObviously, this requires a somewhat deeper understanding of the ListCell mechanics. Most beginners aren\u0026rsquo;t even aware that item exists in the ListCell as on ObjectProperty, and they assume that the only way to deal with a changing item is through updateItem().\nWhat About Performance? # You may be thinking, \u0026ldquo;What if I have an ObservableList with thousands, and thousands of items? Won\u0026rsquo;t that mean thousands and thousands of Listeners running all the time?\u0026rdquo;\nFor sure, there is some overhead to Listeners, but it\u0026rsquo;s not as much as you might think. Let\u0026rsquo;s look at what happens when you add an InvalidationListener to an Observable\u0026hellip;\nTo find this you need to look at ObjectPropertyBase which is the highest abstract class that implements addListener() for Properties. In it, you\u0026rsquo;ll find that it delegates to something called ExpressionHelper which is an instance variable of ObjectPropertyBase, and then that ends up in a subclass of ExpressionHelper called Generic. In there, you will find this:\nprotected Generic\u0026lt;T\u0026gt; addListener(InvalidationListener listener) { if (invalidationListeners == null) { invalidationListeners = new InvalidationListener[] {listener}; invalidationSize = 1; } else { final int oldCapacity = invalidationListeners.length; if (locked) { final int newCapacity = (invalidationSize \u0026lt; oldCapacity)? oldCapacity : (oldCapacity * 3)/2 + 1; invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity); } else if (invalidationSize == oldCapacity) { invalidationSize = trim(invalidationSize, invalidationListeners); if (invalidationSize == oldCapacity) { final int newCapacity = (oldCapacity * 3)/2 + 1; invalidationListeners = Arrays.copyOf(invalidationListeners, newCapacity); } } invalidationListeners[invalidationSize++] = listener; } return this; } Yikes!\nIn summary, it has an array of InvalidationListeners and it adds your new InvalidationListener to it. The remaining dozen or so lines of appear to be some kind of memory management optimization. That\u0026rsquo;s good to see, if you are worried about performance degredation associated with extractors.\nSo, this means that every Observable potentially contains an Array that holds all of the InvalidationListeners that have been added to it.\nHow does it use this?\nIn ObjectPropertyBase we have this:\npublic void set(T newValue) { if (isBound()) { throw new java.lang.RuntimeException((getBean() != null \u0026amp;\u0026amp; getName() != null ? getBean().getClass().getSimpleName() + \u0026#34;.\u0026#34; + getName() + \u0026#34; : \u0026#34;: \u0026#34;\u0026#34;) + \u0026#34;A bound value cannot be set.\u0026#34;); } if (value != newValue) { value = newValue; markInvalid(); } } The most important part for us is the call to markInvalid():\nprotected void fireValueChangedEvent() { ExpressionHelper.fireValueChangedEvent(helper); } private void markInvalid() { if (valid) { valid = false; invalidated(); fireValueChangedEvent(); } } And we are back to ExpressionHelper again, and then back into Generic for the actual implementation:\nfor (int i = 0; i \u0026lt; curInvalidationSize; i++) { try { curInvalidationList[i].invalidated(observable); } catch (Exception e) { Thread.currentThread().getUncaughtExceptionHandler().uncaughtException(Thread.currentThread(), e); } } This is just a snippet of the code for this method, but you can see how it works. It loops through the Array of InvalidationListeners and invokes invalidated() on each one. Remember that InvalidationListener is a Functional Interface with one method, invalidated().\nIf you were picturing an implementation where JavaFX had some master list of Listeners and it had to check them all every time something changed, then you can see that this is backwards to that. Each Observable maintains its own list of Listeners that have been registered with it. Then when its value is updated, it triggers each of those InvalidationListeners.\nIf there is a potential performance hit, it would be from having many Listeners on each Observable item. Not from having many Observable items with one or two Listeners each.\nConclusion # Creating an ObservableList with an extractor causes just two things to happen:\nThe ObservableList will invalidate, and fire any InvalidationListeners on it when any Observable returned from the extractor invalidates. If you wrap the ObservableList in a ListProperty that ListProperty will both invalidate and fire and ChangeListeners on it when any Observable return from the extractor invalidates. Invalidation is the key concept on Bindings. This is because Bindings always recalculate by calling their computeValue() method when any of their dependencies invalidates.\nThis means that you can create Bindings on ObservableLists that will re-evaluate not just when items in the ObservableList are added, removed or replaced, but when internal values in those ObservableList items change\u0026hellip;if you add the appropriate extractor.\n","date":"5 August 2025","externalUrl":null,"permalink":"/javafx/elements/observable-lists/extractors/","section":"JavaFX","summary":"ObservableList extractors allow you to track changes to elements inside your ObservableLists.","title":"ObservableLists: Extractors","type":"javafx"},{"content":" Introduction # In an earlier article I looked at Task and how to track progress and partial results as a Task is running. This can be a challenge, as any Observable value that can be linked to your layout can only be updated from the FXAT, while the action of the Task is designed to be run on a background thread. The usual answer to this is to wrap any calls to update those Observable objects in a Platform.runLater() call.\nIn that earlier article, we looked at how the Task.updateValue() and Task.updateMessage() methods are designed to limit the number of Platform.runlater() calls that are executed, in order to avoid flooding the FXAT with jobs that would affect the performance of the GUI.\nWe also looked at creating a custom Property in Task that could be updated in the same manner as Task.value and Task.message and then bound to elements in your layout. Those methods use an internal AtomicReference field, that can safely be updated by multiple threads, to hold an temporary value that can later be transferred to the Property through Platform.runLater().\nBut AtomicReference relies on an \u0026ldquo;atomic\u0026rdquo; getAndSet() method to retrieve and update the value that it holds in a single, thread-safe, operation. But what if you don\u0026rsquo;t want to replace the value, but add to it?\nThis is the situaton that you get when your monitored results are in the form of an ObservableList.\nWhat the JavaDocs Say: # Let\u0026rsquo;s have a look at the informational section at the top of the JavaDocs for Task:\nQuote Suppose instead of updating a single value, you want to populate an ObservableList with results as they are obtained. One approach is to expose a new property on the Task which will represent the partial result. Then make sure to use Platform.runLater when adding new items to the partial result.\npublic class PartialResultsTask extends Task\u0026lt;ObservableList\u0026lt;Rectangle\u0026gt;\u0026gt; { // Uses Java 7 diamond operator private ReadOnlyObjectWrapper\u0026lt;ObservableList\u0026lt;Rectangle\u0026gt;\u0026gt; partialResults = new ReadOnlyObjectWrapper\u0026lt;\u0026gt;(this, \u0026#34;partialResults\u0026#34;, FXCollections.observableArrayList(new ArrayList\u0026lt;Rectangle\u0026gt;())); // public final ObservableList\u0026lt;Rectangle\u0026gt; getPartialResults() { return partialResults.get(); } public final ReadOnlyObjectProperty\u0026lt;ObservableList\u0026lt;Rectangle\u0026gt;\u0026gt; partialResultsProperty() { return partialResults.getReadOnlyProperty(); } // @Override protected ObservableList\u0026lt;Rectangle\u0026gt; call() throws Exception { updateMessage(\u0026#34;Creating Rectangles...\u0026#34;); for (int i=0; i\u0026lt;100; i++) { if (isCancelled()) break; final Rectangle r = new Rectangle(10, 10); r.setX(10 * i); Platform.runLater(new Runnable() { @Override public void run() { partialResults.get().add(r); } }); updateProgress(i, 100); } return partialResults.get(); } } They give us \u0026ldquo;one approach\u0026hellip;\u0026rdquo;, but no more. This approach can work if the the updates to the list are relatively infrequent\u0026hellip;\nQuestion How would you handle a situation where there are thousands of updates to the ObservableList from multiple threads that are coming milliseconds apart?\nThere\u0026rsquo;s no help from the JavaDocs for this situation.\nA Quick Review of the Task.updateMessage() # You really should go back and read that earlier article , but we\u0026rsquo;ll have a quick look at the process for updating a single value.\nTask has a number of Properties that are intended to be updated and changed from background threads, and it is instructive to see how Task internally handles these updates.\nTask has a field called message and the infrasture to update it from background threads. The source code for Task.updateMessage() looks like this:\nprivate AtomicReference\u0026lt;String\u0026gt; messageUpdate; . . . protected void updateMessage(String var1) { if (this.isFxApplicationThread()) { this.message.set(var1); } else if (this.messageUpdate.getAndSet(var1) == null) { this.runLater(new Runnable() { public void run() { String var1 = (String)Task.this.messageUpdate.getAndSet((Object)null); Task.this.message.set(var1); } }); } } We have an internal, private, variable called messageUpdate which is an AtomicReference, and that will hold a transient value for updates to the message Property. Because it is an AtomicReference, it is thread-safe, and can be updated freely without concurrency issues.\nThe updateMessage() method, if run from the FXAT, will simply update Task.message directly.\nWhen called from a background thread, however, it does something more complicated. First, it uses AtomiceReference.getAndSet() to update messageUpdate and to check it\u0026rsquo;s previous value. If that previous value was null, then it knows that there wasn\u0026rsquo;t any previous transient value waiting to be transferred to Task.message via Platform.runLater() and it needs to initiate that process. So it creates a Runnable and schedules it on the FXAT via Platform.runLater().\nThat Runnable does two things:\nIt transfers the current value of Task.messageUpdate to Task.message. It sets the value of Task.messageUpdate to null. If Task.updateMessage() is called from a background thread any time between Platform.runLater() being called and the Runnable being executed on the FXAT, then the previous value of messageUpdate will not be null, and it will not, therefore, need to launch a new Runnable on the FXAT. In fact, the only thing that will happen will be that Task.messageUpdate has its value changed. When the Runnable does execute on the FXAT, it will update Task.message with only that latest value that is in Task.messageUpdate.\nIn this way, Task.updateMessage() allows gazillions of updates from the background threads, but doesn\u0026rsquo;t flood the FXAT with jobs. As a matter of fact, you never have more than a single job pending on the FXAT at any given time for these updates. Transient values that never make it to Task.message would probably never have made it to the screen if Platform.runlater() had been invoked for every one of them in any case. They certainly wouldn\u0026rsquo;t have been seen by the user.\nAbout AtomicReference # AtomicReference is thread-safe because its \u0026ldquo;read\u0026rdquo; and \u0026ldquo;set\u0026rdquo; operations are conducted in a single, \u0026ldquo;atomic\u0026rdquo; operation. This means that there\u0026rsquo;s no possibility that the value could have been changed by some other thread accessing the same object in between the \u0026ldquo;read\u0026rdquo; stage and the \u0026ldquo;set\u0026rdquo; stage.\nBut this comes with a cost.\nThere is no way to modify a value in AtomicReference, we can only replace it. Yes, there is a way to use the current value in an AtomicReference to calculate a new value for it, AtomicReference.getAndUpdate() but this will also replace the value. So, we can do something like this:\nval atomicReference = AtomicReference\u0026lt;Int\u0026gt;(10) val oldValue = atomicReference.getAndUpdate{it + 5} This allows us to create a new value for atomicReference based on its old value, but doesn\u0026rsquo;t simply modify the value already inside it.\nThis means that we cannot do this:\nval atomicReference = AtomicReference\u0026lt;MutableList\u0026lt;Int\u0026gt;\u0026gt;(mutableListOf()) val oldValue = atomicReference.getAndUpdate(it.add(5)) because the compiler will complain that it.add(5) returns Boolean when it was expecting MutableList\u0026lt;Int\u0026gt;. This means that we will have to find some other way to deal with it.\nSimply Running the Updates in Platform.runLater() # Before we try anything else, let\u0026rsquo;s see what happens if we take the approach from the JavaDocs, and just put every update on the FXAT via a call to Platform.runLater(). Because, if this works, then we\u0026rsquo;re all done!\nI\u0026rsquo;m going to use the same type of application that I used in that earlier article, this will make it easier to follow if you did go back and read that article.\nclass TaskProgress5App : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 800.0, 500.0) stage.title = \u0026#34;Task Progress Example\u0026#34; stage.show() } @OptIn(ExperimentalAtomicApi::class) private fun createContent(): Region = BorderPane().apply { val task = object : Task\u0026lt;Unit\u0026gt;() { val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList() val waitingEvents = AtomicInt(0) init { updateMessage(\u0026#34;\u0026#34;) updateProgress(0, 100) setOnSucceeded { evt -\u0026gt; updateProgress(100, 100) } } private fun appendValue(var1: String) { if (Platform.isFxApplicationThread()) { valueList.add(var1) } else { println(\u0026#34;Queuing: ${waitingEvents.incrementAndFetch()}\u0026#34;) Platform.runLater { println(\u0026#34;Performing: ${waitingEvents.decrementAndFetch()}\u0026#34;) valueList.add(var1) } } } override fun call() { handleProcessing2( { done, total -\u0026gt; updateProgress(done, total) }, { appendValue(it) }, { updateMessage(it) }) } } center = VBox(10.0).apply { children += Label().apply { textProperty().bind(task.messageProperty()) } children += ProgressBar().apply { progressProperty().bind(task.progressProperty()) minWidth = 200.0 } alignment = Pos.CENTER } right = TextArea().apply { textProperty().bind(ListToString(task.valueList)) } bottom = HBox(Button(\u0026#34;Run Task\u0026#34;).apply { setOnAction { Thread(task).start() } }).apply { alignment = Pos.CENTER padding = Insets(20.0) } } } @OptIn(ExperimentalAtomicApi::class) fun handleProcessing2( progressUpdater: (Long, Long) -\u0026gt; Unit, dataCollector: (String) -\u0026gt; Unit, messageUpdater: (String) -\u0026gt; Unit ) { val maxCount = 1000 val threadCount = 50 val job = Runnable { dataCollector(\u0026#34;Thread ${Thread.currentThread().name} starting\u0026#34;) for (x in 0..maxCount) { dataCollector(\u0026#34;Thread ${Thread.currentThread().name}: $x\u0026#34;) Thread.sleep(Duration.ofMillis(10)) } } val sTime = LocalTime.now() progressUpdater(0, 100) messageUpdater(\u0026#34;Initializing\u0026#34;) Thread.sleep(Duration.ofMillis(1000)) progressUpdater(-1, 100) messageUpdater(\u0026#34;Processing\u0026#34;) val threadPool = Executors.newFixedThreadPool(threadCount) repeat(threadCount) { threadPool.execute(job) } messageUpdater(\u0026#34;ThreadPool Started\u0026#34;) threadPool.shutdown() var counter = 0 while (!threadPool.awaitTermination(4, TimeUnit.SECONDS)) messageUpdater(\u0026#34;Still Waiting ${counter++}\u0026#34;) messageUpdater(\u0026#34;Completed ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}\u0026#34;) progressUpdater(100, 100) } class ListToString(private val theList: ObservableList\u0026lt;String\u0026gt;) : StringBinding() { init { super.bind(theList) } override fun computeValue(): String? = theList.joinToString(\u0026#34;\\n\u0026#34;) } fun main() { Application.launch(TaskProgress5App::class.java) } We have a TextField that has its textProperty() bound to Task.valueList through the Binding called, ListToString. This means that as Task.valueList grows, the text in the TextArea should grow with it.\nThe background job uses an ExecutorPool of 50 threads to each run a simple job that just generates a String which is passed back to the Task and then sleeps for 10 ms. When the Task gets each String from the background thread, it uses Platform.runLater() to add that String to Task.valueList.\nThis means that, with 50 threads each generating 1000 Strings, with a 10ms delay between them, we get 50,000 Strings submitted via 50,000 Platform.runLater() calls in a 10 second period. This means it will generate 50,000 FXAT jobs in that 10s.\nIt looks like this when it is running:\nHonestly, when I first ran this without the println() statements, it just looked hung. When I added them, I saw that waitingEvents rose up to 40K+ within a matter of seconds, the Task completed, and then it continued for several minutes to process all of the jobs on the FXAT and decrement the waitingEvents counter.\nAn earlier version of this code had the ProgressIndicator stay in Indeterminent mode through the processing. It appeared to be stuck while the jobs were being executed. So clearly this approach is not very different from running a long process on the FXAT as far as the GUI responsiveness is concerned.\nWarning Never flood the FXAT with thousands and thousands of jobs submitted via Platform.runLater()\nWhen the jobs on the FXAT finally all complete, after 10 minutes or more, it looks like this:\nYou can see that it took a little more than 11 seconds for the background threads to complete, which seems about right. But the GUI was essentially frozen for the next 10+ minutes while the FXAT jobs were processed.\nFailure Clearly, the methodology suggested by the JavaDocs is not a viable approach.\nUsing Synchronized Operations # The challenge is very well defined at this point. We need to gather our updates together into batches such that we never have more than 1 or 2 jobs in the FXAT to update our ObservableList, but we cannot use the thread-safe Atomic classes to do this. The answer, is to employ locks on our update List so that each thread, including the FXAT, can update it without causing data integrity issues.\nLet\u0026rsquo;s look at how we\u0026rsquo;ll do this\u0026hellip;.\nThe only thing that needs to change is our Task. The inner workings of the update process are hidden from the rest of the code in the application, so it won\u0026rsquo;t be impacted:\nval task = object : Task\u0026lt;Unit\u0026gt;() { private val dataList = mutableListOf\u0026lt;String\u0026gt;() val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList() val counter = SimpleIntegerProperty(0) init { updateMessage(\u0026#34;\u0026#34;) updateProgress(0, 100) setOnSucceeded { evt -\u0026gt; updateProgress(100, 100) } } private fun appendValue(var1: String) { if (Platform.isFxApplicationThread()) { valueList.add(var1) } else { val sTime = LocalTime.now() synchronized(dataList) { if (dataList.isEmpty()) { Platform.runLater { val sTime = LocalTime.now() synchronized(dataList) { valueList.add( \u0026#34;FX Event ${counter.value++} happened in ${ sTime.until(LocalTime.now(), ChronoUnit.MILLIS) }ms\\n\u0026#34; ) valueList.addAll(dataList) dataList.clear() } } } dataList.add(var1 + \u0026#34; ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}\u0026#34;) } } } override fun call() { handleProcessing1( { done, total -\u0026gt; updateProgress(done, total) }, { appendValue(it) }, { updateMessage(it) }) } } Here we\u0026rsquo;ve introduced a private field called dataList which is just a MutableList\u0026lt;String\u0026gt;. This is what we are going to use to \u0026ldquo;batch\u0026rdquo; our updates to the ObservableList on the FXAT. The rest of the changes are in appendValue().\nIf you\u0026rsquo;ve looked at the source code for Task.updateMessage() above, then you should recognize the pattern here. First, if we are on the FXAT then we just add the value to valueList without any fuss. If we are not on the FXAT, then we need to add our new String to dataList and decide if we need to start a new job on the FXAT. That determination is done by looking at dataList to see if it is empty before we add our new value. If it is, then we need to start a new FXAT job.\nThat FXAT job does three things:\nAdds a message to valueList that says, \u0026ldquo;FXAT happened\u0026rdquo;. This way we can track how many updates end up in each update \u0026ldquo;batch\u0026rdquo;. Adds all of the items in dataList to valueList. Clears out dataList. All three things are done inside a synchronized block locked on dataList.\nThe last thing that appendValue() does is to add our new value to dataList after appending some timing information to it.\nIn order to avoid concurrency problems, all of this activity: checking if dataList is empty, possibly launching a new job on the FXAT and appending the new value to dataList is done in a synchronized block, locking on dataList.\nWe should look at where this code runs:\nEvery call to appendValue() runs on one of the 50 threads in the ExecutorPool. There can, therefore, be multiple simultaneous access from many different threads to this code at any given time. The code that copies data from dataList to valueList and clears out dataList always runs from the FXAT, and may run at the same time that any number of threads are running appendValue(). The synchronized blocks around every single access to dataList ensure that only one thread can execute code in either of those two blocks at any given time.\nThis means that when a background thread is adding a new value to dataList, no other background threads can do the same thing, and the synchronized block inside the job on the FXAT cannot run either. When the job on the FXAT is running, no other background thread can enter the synchronized block in appendValue(). If they cannot enter, they wait.\nFor this reason, the time calculation code doesn\u0026rsquo;t just return 0 every time. This information in the output will tell us how long any threads had to wait for other threads to complete their processing first. There\u0026rsquo;s a time calculation on both the background threads and the FXAT job. We also count how many times it runs the FXAT job.\nIt looks like this:\nWe can see that it now took about 15.5 seconds to complete the background task. This means that the synchronization locking caused an approximate, cumulative 4 seconds of waiting time.\nOn the other hand, the process finished almost immediately after the background tasks completed. At no time did the GUI appear frozen or unresponsive, and everything seemed to operate smoothly.\nYou can also see from the screensnap, that the FXAT job was invoked 299 times in that 15 seconds. It never seemed to have to wait.\nAbout that Binding # In this example, the contents of the TextArea reflect the contents of Task.valueList through a custom Binding:\nclass ListToString(private val theList: ObservableList\u0026lt;String\u0026gt;) : StringBinding() { init { super.bind(theList) } override fun computeValue(): String? = theList.joinToString(\u0026#34;\\n\u0026#34;) } This might seem a little \u0026ldquo;heavy handed\u0026rdquo; at first. The entire String is rebuilt every time that the ObservableList changes. Surely it might be better to put a ListChangeListener on valueList instead, and then just apply the changes to the TextArea?\nWe really cannot make any assumptions about how the layout is going to use the Properties that we expose from our Task. We need to design our implementation such that any \u0026ldquo;normal\u0026rdquo; usage of the Property is going to be functional. That includes a \u0026ldquo;heavy-handed\u0026rdquo; Binding like this.\nRemember that String in Java is immutable. So any attempt to append data to a String is going to result in copying the entire String over and adding the new text. Is this more efficient than List.joinToString()? Maybe, but it\u0026rsquo;s probably more a matter of degree than magnitude.\nInfo Bindings are recalculated in-line with the code that invalidates their dependencies.\nBindings are not executed through EventHandlers that are submitted as new jobs an the FXAT. This means that you can measure their impact on performance directly. In our FXAT Runnable if we move the code that generatest the \u0026ldquo;FX Event\u0026hellip;\u0026rdquo; text from the top of the synchronized block to the bottom - after valueList has been modified, and after the ListToString.computeValue() has run - we get this:\nand this:\nYou can see the top and the bottom of the timings, and as valueList gets bigger, the times get longer. I checked and this is a progression through the output.\nI noticed that the first few elements added to each batch picked up measurable delays as the processing went on. This was because the FXAT job started to take longer and longer to complete, so more background tasks (which completed very quickly) got backed up waiting for the FXAT job to release the lock.\nI also noticed that the number of elements in the batches got larger as the processing went on. Once again, the increased time for the FXAT job to release the lock meant that more background threads completed their sleep(10) while the lock was on, and therefore contributed to the batches. Eventually, the wait became longer than the 10ms sleep time, so every batch included contributions from every thread.\nQuestion Is there anything we can do here to limit the time that the FXAT job spends in the syncronized block?\nAbsolutely! We need to extract the values from dataList and then clear it out inside the synchonized block, but we don\u0026rsquo;t have to update valueList inside the synchronized block, since we can only ever update it from the single FXAT thread:\nprivate fun appendValue(var1: String) { if (Platform.isFxApplicationThread()) { valueList.add(var1) } else { val sTime = LocalTime.now() synchronized(dataList) { if (dataList.isEmpty()) { Platform.runLater { val sTime = LocalTime.now() val tempList = mutableListOf\u0026lt;String\u0026gt;() synchronized(dataList) { tempList.addAll(dataList) dataList.clear() valueList.add( \u0026#34;FX Event ${counter.value++} step 1 in ${ sTime.until(LocalTime.now(), ChronoUnit.MILLIS) }ms\u0026#34; ) } valueList.addAll(tempList) valueList.add( \u0026#34;FX Event ${counter.value++} done 1 in ${ sTime.until(LocalTime.now(), ChronoUnit.MILLIS) }ms\\n\u0026#34; ) } } dataList.add(var1 + \u0026#34; ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}\u0026#34;) } } } Here, I\u0026rsquo;ve added a tempList local variable which is just a MutableList\u0026lt;String\u0026gt;. The elements from dataList are extracted into it, dataList is cleared and the lock is released. Then, outside of the synchronized block, the contents of tempList are added to valueList and the ListToString Binding will run. The output looks like this:\nYou can see that we\u0026rsquo;ve stripped away almost 3 seconds from our background task runtime. In fact, this is now only a little more than 1 second longer than the first version, without any locking at all. I\u0026rsquo;ve added an intermediate timer, so you can see how long the synchronized part takes, and it\u0026rsquo;s usually 0ms. The Binding step seems to take longer now. This may reflect a bigger continual load on my CPU from the background threads that possibly slows down the FXAT.\nIn my testing, I found that everything continued to work fine, even as I lowered the sleep duration in each thread down to one or two milliseconds.\nReturning a Value # What we\u0026rsquo;ve done so far is good, but it falls short of solving Task as an accumulator that delivers a final value.\nThe problem is that Task.value is private, and Task.valueProperty exposes Task.value as a ReadOnlyObjectProperty, even to sub-classes of Task. The only way that we can update Task.value is by calling Task.updateValue(), and this uses the same AtomicReference logic as we saw in Task.updateMessage(), it won\u0026rsquo;t work when we are trying to add items to a List.\nIt\u0026rsquo;s one thing to add a custom valueList and expose that to the layout, but if we want our Task to work in a more generic fashion, then we need to have some way to transfer valueList to Task.value when the processing is complete. So, this is the last item we need to address.\nThere\u0026rsquo;s one issue that comes from using Platform.runLater() to batch update the ObservableList. The Task is virtually guaranteed to complete before the all the jobs have been run on the FXAT.\nWe saw this with the first version, just launching many, many jobs using Platform.runLater(). The Task completed minutes before all of the jobs had been run on the FXAT, and any layout updates that would be triggered by the completion of Task would be incorrect.\nLet\u0026rsquo;s see how this impacts the layout. To make things simpler, and easier to see where there are issues, we\u0026rsquo;ll make the results of the Task the size of the ObservableList instead of the List itself:\nprivate fun createContent(): Region = BorderPane().apply { val task = object : Task\u0026lt;Int\u0026gt;() { private val dataList = mutableListOf\u0026lt;String\u0026gt;() val valueList: ListProperty\u0026lt;String\u0026gt; = SimpleListProperty(FXCollections.observableArrayList()) val counter = SimpleIntegerProperty(0) val manualSize = SimpleIntegerProperty(0) init { updateMessage(\u0026#34;\u0026#34;) updateProgress(0, 100) setOnSucceeded { evt -\u0026gt; updateProgress(100, 100) } } private fun appendValue(var1: String) { if (Platform.isFxApplicationThread()) { valueList.add(var1) } else { val sTime = LocalTime.now() synchronized(dataList) { if (dataList.isEmpty()) { Platform.runLater { val sTime = LocalTime.now() val tempList = mutableListOf\u0026lt;String\u0026gt;() synchronized(dataList) { tempList.addAll(dataList) dataList.clear() valueList.add( \u0026#34;FX Event ${counter.value++} step 1 in ${ sTime.until(LocalTime.now(), ChronoUnit.MILLIS) }ms\u0026#34; ) } valueList.addAll(tempList) valueList.add( \u0026#34;FX Event ${counter.value++} done 1 in ${ sTime.until(LocalTime.now(), ChronoUnit.MILLIS) }ms\\n\u0026#34; ) } } dataList.add(var1 + \u0026#34; ${sTime.until(LocalTime.now(), ChronoUnit.MILLIS)}\u0026#34;) } } } override fun call(): Int { handleProcessing3( { done, total -\u0026gt; updateProgress(done, total) }, { appendValue(it) }, { updateMessage(it) }) val tempSize = valueList.size Platform.runLater { manualSize.value = tempSize } return valueList.size } } center = VBox(10.0).apply { children += HBox( 10.0, promptOf(\u0026#34;Manual Size:\u0026#34;), Label().apply { textProperty().bind(task.manualSize.asString()) }) children += HBox( 10.0, promptOf(\u0026#34;Counted Size:\u0026#34;), Label().apply { textProperty().bind(task.valueList.sizeProperty().asString()) }) children += HBox( 10.0, promptOf(\u0026#34;Task Value:\u0026#34;), Label().apply { textProperty().bind(task.valueProperty().asString()) }) children += HBox( 10.0, promptOf(\u0026#34;Task Completion:\u0026#34;), Label(\u0026#34;None\u0026#34;).apply { task.setOnSucceeded { evt -\u0026gt; this.text = task.get().toString() } }) children += Label().apply { textProperty().bind(task.messageProperty()) } children += ProgressBar().apply { progressProperty().bind(task.progressProperty()) minWidth = 200.0 } alignment = Pos.CENTER } right = TextArea().apply { textProperty().bind(ListToString(task.valueList)) } bottom = HBox(Button(\u0026#34;Run Task\u0026#34;).apply { setOnAction { Thread(task).start() } }).apply { alignment = Pos.CENTER padding = Insets(20.0) } } In the layout, I\u0026rsquo;ve added a few Labels to display various values of the size of Task.valueList:\nThe one tagged with \u0026ldquo;Counted Size\u0026rdquo; is simply bound to the size of the Task.valueList. I switched valueList over to a ListProperty instead of just an ObservableList because ListProperty has a size Property. The one tagged \u0026ldquo;Manual Size\u0026rdquo; is bound to a IntegerProperty that I added to task called manualSize. The value is updated manually by task.call(). The one tagged with \u0026ldquo;Task Value\u0026rdquo; is bound to task.valueProperty. The one tagged \u0026ldquo;Task Completion\u0026rdquo; is manually updated through the task.onSucceeded Property, which calls task.get(). All together, this is probably over-kill, but you\u0026rsquo;ll see the problem.\nThe type of task was changed from Void to Int so that we now have a value that we can grab. Ordinarily, for something like this, you\u0026rsquo;d probably make the type of task an ObservableList, but keeping it as Int makes it easier to see the issues here.\nAside from the added and changed Properties in task, the only other change was in task.call(), which now looks like this:\noverride fun call(): Int { handleProcessing3( { done, total -\u0026gt; updateProgress(done, total) }, { appendValue(it) }, { updateMessage(it) }) val tempSize = valueList.size Platform.runLater { manualSize.value = tempSize } return valueList.size } As you can see, task.call() now returns Int. Three lines have been added to the bottom of this method. First we create a local variable called tempSize and initialize it with the size of valueList. To update task.manualSize we need to be on the FXAT, so we\u0026rsquo;ll need Platform.runLater() here. Then, finally, we return valueList.size.\nA few points:\nTask.call() itself runs in a background thread. We don\u0026rsquo;t really know what invokes Task.call(). Whatever value goes into Task.value has to be updated on the FXAT. This means that whatever calls Task.call() and receives an answer will need to invoke Platform.runLater() to update Task.value. This implies some kind of delay between the end of Task.call() and the update of Task.value(). Here\u0026rsquo;s the results:\nThere\u0026rsquo;s the problem!\nThe actual, final size of valueList after all the FXAT jobs have completed is 50,502. The size of valueList at the moment that task.call() completed was only 50,335. The value that was captured by Task.onSucceeded and the value that went into Task.value was 50,335.\nI think you can see that if you weren\u0026rsquo;t interested in the size, but actually wanted Task to return the List itself, you\u0026rsquo;d be short almost 200 entries.\nThe Key Element of the Problem # The crux of the problem is that Task is designed such that Task.call() returns a value and that value is manually used to update Task.value. However, Task.call() runs on a background thread, which means that updates to the accumulating return value, running on the FXAT, may not have completed at the time that Task.call() completes.\nQuestion How do we ensure that Task.call() does not complete before all of the jobs running on the FXAT that are updating the results have completed?\nIt turns out that the key factor is that Task.call() runs on a background thread. This means that we have no problems running Thread.sleep() inside Task.call(). All we need is a way to determine if all of the jobs on the FXAT have completed!\nPausing at The End of Task.call() # Before we go there, let\u0026rsquo;s have a look at how the ThreadPool is handled:\nval threadPool = Executors.newFixedThreadPool(50) repeat(50) { threadPool.execute(job) } messageUpdater(\u0026#34;ThreadPool Started\u0026#34;) threadPool.shutdown() var counter = 0 while (!threadPool.awaitTermination(4, TimeUnit.SECONDS)) messageUpdater(\u0026#34;Still Waiting ${counter++}\u0026#34;) That while{} statement is important. More correctly, the ThreadPool.awaitTermination() is very important. This ensures that our method, running on the main background thread, doesn\u0026rsquo;t return until after all of the jobs running on all of the threads have completed and the ThreadPool has shutdown.\nSo, when we return from handleProcessing3(), we know that there are no more updates coming to Task.dataList from any background threads.\nThis means that the only way that Task.dataList can change after we return from handleProcessing3() is from a job on the FXAT. And we know that the only change that such a job makes to Task.dataList is to clear it out.\nNow we know how to determine when the last FXAT job has run, and the updates to Task.valueList are complete\u0026hellip;when Task.dataList is empty!\nAnd since we are still running on a background thread in Task.call(), we can wait for it!\noverride fun call(): Int { handleProcessing3( { done, total -\u0026gt; updateProgress(done, total) }, { appendValue(it) }, { updateMessage(it) }) while (!dataList.isEmpty()) { updateMessage(\u0026#34;Waiting for completion: ${dataList.size}\u0026#34;) println(\u0026#34;Waiting for completion: ${dataList.size}\u0026#34;) Thread.sleep(10) } Thread.sleep(100) updateMessage(\u0026#34;All jobs completed\u0026#34;) val tempSize = valueList.size Platform.runLater { manualSize.value = tempSize } return valueList.size } I\u0026rsquo;ve added a little while{} loop that uses sleep(10) to wait for the last FXAT job to run. The first time I ran this I didn\u0026rsquo;t have the last sleep(100) after the while{} loop and it still gave incomplete results.\nRemember how we used a temp variable to hold the contents of dataList so that we could clear out dataList and release the lock so that the background threads could start loading it up again without waiting for the Binding.calculateValue() to run? Well that was causing a time difference between the clearing of dataList and the population of valueList on the FXAT, and we need to account for it here.\nThat last Thread.sleep(100) gives the FXAT enough time to complete the update of valueList after dataList has been cleared out.\nThis is what it looks like when it\u0026rsquo;s done:\nAs you can see, the last FXAT update takes 65ms to run, so the 100ms sleep is long enough to handle this.\nAnd this is the console output:\n\u0026gt; Task :run Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 Waiting for completion: 2319 You can see that the while{} loop with the sleep() ran about 20 times. And now the counts are all the same, and correct.\nConclusion # I think that the way that Task is designed to handle frequent updates from background threads is absolutely brilliant. There a number of things to learn from this:\nUse Task! # Somehow, people seem to think that using Task is a heavyweight approach to background processing, and they prefer to just drop a Runnable into a new Thread and let it go.\nBut Task is designed to provide the integration between your GUI and your background job, and to provide it in a way the behaves nicely with the FXAT. This is one place where the JavaDocs are dead-on:\nQuote A fully observable implementation of a FutureTask. Task exposes additional state and observable properties useful for programming asynchronous tasks in JavaFX, as defined in the Worker interface.\nSo use it. Use it every time!\nLook at the JavaFX Source Code # Sometimes, in the JavaDocs there are hints about interesting things that you simply cannot appreciate until you look \u0026ldquo;under-the-hood\u0026rdquo; and see how it is done. Check out the JavaDocs for Task.updateMessage():\nQuote Updates the message property. Calls to updateMessage are coalesced and run later on the FX application thread, so calls to updateMessage, even from the FX Application thread, may not necessarily result in immediate updates to this property, and intermediate message values may be coalesced to save on event notifications.\nI\u0026rsquo;m not sure that \u0026ldquo;coalesced\u0026rdquo; is the right word here, since it doesn\u0026rsquo;t combine them together but simply replaces older, unprocessed, values with newer ones. But this description strongly hints at something interesting going on behind the scenes.\nLooking at the source code for things like this shows you the proper way to handle complex issues with JavaFX. Don\u0026rsquo;t be scared to look, and most IDE\u0026rsquo;s will take you right to the internal source code from the method call in your application code with a single click.\nOnly Use Platform.runLater() Thoughtfully # I constantly see people online recommending, \u0026ldquo;Wrap it in Platform.runLater()\u0026rdquo;. I see this in tutorial articles as well as discussions and on StackOverflow.\nI consider this to be an anti-pattern, if not an out-and-out \u0026ldquo;code smell\u0026rdquo;.\nSure, in the solution presented here we did use Platform.runLater(), but in a considered and deliberate manner. And also, it was wrapped inside a utility class and not integrated with any of the application/business logic at all.\nYou may have noticed that code in handleProcessing() never changed throughout all of the examples in this article. That\u0026rsquo;s because it\u0026rsquo;s logic was decoupled from the logic in the Task. We updated Task.call() and Task.appendValue(), but never had to touch the business logic.\nAt the other end, the GUI was connected to the Task through that ListToString Binding. That Binding is owned by the layout, as are all other elements related to how the layout utilizes the results, and the partial results, from the Task. If it turned out that the ListToString was causing performance issues with the GUI, then that\u0026rsquo;s the layout\u0026rsquo;s problem to solve.\nHonestly, converting the ObservableList to a String to display it in a TextArea is a pretty bad design decision. It would be much better to use a ListView which is designed to be used in this manner. You could even change the output from String to some structured object, and then have better data available to format the display of the information.\nBeware the JavaDocs # While the actual code that runs JavaFX is often brilliant, the associated JavaDocs often are sorely lacking. In this particular example, the approach suggested by the JavaDocs is just bad - wrong even.\nWhat is also interesting is that the JavaDocs for Platform.runLater() contain this warning:\nQuote NOTE: applications should avoid flooding JavaFX with too many pending Runnables. Otherwise, the application may become unresponsive. Applications are encouraged to batch up multiple operations into fewer runLater calls.\nThe approach suggested by the JavaDocs for Task totally disregard this warning.\nI wish I could say this was a unique occurance, but lots of JavaDoc entries have dubious advice in their introductory sections.\nMulti-threading is Always Complicated # In this application we had three kinds of threads:\nThe FX Application Thread (FXAT) The main background thread to manage the processing 50 ExecutorPool threads that did the actual work It was extremely important to keep track of what activities take place on which thread. For instance, the main background thread controlled the completion of the Task.call() logic. It was extremely important that this thread not run on to completion before all of the ExecutorPool threads had finished.\nThese three thread types all shared access to one data element, Task.dataList. Understanding how each thread used this data element and how access needed to be controlled to ensure that concurrency issues did not arise was key to architecting the solution properly. We saw how keeping hold of the lock unnecessarily in the FXAT caused the entire process to take almost 3 seconds longer.\nThis Solution is Still a Bit Kludgey # One thing that I do not like about this approach is that the intermediate results, while the Task is running, are accessed from the Task differently from how the final result is accessed.\nTo be clear, you can still always access the final result from Task.valueList, but the rest of the Task infrastructure is designed around the result being in Task.value. For instance, Task.get() delegates to Task.value.get().\nBut, unfortunately, there\u0026rsquo;s no clean way to get the partial results into Task.value because it\u0026rsquo;s private to Task and can only be updated through Task.updateValue(). Yes, you could copy the entire ObservableList in Task.valueList over, but that doesn\u0026rsquo;t seem to meet the definition of \u0026ldquo;clean\u0026rdquo; to me. There doesn\u0026rsquo;t seem to be any way to create a replacement for Task that accumulates rather than replaces, and still works the same way as Task.\nYou cannot achieve this by extending Task because key elements are private, and key methods are final. You cannot simply use Task as a basis to create a brand new class that implements FutureTask and Worker because Task uses methods from JavaFX packages that aren\u0026rsquo;t exported to outside modules. This makes it very difficult to implement the Event firing mechanism in Task.\nMaybe there is a better way to craft this into a generic solution that can be added to a library, but I can\u0026rsquo;t find it.\n","date":"2 July 2025","externalUrl":null,"permalink":"/javafx/elements/fxat/task-list-progress/","section":"JavaFX","summary":"What if you want to monitor the progress of a Task that is collecting data into a List?  This article shows an approach to handling accumulated data in on ObserableList while a Task is running while still playing nicely with the FXAT.","title":"Task Partial Results: Lists","type":"javafx"},{"content":" Introduction # The ComobBox! Everybody\u0026rsquo;s go-to control for multiple choice input. It\u0026rsquo;s one of those elements that\u0026rsquo;s dead simple to get started with, but has a lot of power that many people don\u0026rsquo;t use.\nIn this article we are going to cover the absolute basics, and then take a look at some slightly more involved situations that seem to come up all the time. We\u0026rsquo;ll also see how easy it is to massage the presentation of the selectable items in your ComboBox to make it more user-friendly.\nSimple Implementation # The easiest way to use a ComboBox is to just stuff some values in a list, bind the ValueProperty and stick it in a layout:\nclass ComboBoxExample : Application() { private val comboBoxValue: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) override fun start(stage: Stage) { val scene = Scene(createContent()).apply { addWidgetStyles() } stage.scene = scene stage.show() } private fun createContent(): Region = VBox(20.0).apply { children += Label().apply { textProperty().bind(comboBoxValue.map { \u0026#34;The selected value is: $it\u0026#34; }.orElse(\u0026#34;No value has been selected\u0026#34;)) } children += ComboBox\u0026lt;String\u0026gt;().apply { items += listOf(\u0026#34;Fred\u0026#34;, \u0026#34;George\u0026#34;, \u0026#34;Mary\u0026#34;, \u0026#34;Jane\u0026#34;) comboBoxValue.bind(valueProperty()) } padding = Insets(40.0) } } fun main() = Application.launch(ComboBoxExample::class.java) Note the use of ObservableValue.map() and ObservableValue.orElse() to translate the raw value from the ComboBox to the text for the Label. This is new since JavaFX 19, and it greatly simplifies a lot of bindings.\nIt starts out with a Null value, so the ComboBox just looks like an empty Button with an arrow. Click anywhere on it and it opens up with a list of choices for the user to click on:\nPicking one then populates the Button, and closes the pop-up list:\nStandard Configuration Options # Editable ComboBoxes work in two modes, editable and non-editable. \u0026ldquo;Editable\u0026rdquo; means that the user can type into the area in the ComboBox that looks like a TextField and enter values that aren\u0026rsquo;t in the pop-up list. The default is non-editable. Items The contents of of the pop-up list are controlled by an ObservableList called items. VisibleRowCount This is the maximum size of the pop-up list, in rows. The default is 10, but you can make it bigger or smaller, depending on your layout. Value The currently selected value in the ComboBox. This can be set programmatically if, for instance, you want to set a default value. Note that if you do change the value programmatically, it will automatically select the corresponding value in the pop-up list. The value does NOT have to be one of the values in the pop-up list, however. OnAction You can trigger an EventHandler whenever the ValueProperty of the ComboBox is changed. This is the OnAction event. The pop-up list works very much like a ListView and does, therefore, have a SelectionModel. For some reason, and I don\u0026rsquo;t know why (probably some copypasta that ended up in every on-line tutorial), people seem to think that the way to get a ComboBox's value is to go through the pop-up list SelectionModel. In reality, there\u0026rsquo;s almost never any reason to go through this round-about route - just use the Value property.\nComboBox\u0026lt;Object\u0026gt; # So what if you don\u0026rsquo;t want to have your ComboBox return a primitive like int, or a String? How do you do that? ComboBox is a generic class, so to use one you\u0026rsquo;ll need to specify the type of data values that it will return, and that will be held in items.\nFirst off, though, you need to remember that the items that populate the list in a ComboBox should be part of your Presentation Model, and they should be designed with the intent that they are going to be used to support your view. This means that the idea that you\u0026rsquo;re just going to rip some customer records out of a database and dump them into a ComboBox as the choices is probably a bad design decision.\nThat being said, there are times when you might have a more complex object used to populate your ComboBox. It\u0026rsquo;s generally considered bad form to rely on toString() to create the display for your ComboBox, so you\u0026rsquo;ll need to customize it in some way to make it work properly. This means that you\u0026rsquo;re going to have to treat the popup as a ListView (which it is) and customize the cells.\nIn this example, we\u0026rsquo;re going to look at using an object with two fields. One of the easiest ways to do that is to use an Enum class, so that\u0026rsquo;s what we\u0026rsquo;ll do.\nNo surprise, we\u0026rsquo;re going to use the same Enum that we used in the ListView article, because the popup is pretty much just a ListView:\nclass ComboBoxExample1 : Application() { private val comboBoxValue: ObjectProperty\u0026lt;Animal\u0026gt; = SimpleObjectProperty() override fun start(stage: Stage) { val scene = Scene(createContent()).apply { addWidgetStyles() } stage.scene = scene stage.show() } private fun createContent(): Region = VBox(20.0).apply { children += Label().apply { textProperty().bind(comboBoxValue.map { \u0026#34;The selected value is: $it\u0026#34; }.orElse(\u0026#34;No value has been selected\u0026#34;)) } children += ComboBox\u0026lt;Animal\u0026gt;().apply { items += listOf(Animal.BEAR, Animal.OWL, Animal.BIRD, Animal.SHARK, Animal.HEDGEHOG, Animal.LOBSTER) comboBoxValue.bind(valueProperty()) } padding = Insets(40.0) } } enum class Animal(val animalName: String, imageName: String) { BEAR(\u0026#34;Bear\u0026#34;, \u0026#34;bear.png\u0026#34;), BIRD(\u0026#34;Bird\u0026#34;, \u0026#34;bird.png\u0026#34;), HEDGEHOG(\u0026#34;Hedgehog\u0026#34;, \u0026#34;hedgehog.png\u0026#34;), LOBSTER(\u0026#34;Lobster\u0026#34;, \u0026#34;lobster.png\u0026#34;), OWL(\u0026#34;Owl\u0026#34;, \u0026#34;owl.png\u0026#34;), SHARK(\u0026#34;Shark\u0026#34;, \u0026#34;shark.png\u0026#34;); val imageView: ImageView = ImageView(this::class.java.getResource(imageName)?.toExternalForm()) override fun toString(): String { return animalName } } fun main() = Application.launch(ComboBoxExample1::class.java) Which looks like this:\nHere, we are just letting the Animal.toString() do the job of populating the pop-up contents. But, what if we want to have the image associated with the Animal appear in the ComboBox? We have to create a custom CellFactory for this:\nchildren += ComboBox\u0026lt;Animal\u0026gt;().apply { items += listOf(Animal.BEAR, Animal.OWL, Animal.BIRD, Animal.SHARK, Animal.HEDGEHOG, Animal.LOBSTER) comboBoxValue.bind(valueProperty()) setCellFactory { ListCell\u0026lt;Animal\u0026gt;().apply { graphicProperty().bind(itemProperty().map { it.imageView }) textProperty().bind(itemProperty().map { it.animalName }) } } } This is an approach you don\u0026rsquo;t see in most tutorials. There\u0026rsquo;s no custom class extending ListCell here.\nRemember that ListCell is a subclass of Labeled, which means that its presentation can work very much like a standard Label. Specifically, we have both a Graphic and a Text element, and we have a graphicProperty() and a textProperty() method. If we bind those properties to data in item, then they\u0026rsquo;ll just show up on the screen and it will work.\nSince we\u0026rsquo;re not going to override updateItem(), nor are we going to customize the layout of the ListCells, we don\u0026rsquo;t have to create a custom ListCell class, not even an anonymous inner class. We can just instantiate a plain vanilla ListCell and configure it from outside, using the methods already available from ListCell.\nAnd it looks like this:\nBut, you can see that there is a bit of a problem. The main part of the CheckBox doesn\u0026rsquo;t have the image. This area is called the Button and it has a ListCell in it, just like the pop-up ListView does. We can set it, too, although it doesn\u0026rsquo;t need a factory but just an instance of the ListCell:\nchildren += ComboBox\u0026lt;Animal\u0026gt;().apply { items += listOf(Animal.BEAR, Animal.OWL, Animal.BIRD, Animal.SHARK, Animal.HEDGEHOG, Animal.LOBSTER) comboBoxValue.bind(valueProperty()) setCellFactory { createCell() } buttonCell = createCell() } . . . private fun createCell() = ListCell\u0026lt;Animal\u0026gt;().apply { itemProperty().subscribe(Consumer { graphic = it?.imageView text = it?.animalName }) } In order to keep with DRY (Don\u0026rsquo;t Repeat Yourself), we\u0026rsquo;ve pulled the ListCell instantiation into its own method so that we can call if from the factory, and then directly to set the Button. However, there was a problem when trying to bind the Graphic and Text properties to Item fields. It turns out that the inner workings of the ComboBox skin actually set the values of Graphic and Text in the Button component directly in some circumstances, and the binding causes grief with that. So the bindings have been turned into a Subscription that can co-exist nicely with ComboBox skin code.\nNow, it looks like this:\nUh oh! Where did the image go for the shark in the list? Since the Enum contains an actual ImageView which is a Node, and each Node can only appear on the SceneGraph once, it gets removed from the pop-up and put into the Button. To fix that, we\u0026rsquo;ll need to change the design of Animal (honestly, having ImageView in Animal was probably a bad idea to start with):\nclass ComboBoxExample1 : Application() { private val comboBoxValue: ObjectProperty\u0026lt;Animal\u0026gt; = SimpleObjectProperty() override fun start(stage: Stage) { val scene = Scene(createContent(), 280.0, 300.0).apply { addWidgetStyles() } stage.scene = scene stage.show() } private fun createContent(): Region = VBox(20.0).apply { children += Label().apply { textProperty().bind(comboBoxValue.map { \u0026#34;The selected value is: $it\u0026#34; }.orElse(\u0026#34;No value has been selected\u0026#34;)) } children += ComboBox\u0026lt;Animal\u0026gt;().apply { items += listOf(Animal.BEAR, Animal.OWL, Animal.BIRD, Animal.SHARK, Animal.HEDGEHOG, Animal.LOBSTER) comboBoxValue.bind(valueProperty()) setCellFactory { createCell() } buttonCell = createCell() } padding = Insets(40.0) } private fun createCell() = ListCell\u0026lt;Animal\u0026gt;().apply { val imageView = ImageView() itemProperty().subscribe(Consumer { imageView.image = it?.image graphic = imageView text = it?.animalName }) } } enum class Animal(val animalName: String, imageName: String) { BEAR(\u0026#34;Bear\u0026#34;, \u0026#34;bear.png\u0026#34;), BIRD(\u0026#34;Bird\u0026#34;, \u0026#34;bird.png\u0026#34;), HEDGEHOG(\u0026#34;Hedgehog\u0026#34;, \u0026#34;hedgehog.png\u0026#34;), LOBSTER(\u0026#34;Lobster\u0026#34;, \u0026#34;lobster.png\u0026#34;), OWL(\u0026#34;Owl\u0026#34;, \u0026#34;owl.png\u0026#34;), SHARK(\u0026#34;Shark\u0026#34;, \u0026#34;shark.png\u0026#34;); val image: Image = Image(this::class.java.getResource(imageName)?.toExternalForm()) override fun toString(): String { return animalName } } fun main() = Application.launch(ComboBoxExample1::class.java) Now we have the Animal holding an Image, which is not a Node, and then loading that Image into an ImageView that is associated with each ListCell.\nAnd it looks like this:\nCodes and Descriptions # One situation that pops up all the time is where you have some sort of list of codes, and you want to display some descriptive text about the code instead of the code in your ComboBox. But you still want to return the code from the ComboBox selection. How do you do this?\nA lot of programmers try using a Map\u0026lt;Code, Description\u0026gt; to establish the relationship between the code and its description. But then it\u0026rsquo;s hard to go on from there.\nHonestly, using a Map instead of creating a class to hold a value pair is really not a great strategy to begin with. JavaFX has a general purpose class called Pair that you can use for this purpose. It has two fields, key and value and all the methods you might need with it. So you could create a List\u0026lt;Pair\u0026lt;Code, Description\u0026gt;\u0026gt; just as easily as you could a Map.\nSo let\u0026rsquo;s use ObservableList\u0026lt;Pair\u0026gt;\u0026hellip;\nclass ComboBoxExample3 : Application() { private val comboBoxValue: ObjectProperty\u0026lt;Pair\u0026lt;String, String\u0026gt;\u0026gt; = SimpleObjectProperty() override fun start(stage: Stage) { val scene = Scene(createContent(), 280.0, 300.0).apply { addWidgetStyles() } stage.scene = scene stage.show() } private fun createContent(): Region = VBox(20.0).apply { children += Label().apply { textProperty().bind(comboBoxValue.map { \u0026#34;The selected value is: ${it.key}\u0026#34; } .orElse(\u0026#34;No value has been selected\u0026#34;)) } children += ComboBox\u0026lt;Pair\u0026lt;String, String\u0026gt;\u0026gt;().apply { items += listOf( Pair(\u0026#34;GB\u0026#34;, \u0026#34;Great Britain\u0026#34;), Pair(\u0026#34;FR\u0026#34;, \u0026#34;France\u0026#34;), Pair(\u0026#34;AU\u0026#34;, \u0026#34;Australia\u0026#34;), Pair(\u0026#34;ES\u0026#34;, \u0026#34;Spain\u0026#34;) ) comboBoxValue.bind(valueProperty()) setCellFactory { ListCell\u0026lt;Pair\u0026lt;String, String\u0026gt;\u0026gt;().apply { textProperty().bind(itemProperty().map { it.value }) } } buttonCell = ListCell\u0026lt;Pair\u0026lt;String, String\u0026gt;\u0026gt;().apply { itemProperty().subscribe(Consumer { text = it?.value }) } } padding = Insets(40.0) } } fun main() = Application.launch(ComboBoxExample3::class.java) Which looks like this:\nThe key point here is that it doesn\u0026rsquo;t take a mountain of code to populate your ComboBox values with just the description, while still retaining the code in ComboBox.value. All you need is a standard ListCell and binding its textProperty() to a Binding derived from its itemProperty() extracting the Pair.value field does the trick.\nThis technique will work with much more complex objects, too. Just set up a Binding that composes a String made up from whatever class you are putting into the ComboBox.\nAt the same time, you can still use Map, in which case you\u0026rsquo;ll need to put the Map.entrySet() into an ObservableList. Map.Entry has key and value as well.\nLinking ComboBoxes # Something that comes up fairly often is the idea of having two ComboBoxes that are somehow connected to each other. Changing the selection in one ComboBox changes the list of items available in the second ComboBox.\nWe\u0026rsquo;ll go back to our Animal based ComboBox and then add another that has names of individual animals making the screen a \u0026ldquo;Pick your favourite animal at the zoo\u0026rdquo; application. Let\u0026rsquo;s look at the code first, then see how it works:\nclass ComboBoxExample2 : Application() { private val animalValue: ObjectProperty\u0026lt;Animal\u0026gt; = SimpleObjectProperty() private val nameValue: ObjectProperty\u0026lt;String\u0026gt; = SimpleObjectProperty() override fun start(stage: Stage) { val scene = Scene(createContent(), 400.0, 300.0).apply { addWidgetStyles() } stage.scene = scene stage.show() } private fun createContent(): Region = VBox(20.0).apply { children += Label().apply { textProperty().bind(FavouriteAnimalBinding(animalValue, nameValue)) } children += HBox(14.0).apply { children += ComboBox\u0026lt;Animal\u0026gt;().apply { items += listOf(Animal.BEAR, Animal.OWL, Animal.BIRD, Animal.SHARK, Animal.HEDGEHOG, Animal.LOBSTER) animalValue.bind(valueProperty()) setCellFactory { createCell() } buttonCell = createCell() } children += ComboBox\u0026lt;String\u0026gt;().apply { val zooAnimals = populateZooAnimals() itemsProperty().bind(animalValue.map { zooAnimals[it] }) nameValue.bind(valueProperty()) placeholder = Label(\u0026#34; Pick an Animal first \u0026#34;) } } padding = Insets(40.0) } private fun populateZooAnimals(): Map\u0026lt;Animal, ObservableList\u0026lt;String\u0026gt;\u0026gt; = mapOf\u0026lt;Animal, ObservableList\u0026lt;String\u0026gt;\u0026gt;( Animal.OWL to FXCollections.observableArrayList(\u0026#34;Hoodini\u0026#34;, \u0026#34;Olive Owl\u0026#34;, \u0026#34;Whoolio\u0026#34;, \u0026#34;Barney\u0026#34;), Animal.SHARK to FXCollections.observableArrayList(\u0026#34;Striper\u0026#34;, \u0026#34;Steve\u0026#34;, \u0026#34;Freddy\u0026#34;, \u0026#34;Sushi\u0026#34;), Animal.LOBSTER to FXCollections.observableArrayList(\u0026#34;Clawdia\u0026#34;, \u0026#34;Kevin\u0026#34;, \u0026#34;Butter\u0026#34;), Animal.BEAR to FXCollections.observableArrayList(\u0026#34;Gummi\u0026#34;, \u0026#34;Paddington\u0026#34;, \u0026#34;Baloo\u0026#34;, \u0026#34;Ted\u0026#34;), Animal.BIRD to FXCollections.observableArrayList(\u0026#34;Tweety\u0026#34;, \u0026#34;Comet\u0026#34;, \u0026#34;Chickpea\u0026#34;, \u0026#34;Cracker\u0026#34;), Animal.HEDGEHOG to FXCollections.observableArrayList(\u0026#34;Sonic\u0026#34;, \u0026#34;Spike\u0026#34;, \u0026#34;Whoolio\u0026#34;, \u0026#34;Herbert\u0026#34;) ) private fun createCell() = ListCell\u0026lt;Animal\u0026gt;().apply { val imageView = ImageView() itemProperty().subscribe(Consumer { imageView.image = it?.image graphic = imageView text = it?.animalName }) } } class FavouriteAnimalBinding(private val animal: ObservableValue\u0026lt;Animal?\u0026gt;, val name: ObservableValue\u0026lt;String?\u0026gt;) : StringBinding() { init { super.bind(animal, name) } override fun computeValue(): String { val favString = \u0026#34;My favourite animal at the zoo is\u0026#34; return animal.value?.let { animalType -\u0026gt; name.value?.let { animalName -\u0026gt; \u0026#34;$favString $animalName the $animalType\u0026#34; } ?: \u0026#34;$favString a $animalType\u0026#34; } ?: \u0026#34;I haven\u0026#39;t picked a favourite yet\u0026#34; } } fun main() = Application.launch(ComboBoxExample2::class.java) Here\u0026rsquo;s what it looks like at launch:\nAs you pick an Animal:\nAs you select a name:\nAnd once a name has been selected:\nThe trick with this is to treat the ObservableList of the second ComboBox as a Property itself, which ComboBox gives us as itemsProperty(). This is an ObjectProperty\u0026lt;ObservableList\u0026lt;String\u0026gt;\u0026gt;, which means that we can bind it. Do do that, we use ObservableValue.map() to extract an ObservableList from a Map\u0026lt;Animal,ObservableList\u0026gt;.\nEach entry in our Map is keyed with an Animal, and the value is an ObservableList\u0026lt;String\u0026gt; of names of particular animals of that type. The ObservableValue.map() function here just pulls the List from the Map using the current Animal value selected in the first ComboBox. An equivalent way to do this in a version of JavaFX earlier than 19 would be Bindings.valueAt().\nOne last feature you should probably include is a Placeholder on the second ComboBox. This is what will be shown if the ListView in the pop-up is empty - as it will be when an Animal has not yet been selected. In this case, we\u0026rsquo;re just showing a Label that instructs the user to pick an animal first:\nThis is done with this line of code:\nplaceholder = Label(\u0026#34; Pick an Animal first \u0026#34;) Editable ComboBoxes # Making a ComboBox editable means that the user can type in the Button cell and enter a value that potentially isn\u0026rsquo;t in the drop-down list. Here\u0026rsquo;s what happens when we make the ComboBox for name editable:\nThat ComboBox now looks like a TextField with a twisty Button beside it. When we type in something and hit \u0026lt;Enter\u0026gt; this is what happens:\nAnd you can see that none of the items in the drop-down are selected:\nIf we change it to one of the items in the drop-down, then the ComboBox makes the connection and highlights it for us:\nThe connection isn\u0026rsquo;t made until you commit the change by hitting \u0026lt;Enter\u0026gt; or taking the focus away from the ComboBox. You\u0026rsquo;re going to have to trust me on this, as I can\u0026rsquo;t get a screenshot without taking focus away from ComboBox.\nConclusion # And that\u0026rsquo;s it for our tour of the basics of ComboBoxes. With the information here you can probably handle the vast majority of ComboBox situations that come up when designing a JavaFX application.\nThe main things to keep in mind:\nGrab the value from ComboBox.valueProperty(), don\u0026rsquo;t go digging into the SelectionModel to get the selected item. This is especially true if your ComboBox is editable.\nYou can use the Labeled nature of ListCell to do a fair bit of manipulation of the presentation of your items without having to extend ListCell into a custom class.\nYou can use a generic class like Pair to display human-friendly names for items that are codes. Also, Enum can be really useful for this too.\n","date":"30 August 2024","externalUrl":null,"permalink":"/javafx/elements/comboboxes/","section":"JavaFX","summary":"The ComboBox is the classic “choose from a list” screen widget that you see everywhere. In JavaFX, the ComboBox is a fairly complex structure that involves a TextBox and a ListView tied together. In this article you’ll learn how to configure the ComboBox to fit a variety of uses.","title":"Understanding the ComboBox","type":"javafx"},{"content":" Introduction # In Part 1 of this series, we looked at the Observable classes designed to wrap aribitrary Object values, and in Part 2 we looked at the typed Observable classes. In this article we are going to look at the Observables designed to hold ObservableLists.\nThis is a subject that always confused me. Aren\u0026rsquo;t ObserableLists already Observable? And doesn\u0026rsquo;t ObseravbleList already implement the Obseravble interface? So why wrap an ObservableList inside an Observable wrapper?\nThe whole thing, at first glance, seems very circular. We have an Observable that wraps an ObservableList and supports these same methods as ObservableList. What\u0026rsquo;s the point?\nThe point is that this turns out to be awesomely cool! And not just cool, but super useful, too. In this article we\u0026rsquo;ll first look at how ListProperty behaves, then we\u0026rsquo;ll look at how the various interfaces and classes go together, and then we\u0026rsquo;ll take a look at some practical examples.\nAnd, by the way, everything about ListProperty is also true for MapProperty and SetProperty too.\nObservableList Observables in Action # The reality is, you\u0026rsquo;ve already used a Property that wraps an ObseravbleList if you have ever used TableView. That\u0026rsquo;s because the items field in TableView is an ObjectProperty\u0026lt;ObservableList\u0026lt;S\u0026gt;\u0026gt;. But it\u0026rsquo;s highly unlikely that you\u0026rsquo;ve ever used this as a Property, meaning that you\u0026rsquo;ve probably never put a Listener on it or bound it to or from anything. And even then, ObjectProperty\u0026lt;ObservableListe\u0026lt;S\u0026gt;\u0026gt; is still not ListProperty\u0026lt;S\u0026gt;, which is where the real coolness lies.\nWe should have a closer look at what this does.\nThe best way to figure out the value of List Obseravbles is to see one in action\u0026hellip;\nThe Basic Scenario # Here\u0026rsquo;s a small program that can test how ObjectProperty\u0026lt;ObservableList\u0026lt;String\u0026gt;\u0026gt; works:\nclass Example1 : Application() { @Throws(Exception::class) override fun start(primaryStage: Stage) { val mainScene = Scene(createContent(), 840.0, 700.0) primaryStage.scene = mainScene primaryStage.show() } private fun createContent(): Region = VBox(20.0).apply { val list1 = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;ghi\u0026#34;) val list2 = FXCollections.observableArrayList(\u0026#34;jkl\u0026#34;, \u0026#34;mno\u0026#34;) val messages = SimpleStringProperty(\u0026#34;\u0026#34;) val listProperty = SimpleListProperty(list1) listProperty.subscribe { oldVal, newVal -\u0026gt; messages.value += \u0026#34;Change -\u0026gt; Old: $oldVal New: $newVal\\n\u0026#34; } list1.subscribe { messages.value += \u0026#34;List1 Invalidated\\n\u0026#34; } children += Label().apply { textProperty().bind(listProperty.sizeProperty().map { \u0026#34;List Size: $it\u0026#34; }) } children += TextArea().apply { textProperty().bind(messages) prefHeight = 300.0 } children += Button(\u0026#34;Add Element to List1\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Add Element to List1 clicked\\n\u0026#34; list1.add(\u0026#34;xyz\u0026#34;) } } children += Button(\u0026#34;Swap to List2\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Swap List clicked\\n\u0026#34; listProperty.set(list2) } } padding = Insets(30.0) } } fun main() = Application.launch(Example1::class.java) Data-wise, we have two ObservableArrayLists\u0026lt;String\u0026gt; called list1 and list2, and a SimpleListProperty called listProperty. We start off with list1 loaded into listProperty.\nOn the screen, we have a Label() with its textProperty bound to listProperty.sizeProperty(). Then we have a TextArea with its textProperty bound to a StringProperty called messages.\nWe\u0026rsquo;ve added a Subscription to the listProperty to add a line to messages whenever its value changes. Then we\u0026rsquo;ve added a Subscription to list1 to tell us whenever it becomes invalidated.\nFinally, we have two Buttons. The first adds a line to messages and adds a new element to list1. The second swaps list2 into listProperty after adding a line to messages so that we can tell when it was clicked.\nLet\u0026rsquo;s see what happens. First we\u0026rsquo;ll click the first Button a couple of times:\nYou can see that the list size of 5 matches the size of list1 which is still loaded into listProperty. You can see the two messages from clicking the first Button twice, and you can see the two \u0026ldquo;Invalidation\u0026rdquo; messages from the Listener on list1.\nThe really interesting part is that you can see the messages from the Subscription on listProperty. But we didn\u0026rsquo;t change the value of listProperty!\nThis tells us the changes to the ObseravbleList in a ListProperty trigger a change in the ListProperty itself! But you can see that both oldVal and newVal are still the same thing. So it\u0026rsquo;s not like we get a snapshot of what the ObservableList looked like before and after the change.\nThen we\u0026rsquo;ll click on the second Button and then the first Button a few more times:\nYou can see the message where the second Button was clicked, and then the Subscription message. Now we have different values for oldVal and newVal. You can also see that the listProperty.sizeProperty() has changed from the Label at the top.\nNext the first Button was clicked twice more, and you can see the messages from the Invalidation Subscription on list1. Interestingly, even though we haven\u0026rsquo;t done any getValue() operations on list1 it seems to be revalidating itself, because the second click also triggered the Subscription.\nAdding a ListChangeListener # There\u0026rsquo;s another thing we can try. ListProperty supports ObseravbleList.addListener(ListChangeListener) which will show us actual changes.\nLet\u0026rsquo;s see if we swap out the Subscription on listProperty with one of those:\nlistProperty.addListener(ListChangeListener { change -\u0026gt; messages.value += \u0026#34;listProperty change: $change \\n\u0026#34; }) And we\u0026rsquo;ll add another Button to add an element to list2:\nchildren += Button(\u0026#34;Add Element to List2\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Add Element to List2 clicked\\n\u0026#34; list2.add(\u0026#34;xyz\u0026#34;) } } And this is what happens:\nWe start off by clicking on the first Button and we see list1 invalidated and the ListChangeListener on listProperty fires and adds its message. Then we click on the Button to add an element to list2, which generates no new messages other than the Button click message. We click the first Button again, and see all the messages that we expect to see.\nThen we click the \u0026ldquo;Swap\u0026rdquo; Button and we see the ListChangeListener on listProperty fire again! It tells us that the entire ObseravbleList has been changed by showing us the elements (all of them) being replaced. Notice that this is fundamentally different from the way that the old Subscription worked by showing us the change in the wrapper value.\nNext, the first Button was clicked again. This generated the click message, and the list1 invalidation message, but no messages from the ListChangeListener. So we can see that the ListChangeListener didn\u0026rsquo;t just get passed on down to list1 through listProperty.\nFinally, the Button to add an element to list2 was clicked. We see the click message, and then a message from the ListChangeListener on listProperty, telling us that a new element was added to listProperty.\nAdding Elements to the Wrapper Observable # ListProperty also supports the List.add() method. What happens if we try that.\nWe\u0026rsquo;ll change the \u0026ldquo;Add to List1\u0026rdquo; Button to add to listProperty instead. Then we\u0026rsquo;ll take away \u0026ldquo;Add to List2\u0026rdquo; and put an Invalidation Subscription on list2:\nchildren += Button(\u0026#34;Add Element to listProperty\u0026#34;).apply { setOnAction { messages.value += \u0026#34;Add Element to listProperty clicked\\n\u0026#34; listProperty.add(\u0026#34;xyz\u0026#34;) } } Let\u0026rsquo;s see what happens:\nYou can see the \u0026ldquo;Add\u0026rdquo; Button is clicked twice, and we get the messages that we would expect; list1 is invalidated and the change on listProperty is shown both times.\nThen the \u0026ldquo;Swap\u0026rdquo; Button is clicked and we see the click message and the change of all of the elements in listProperty displays. No InvalidationListeners fire.\nFinally the \u0026ldquo;Add\u0026rdquo; Button is clicked twice more and we see that list2 is invalidated and the change on listProperty displays twice.\nWhat This Means # In our examples, we see how a ListProperty acts exactly like a Property\u0026lt;ObseravbleList\u0026gt; but also exactly like the ObservableList that is currently in its value.\nNow let\u0026rsquo;s take a look at how all of the classes and elements hook up together\u0026hellip;\nThe List Classes and Interfaces # We\u0026rsquo;ll start off with the chart, which looks quite a bit like the charts from the first two parts of this series:\nHere you can see that all of the generic \u0026lt;T\u0026gt;\u0026rsquo;s have been replaced with \u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; and the typed entries are of \u0026lt;E\u0026gt;. I\u0026rsquo;ve used ObL as a shorthand for ObservableList\u0026lt;E\u0026gt; to avoid having to use a tiny, tiny font.\nUnderstand, though, that there is no interface called ObservableValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;, it\u0026rsquo;s just ObservableValue\u0026lt;T\u0026gt; with the \u0026lt;T\u0026gt; resolved as a \u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;. The same holds true for all of the other entries on the chart defined with \u0026lt;ObL\u0026gt;.\nThis chart is a little bit more complicated in its relationships because of the dual base interfaces; ObservableValue and ObservableList. However, you can see that every entry with \u0026ldquo;List\u0026rdquo; in its name implements ObservableList, while those without it do not. For instance, WritebleListValue implements ObservableList, while Property does not.\nThe Interfaces # We\u0026rsquo;ll start with the interfaces. Some of the descriptions are a little bit stripped down from what you\u0026rsquo;ll see in Part 1 . I want this article to be complete within itself, but I don\u0026rsquo;t want to repeat a lot of information that you can find in Part 1 .\nObservable \u0026amp; ObservableValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; # At the top of the chart, you\u0026rsquo;ll find the interface, Observable. You\u0026rsquo;ll notice that it is not typed at all, it\u0026rsquo;s not even a generic. That\u0026rsquo;s because all of its methods do not deal with data.\nThis is the root for all of the other classes and interfaces, but it\u0026rsquo;s really quite simple and only defines three methods: addListener(), removeListener() and subscribe(). These are all related to the process called \u0026ldquo;Invalidation\u0026rdquo;.\nThe next interface down is ObservableValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;, which extends Observable. The key method in this interface is ObservableValue.getValue(), which allows us to actually read the value and will re-validate the Observable.\nNow that we can read the value we can have the methods related to ChangeListeners, including those related to Subscriptions. The other methods it adds are the map() and flatMap() methods used for transforming an ObservableValue into a different ObservableValue.\nThere is also the ObservableObjectValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; interface, which introduces the get() method, which at this point is effectively identical to getValue().\nObservableListValue\u0026lt;E\u0026gt; # ObservableListValue is where the List Observables start to get very different from the other typed Observables. This interface extends ObservableObjectValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;, as expected, but it also implements ObservableList\u0026lt;E\u0026gt;. Unlike, for instance, ObservableStringValue, it\u0026rsquo;s not just a placeholder entry that you can treat the same as ObservableObjectValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;.\nThis is the place on the chart where ObservableValue takes on the characteristics of ObservableList. In addition to the getValue and the two types of Listener methods we also ListChangeListener methods along with all of the methods in List, Collection, and Iterable. There are some extra methods for manipulating lists as well; setAll(), addAll(), and removeAll().\nFinally, we get filtered() and sorted() which generate FilteredList and SortedList.\nReadOnlyProperty\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; \u0026amp; WritableValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; # These are the two parent interfaces for all of the Property classes.\nReadOnlyProperty\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; simply introduces the getBean() and getName() methods. These are presumably meaningful methods if you are serializing your Properties.\nThe interface WritableValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; gives us the setValue() method. Now we can actually put something into an Observable!\nWritableObjectValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; and WritableListValue\u0026lt;E\u0026gt; # WritableObjectValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; adds the set() method, which, for String types is identical to setValue().\nWritabaleListValue\u0026lt;E\u0026gt; is an extension of WritableObjectValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; that adds no methods but does also implement ObseravbleList\u0026lt;E\u0026gt; (even though it has no actual implementations of its methods). So an object passed as WritebleListValue\u0026lt;E\u0026gt; can be treated as if it was an ObseravbleList\u0026lt;E\u0026gt;.\nProperty\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; # The Property\u0026lt;ObseravbleList\u0026lt;E\u0026gt;\u0026gt; interface extends WritableObjectValue\u0026lt;ObseravbleList\u0026lt;E\u0026gt;\u0026gt; and adds the ability to bind to other observables. We get bind() and bindBidirectional(), and their corresponding unbinding methods. Also there is the isBound() method that will tell us if the Property has been bound to something.\nNotice, though, that it does NOT implement or extend WritebleListValue\u0026lt;E\u0026gt; so it cannot be treated like an ObseravbleList\u0026lt;E\u0026gt;.\nBinding\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; # Binding\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; is the last interface on the chart. It extendsObservableValue\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; adding a method to force the Binding to invalidate, a method to check if the Binding is valid and a method to get the dependencies of the Binding.\nThe Classes # Everything else on the chart is a class, let\u0026rsquo;s take a look at them.\nListExpression\u0026lt;E\u0026gt; # This is the root class for all of the Property\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; classes, even though the diagram goes diagonally down to ObjectProperty\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;. Note that ListExpression\u0026lt;E\u0026gt; is a drop-in replacement for ObjectExpression and does not extend or implement ObjectExpression\nThis is the only class in the chart that introduces new concrete methods that don\u0026rsquo;t implement methods already defined by any of the interfaces in the chart.\nNote ListExpression is where the implementation of the methods for ObservableList happens.\nThis class provides the core functionality for the \u0026ldquo;Fluent API\u0026rdquo; for creating List bindings. In truth, this isn\u0026rsquo;t much. There\u0026rsquo;s the expected isNull() and isNotNull(), along with isEqualTo() and isNotEqualTo(). There are also two versions of valueAt() which returns an ObjectBinding\u0026lt;E\u0026gt;, one of these takes as static index value, the other an ObservableIntegerValue.\nBeyond that there are emptyProperty() and sizeProperty(). We saw sizeProperty() used in our test example above. This alone could be a reason to use a ListProperty.\nBut more importantly, ListExpression\u0026lt;E\u0026gt; is an abstract class that implements all of the List and ObservableList methods, essentially turning ListExpression into an implementation of ObservableList.\nReadOnlyListProperty\u0026lt;E\u0026gt; # This class extends ListExpression and implements ReadOnlyProperty. This class can also be thought of as very much like ReadOnlyProperty but with the ability to use the Fluent API with it to create bindings.\nListProperty\u0026lt;E\u0026gt; # The best way to think about ListProperty\u0026lt;E\u0026gt; is an implementation of Property\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt; but, since it inherits from ListExpression\u0026lt;E\u0026gt; through ReadOnlyListProperty\u0026lt;E\u0026gt;, it also has the methods for creating bindings via the Fluent API, and the methods to implement ObservableList\u0026lt;E\u0026gt;. However, it only has implementations for bidirectional binding and setting the value.\nThis is, however, a great class to declare your instantiated Properties as. Like this:\nval someProperty : ListProperty\u0026lt;String\u0026gt;= SimpleListProperty(FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;)) ListPropertyBase\u0026lt;E\u0026gt; # This class adds almost all the remaining methods defined in the various interfaces up hierarchy. We get methods to bind and unbind, add and remove Listeners and a get() method.\nThe methods that are missing are the two silly Java Bean related methods. This is the class that you probably want to extend from if you want to create your own Property classes, especially if you want to ignore Java Bean stuff as much you can.\nSimpleListProperty\u0026lt;E\u0026gt; # This is the only class in the chart that you can directly instantiate, so everybody is familiar with it. It adds the getBean() and getName() methods and the infrastructure and constructors to set their values.\nReadOnlyListPropertyBase\u0026lt;E\u0026gt; and ReadOnlyListWrapper\u0026lt;E\u0026gt; # ReadOnlyListPropertyBase\u0026lt;E\u0026gt; is very similar to ListPropertyBase\u0026lt;E\u0026gt; in that it is an abstract class that has almost all of the interface methods implemented except for the \u0026ldquo;Bean\u0026rdquo; methods. It\u0026rsquo;s also missing the get() method.\nThe only \u0026ldquo;only out of the box\u0026rdquo; implementation of ReadOnlyListPropertyBase\u0026lt;E\u0026gt; is contained in ReadOnlyListWrapper\u0026lt;E\u0026gt;. This is a fairly involved topic that was covered in depth here in Part I.\nListBinding\u0026lt;E\u0026gt; # This is the sole class on the Binding side of the chart, and it\u0026rsquo;s abstract and it extends ListExpression\u0026lt;E\u0026gt; and implements Binding\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;. Any utility (including the Fluent API) that creates List Bindings will do so by extending from this class.\nIt doesn\u0026rsquo;t add any new public methods but there are a few protected methods that are the framework for custom Binding classes. We get bind() and unbind() to start with. We also get allowValidation(), onInvalidating() and isObserved(). There is one abstract method, and that\u0026rsquo;s computeValue() which is also protected.\nThe intention is clear about the standard use case for custom classes extended from ListBinding\u0026lt;E\u0026gt;. It\u0026rsquo;s that the Observables to be bound are defined when the class is instantiated and that you\u0026rsquo;ll also define a computeValue() method that will use those bound values to determine the return value of any calls to get() or getValue().\nTip Note that computeValue() is only called when a call is made to get() or getValue() when the Binding has been invalidated.\nSince ListBinding extends from ListExpression you can use the Fluent API with it, and you can treat it just as though it was an ObservableList.\nUsing this Information # At some point during this article, you should have had an, \u0026ldquo;Oh! Wow!\u0026rdquo; moment. I know I did when I was writing it.\nIf you didn\u0026rsquo;t, then consider this: ListProperty\u0026lt;E\u0026gt; can be used any place that you can use ObservableList\u0026lt;E\u0026gt;.\nAny place.\nLet\u0026rsquo;s look at the ramifications of this\u0026hellip;\nIn your Presentation Model # For years I\u0026rsquo;ve been saying that if you are using the \u0026ldquo;set\u0026rdquo; methods for Nodes than have values, then you\u0026rsquo;re doing it wrong. You shouldn\u0026rsquo;t be calling setText() or setValue() or setSelected(). Use should be using textProperty().bind(), valueProperty().bind() and selectedProperty().bind().\nYet, I\u0026rsquo;ll still call ListView.setItems() or TableView.setItems(). It never even occurred to me that there was another way. But if I declare my ObservableLists as Properties in my Model, then I can do the same binding operation:\nclass Model { val someStuff : ListProperty\u0026lt;String\u0026gt; = SimpleListProperty\u0026lt;String\u0026gt;(FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;)) } and then I can do this:\nlistView.itemsProperty().bind(model.someStuff) But here\u0026rsquo;s the cool part, I can ignore the Property nature of model.someStuff if I want. Like this:\nmodel.someStuff.add(\u0026#34;hjk\u0026#34;) listView.setItems(model.someStuff) And it will still work.\nBut what\u0026rsquo;s the big deal? What\u0026rsquo;s the difference?\nIf I declare model.someStuff as an ObservableList\u0026lt;String\u0026gt;, then I have to use listView.setItems(model.someStuff). And then, if I want to change the list in the business logic, I have to clear out model.someStuff and replace its contents with new items. But, if I declare it as a ListProperty\u0026lt;String\u0026gt;, I not only have the ObservableList methods, but I also have Property methods. So now I can do this:\nmodel.someStuff.set(someOtherObseravbleList) and the ListView will automatically change.\nBut wait!! There\u0026rsquo;s more!\nThis will still work, even if I used listView.setItems(model.someStuff)! Wrap your brain around that.\nAs a Binding # Let\u0026rsquo;s look at this some more. But now we\u0026rsquo;ll use a ListBinding. Here\u0026rsquo;s something close to the examples we started out with:\nclass Example1 : Application() { @Throws(Exception::class) override fun start(primaryStage: Stage) { val mainScene = Scene(createContent(), 600.0, 500.0) primaryStage.scene = mainScene primaryStage.show() } private fun createContent(): Region = VBox(20.0).apply { val list1 = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;ghi\u0026#34;, \u0026#34;123\u0026#34;) val list2 = FXCollections.observableArrayList(\u0026#34;jkl\u0026#34;, \u0026#34;mno\u0026#34;) val listProperty1 = SimpleListProperty(list1) val listProperty2 = SimpleListProperty(list2) val listBinding = object : ListBinding\u0026lt;String\u0026gt;() { init { super.bind(listProperty1, listProperty2) } override fun computeValue(): ObservableList\u0026lt;String\u0026gt; = if (listProperty1.size \u0026gt;= listProperty2.size) listProperty1 else listProperty2 } children += ListView\u0026lt;String\u0026gt;().apply { itemsProperty().bind(listBinding) } children += Button(\u0026#34;Add Element to list2\u0026#34;).apply { setOnAction { listProperty2.add(\u0026#34;xyz\u0026#34;) } } padding = Insets(30.0) } } fun main() = Application.launch(Example1::class.java) Now, we\u0026rsquo;ve added a ListView with its itemsProperty() bound to a ListBinding. The Binding is dependent on two ListProperties and will return whichever one is the biggest. At the beginning, list1 has 4 elements and list2 has 2 elements. The Button now adds an element to list2 with each click. Here\u0026rsquo;s how it starts out:\nAnd here\u0026rsquo;s what it looks like after 3 clicks:\nAnd it works exactly the same if we use setItems(listBinding) instead of itemsProperty().bind(listBinding).\nBut Wait!!! There\u0026rsquo;s more!\nRemember that ObservableList implements Observable. And what does Observable define? Invalidation!! And, of course, invalidation is the core element of binding. Which means that we can do this:\nclass Example1 : Application() { @Throws(Exception::class) override fun start(primaryStage: Stage) { val mainScene = Scene(createContent(), 600.0, 500.0) primaryStage.scene = mainScene primaryStage.show() } private fun createContent(): Region = VBox(20.0).apply { val list1 = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;ghi\u0026#34;, \u0026#34;123\u0026#34;) val list2 = FXCollections.observableArrayList(\u0026#34;jkl\u0026#34;, \u0026#34;mno\u0026#34;) val listBinding = object : ListBinding\u0026lt;String\u0026gt;() { init { super.bind(list1, list2) } override fun computeValue(): ObservableList\u0026lt;String\u0026gt; = if (list1.size \u0026gt;= list2.size) list1 else list2 } children += ListView\u0026lt;String\u0026gt;().apply { itemsProperty().bind(listBinding) } children += Button(\u0026#34;Add Element to list2\u0026#34;).apply { setOnAction { list2.add(\u0026#34;xyz\u0026#34;) } } padding = Insets(30.0) } } fun main() = Application.launch(Example1::class.java) Which will work exactly the same as the previous example. What happens is that each time we add an element to list2 it invalidates. This causes listBinding to invalidate, and then computeValue() will eventually get called, revalidating both listBinding and list2.\nLinked ComboBoxes # One last example of this, because it does come up quite often\u0026hellip;\nWhat if you have two ComboBoxes and the selection list in the second one depends on the selection in the first? Here\u0026rsquo;s how I\u0026rsquo;d do this now\u0026hellip;\nclass Example2 : Application() { @Throws(Exception::class) override fun start(primaryStage: Stage) { val mainScene = Scene(createContent(), 320.0, 300.0) primaryStage.scene = mainScene primaryStage.show() } private fun createContent(): Region = HBox(20.0).apply { val birds = FXCollections.observableArrayList(\u0026#34;Robin\u0026#34;, \u0026#34;Starling\u0026#34;, \u0026#34;Woodpecker\u0026#34;, \u0026#34;Blue Jay\u0026#34;) val fish = FXCollections.observableArrayList(\u0026#34;Goldfish\u0026#34;, \u0026#34;Carp\u0026#34;, \u0026#34;Bass\u0026#34;, \u0026#34;Perch\u0026#34;) val rodents = FXCollections.observableArrayList(\u0026#34;Rat\u0026#34;, \u0026#34;Mouse\u0026#34;, \u0026#34;Capybara\u0026#34;, \u0026#34;Chipmunk\u0026#34;) val selectionList = FXCollections.observableArrayList(\u0026#34;Birds\u0026#34;, \u0026#34;Fish\u0026#34;, \u0026#34;Rodents\u0026#34;) val selectedType = SimpleStringProperty(\u0026#34;\u0026#34;) val listBinding = object : ListBinding\u0026lt;String\u0026gt;() { init { super.bind(selectedType) } override fun computeValue(): ObservableList\u0026lt;String\u0026gt;? = when (selectedType.value) { \u0026#34;Birds\u0026#34; -\u0026gt; birds \u0026#34;Fish\u0026#34; -\u0026gt; fish \u0026#34;Rodents\u0026#34; -\u0026gt; rodents else -\u0026gt; null } } children += ComboBox\u0026lt;String\u0026gt;().apply { items = selectionList selectedType.bindBidirectional(valueProperty()) } children += ComboBox\u0026lt;String\u0026gt;().apply { itemsProperty().bind(listBinding) } padding = Insets(30.0) } } fun main() = Application.launch(Example2::class.java) We have three lists; birds, fish and rodents which might be loaded into the items of the second ComboBox. The first ComboBox basically has a list of the lists, and its valueProperty() is bound to selectedType. There is a ListBinding which is dependent only on selectedType and its computeValue() method will return one of the three other lists based on the value in selectedType. The Kotlin when() statement is equivalent to a variant of the Java switch statement.\nUnfortunately, I can\u0026rsquo;t get screenshots with ComboBoxes open, but the screen starts looking like this:\nWhere you can see that the second ComboBox is very small, as it has an empty list of items. And then after picking \u0026ldquo;Rodents\u0026rdquo;:\nWhere it\u0026rsquo;s now bigger because it has items in its list.\nHere\u0026rsquo;s the thing. You can search the Web and StackOverflow.com and you\u0026rsquo;ll find variations on this situation over and over. But I don\u0026rsquo;t think you\u0026rsquo;re going to find anything like this approach, no matter how hard you look. Because nobody seems to know about it.\nWhy not?\nI had a look through the JavaDocs for the Bindings class. There are exactly zero methods in that class that return a ListBinding. The only way to do this is to write a custom ListBinding as I did.\nNow, you could change listBinding so that it was ObjectBinding\u0026lt;ObservableList\u0026lt;String\u0026gt;\u0026gt; and the rest of the code in this example will work perfectly. That would let you use Bindings or the Fluent API and might be something that someone would stumble across. However, if you did that, it will NOT work with ComboBox.setItems(listBinding).\nThe fact that ComboBox.items is an ObjectProperty\u0026lt;ObservableList\u0026lt;E\u0026gt;\u0026gt;, isn\u0026rsquo;t particularly hidden, but the idea of ObservableLists wrapped in Properties pushes it into a different level of functionality.\nConclusion # I think that there are two really important take-aways from this article:\nListProperty can be used anywhere ObservableList can be used.\nThere\u0026rsquo;s problably some very minor memory and performance hit to using ListProperty, as you need storage for things like sizeProperty() and all of the methods are delegated to the enclosed ObseravbleList. But I doubt that any ordinary application is going to have any noticable impact. On the upside, you do get a lot of versatility that you didn\u0026rsquo;t have before.\nAs you can see from the ComboBox example, using ListBinding or ListProperty allows you to do cool stuff while at the same time not creating dependencies in your client code. With ListBinding the ComboBox example worked whether the client ComboBox was aware it was getting an observable wrapper and using itemProperty().bind(), or if it wasn\u0026rsquo;t and just used setItems(). This is a strong case for just implementing all of your ObseravbleLists as ListProperties as a standard practice.\nObservableList can be treated just like Observable.\nThis is something that seems to get missed all the time. When you think about using an ObseravbleList you immediately think about ListChangeListeners and then dealing with the resulting ListChangeListener.Change objects that it passes. But if you treat it as Obseravble then you can create a Binding with it as a dependancy. If nothing else, this makes it trivial to create Bindings that return the maximum, minimum or average value of an ObservableList of numbers - something that appears incredibly hard otherwise.\nWhen I started this series, I figured this Part III would be something I wrote just for the sake of completeness. But when I dug into the subject I discovered that this is one of those \u0026ldquo;hidden features\u0026rdquo; of JavaFX that everyone should know about and understand, but few people seem to - like TextFormatter.\nIs it better/cooler/more useful that TextFormatter???\nI think it just might be.\n","date":"28 August 2024","externalUrl":null,"permalink":"/javafx/elements/observables/properties/observable-classes/observable-classes-lists/","section":"JavaFX","summary":"You can work with a JavaFX for a long time before you realize that ListProperty is even a thing. And when you do, you’ll probably be extremely confused by it.\nThat’s because ListProperty (and all of the other types that descend from ObservableValue\u003cList\u003e) implements all of the methods of ObservableList as well as everything implied by ObservableValue and its descendants. This is a little mind boggling, because ObservableList\u003cT\u003e is generic itself, and implements everything in List\u003cT\u003e, and this means that ListProperty\u003cT\u003e does everything that Property\u003cT\u003e does, as well as everything that ObservableList\u003cT\u003e and, therefore, List\u003cT\u003e does.\nBut don’t worry, read this and it will all make sense - and actually be really useful.\n","title":"Guide To the Observable Classes - Part III","type":"javafx"},{"content":" Introduction # I was looking at a question on StackOverflow.com about JavaFX where the OP was doing some complicated things including manual styling in his code. He had four states that he wanted to style: \u0026ldquo;active\u0026rdquo;, \u0026ldquo;inactive\u0026rdquo;, \u0026ldquo;highlighted\u0026rdquo; and \u0026ldquo;error\u0026rdquo;. I was thinking, as I looked at the code, that it must be easier to do this using stylesheets and PseudoClasses.\nBut PseudoClasses classes only work in an \u0026ldquo;on/off\u0026rdquo; fashion. And the OP for this question had a set of Boolean flags with big blocks of code that looked at the values of the flags, turning them on and off and applying different stylings according to the value. All of which, of course, was in his layout code.\nIt occurred to me that any time you\u0026rsquo;ve got a set of values where only one can be true, you could probably handle it better with a Enum of some sort. And I wondered how you would integrate that with the JavaFX PseudoClass system to handle dynamic styling based on a set of mutually exclusive values.\nThat\u0026rsquo;s what we\u0026rsquo;re going to look at here.\nPseudoClasses # What is a \u0026ldquo;PseudoClass\u0026rdquo;?\nA PseudoClass is a way to provide temporary styling on a Node (usually) connected to a BooleanProperty in the layout code. For instance, a Button can have a \u0026ldquo;disabled\u0026rdquo; status which is controlled through Button.disabledProperty(). When you look through the CSS stylesheet, you\u0026rsquo;ll find something equivalent to .button: disabled {}. Whatever is set inside the {} is applied to the Button only when the disabled PseudoClass is applied to it.\nThere are a number of pre-defined PseudoClasses in JavaFX that are attached to various Properties in Node and its subclasses. Besides disabled, there\u0026rsquo;s armed, selected, focused and default, just to name a few. You can even combine them together into something like .button :armed :focused. Not all of theses PseudoClasses are available for every Node subclass.\nWithout customizing the code for any of these standard PseudoClasses, you can implement your own styling for them just by creating a stylesheet with an entry for selector: pseudoclass (using real values for selector and pseudoclass).\nCustom PseudoClasses # You can also create your own PseudoClasses, and the mechanism to connect JavaFX Nodes to your custom PseudoClass is simple, although badly explained in the JavaDocs.\nThe process is easy to describe: Create an instance of PseudoClass with an associated CSS identifier (like \u0026ldquo;selected\u0026rdquo;), then call Node.pseudoClassStateChanged() for whatever Node you want, specifying your PseudoClass instance and with a true or false.\nThat\u0026rsquo;s all there is to it, and this code will do it:\nval myPseudoClass = PseudoClass.getPseudoClass(\u0026#34;flashing\u0026#34;) myLabel.pseudoClassStateChanged(myPseudoClass, true) Generally speaking, though, you wouldn\u0026rsquo;t do it this way. First, myPseudoClass should probably by static, or at least a singleton - in Kotlin we\u0026rsquo;d put it inside a companion object. In Java you\u0026rsquo;d make it static final.\nSecondly, you\u0026rsquo;ll normally associate the control of your PseudoClass with a BooleanProperty and then put a Subscription or a Listener on it to call Node.pseudoClassStateChanged(). This would look like this:\ncompanion object { val myPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;flashing\u0026#34;) } . . . model.someBooleanProperty.subscribe{ newVal -\u0026gt; myLabel.pseudoClassStateChanged(myPseudoClass, newVal) } From the JavaDocs # The JavaDocs are a bit bewildering because of the sample code included in the introduction. We should probably look at this code just to understand how it works:\npublic boolean isMagic() { return magic.get(); } public BooleanProperty magicProperty() { return magic; } public BooleanProperty magic = new BooleanPropertyBase(false) { @Override protected void invalidated() { pseudoClassStateChanged(MAGIC_PSEUDO_CLASS, get()); } @Override public Object getBean() { return MyControl.this; } @Override public String getName() { return \u0026#34;magic\u0026#34;; } } private static final PseudoClass MAGIC_PSEUDO_CLASS = PseudoClass.getPseudoClass(\u0026#34;xyzzy\u0026#34;); The first thing, that\u0026rsquo;s not explained at all, is that this code needs to exist inside the MyControl class. This means that this implementation is intended to be used inside a custom class that is a subclass of Node (because it calls pseudoClassStateChanged()).\nThe other slightly confusing (and the most important) thing is that they\u0026rsquo;ve overriden BooleanProperty.invalidated(). Essentially, this is the same as adding an InvalidationListener to the Property. It\u0026rsquo;s not a bad approach, and it\u0026rsquo;s something that I\u0026rsquo;ve been calling an Action Property since it\u0026rsquo;s now a Property that also does something else - in this case trigger a PseudoClass state change.\nThe problem with this approach is that it requires extending some Node subclass to create a custom class, and then you\u0026rsquo;re burdened with that custom class that you have to keep track of.\nIt\u0026rsquo;s not necessary and, as we saw above, it can be done with two lines of code using an external BooleanProperty.\nMultiple Exclusive States # What if you want to establish a styling state for a Node that doesn\u0026rsquo;t just have two values? By this I mean, instead of just on and off, you have state1, state2, state3 and so on.\nPseudoClasses on work with on and off. But you can establish a number of PseudoClasses, each of which can either be on or off and then combine them together somehow such that if one of them turns on, then the others all turn off.\nThe obvious structure for a data element that can have one, and only one, of a discrete number of values is Enum. How can we turn an Enum into a mechanism to control a set of PseudoClasses that work together as described above?\nWe\u0026rsquo;re going to look at this in both Kotlin and Java, since they are significantly different.\nKotlin # In Kotlin, an Enum can implement an Interface. We\u0026rsquo;ll start there:\ninterface PseudoClassSupplier { fun getPseudoClass(): PseudoClass } Just one function: getPseudoClass().\nHere\u0026rsquo;s why we want to do this:\nfun Node.bindPseudoClassEnum(property: ObservableObjectValue\u0026lt;out PseudoClassSupplier\u0026gt;) { property.subscribe { oldValue, newValue -\u0026gt; oldValue?.let { pseudoClassStateChanged(it.getPseudoClass(), false) } newValue?.let { pseudoClassStateChanged(it.getPseudoClass(), true) } } property.value?.getPseudoClass()?.let { pseudoClassStateChanged(it, true) } } This is an extension function on Node, which means it will work for just about anything that you might possibly want to style via a PseudoClass. We pass it an ObservableObjectValue that contains something that can supply PseudoClassSupplier. This method adds a Subscription that supplies both the old and the new values of the ObservableObjectValue. When the Subscription is triggered, we set the state of the old PseudoClass to off for this Node, and set it to on for the new value. This is done with null safety checking since we have no control over whether or not the ObservableObjectValue is empty (or was empty).\nFinally, we use the initial value in the ObservableObjectValue to turn the state to on for this Node, if it\u0026rsquo;s populated.\nHow do we use this then?\nHere\u0026rsquo;s an example of an Enum that implements PseudoClassSupplier and can be used in our ObservableObjectValue\u0026lt;out PseudoClassSupplier\u0026gt;:\nenum class StatusPseudoClass : PseudoClassSupplier { NORMAL, WARNING, ERROR, FAILED; companion object PseudoClasses { val normalPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;normal\u0026#34;) val warningPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;warning\u0026#34;) val errorPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;error\u0026#34;) val failedPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;failed\u0026#34;) } override fun getPseudoClass() = when (this) { NORMAL -\u0026gt; normalPseudoClass WARNING -\u0026gt; warningPseudoClass ERROR -\u0026gt; errorPseudoClass FAILED -\u0026gt; failedPseudoClass } } The tricky thing here is that we want the instantiations of the PseudoClasses to be effectively static, so we need to use a companion object to contain them. However, we cannot reference the companion object in the constructors of the Enum elements (I tried), so we need to handle it in the body of getPseudoClass(). Hence the when (this) {} structure.\nThe result is that the specifics about any particular implementation of this approach are contained neatly into a single Enum class. Once it has been established, it\u0026rsquo;s easy to replicate over and over again for any Enum you can think of.\nLet\u0026rsquo;s look at how to implement this:\nclass EnumPseudoClassApp : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 380.0, 200.0).apply { EnumPseudoClassApp::class.java.getResource(\u0026#34;test.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.title = \u0026#34;PseudoClasses Wow!\u0026#34; stage.show() } private fun createContent(): Region = VBox(10.0).apply { val status: ObjectProperty\u0026lt;StatusPseudoClass\u0026gt; = SimpleObjectProperty() children += Label(\u0026#34;This is a Label\u0026#34;).apply { styleClass += \u0026#34;status-label\u0026#34; bindPseudoClassEnum(status) } val toggleGroup = ToggleGroup() children += HBox(10.0).apply { children += ToggleButton(\u0026#34;Normal\u0026#34;).apply { setOnAction { status.value = StatusPseudoClass.NORMAL } toggleGroup.toggles += this } children += ToggleButton(\u0026#34;Warning\u0026#34;).apply { setOnAction { status.value = StatusPseudoClass.WARNING } toggleGroup.toggles += this } children += ToggleButton(\u0026#34;Error\u0026#34;).apply { setOnAction { status.value = StatusPseudoClass.ERROR } toggleGroup.toggles += this } children += ToggleButton(\u0026#34;Failed\u0026#34;).apply { setOnAction { status.value = StatusPseudoClass.FAILED } toggleGroup.toggles += this } } padding = Insets(50.0) } } fun main() = Application.launch(EnumPseudoClassApp::class.java) Here\u0026rsquo;s the style sheet:\n.root {} .status-label { -fx-font-size: 14px; -fx-font-weight: bold; -fx-text-fill: blue; } .status-label: normal { -fx-text-fill: black; } .status-label: warning { -fx-text-fill: orange; } .status-label: error { -fx-text-fill: red; } .status-label: failed { -fx-text-fill: green; } And it looks like this:\nI put the actions into ToggleButtons in a ToggleGroup just make it clear when one has been selected. The external Property would probably be in a Model somewhere, but here we just have the local variable, status. The PseudoClasses are applied to a Label via Label.bindPseudoClassEnum(status). The actual changes to the Property are handled by the ToggleButton actions.\nPerhaps the most important thing here is that the PseudoClass handling code isn\u0026rsquo;t in the layout code. It\u0026rsquo;s basically \u0026ldquo;plumbing\u0026rdquo; and isn\u0026rsquo;t particularly specific to this layout. I could see a case for putting this Enum class itself away in a utility package somewhere so that it can be used over and over. When you think about it, having the statuses \u0026ldquo;NORMAL\u0026rdquo;, \u0026ldquo;WARNING\u0026rdquo;, \u0026ldquo;ERROR\u0026rdquo; and \u0026ldquo;FAIL\u0026rdquo; (or some variation of this) is a pretty universal concept, and could be used over and over without modification.\nBut it still looks like a lot of code in the layout, mostly because of all of the Buttons. Let\u0026rsquo;s change it a little.\nprivate fun createContent(): Region = VBox(10.0).apply { val amount: ObjectProperty\u0026lt;Int\u0026gt; = SimpleObjectProperty(-1) children += Label().apply { styleClass += \u0026#34;status-label\u0026#34; textProperty().bind(amount.map { \u0026#34;This is a Label: $it\u0026#34; }) bindPseudoClassEnum(amount.map { when { it \u0026lt; 0 -\u0026gt; StatusPseudoClass.FAILED it == 0 -\u0026gt; StatusPseudoClass.NORMAL it \u0026lt; 3 -\u0026gt; StatusPseudoClass.WARNING else -\u0026gt; StatusPseudoClass.ERROR } }) } children += Button(\u0026#34;Add 1\u0026#34;).apply { setOnAction { amount.value += 1 } } padding = Insets(50.0) } Everything else remains the same.\nThe multiple Buttons are gone, and now we just have a single Button that increments an Integer Property. The PseudoClass is now connected to this Integer Property through a Binding that uses ObservableValue.map{} to convert it to a value of the StatusPseudoClass Enum. The map{} uses a variant of when which is the Kotlin equivalent to Java\u0026rsquo;s switch().\nThe value starts at -1 and as the Button is clicked it cyles from FAILED through all of the other values, and the colour of the Label changes.\nEven if you are going to be programming in Kotlin, the Java example is worth looking at because it uses a different approach that will also work in Kotlin.\nJava # Java also allows you to have an Enum implement an Interface, so we\u0026rsquo;ll start off from there for Java, too. First, the Interface:\npublic interface PseudoClassProvider { PseudoClass getPseudoClass(); } I changed the name here, just so it wouldn\u0026rsquo;t collide with the Kotlin Interface name in my project.\nAnd then we\u0026rsquo;ll create an Enum that implements this Interface:\npublic enum StatusEnum implements PseudoClassProvider { NORMAL { @Override public PseudoClass getPseudoClass() { return normal; } }, WARNING { @Override public PseudoClass getPseudoClass() { return warning; } }, ERROR { @Override public PseudoClass getPseudoClass() { return error; } }, FAILED { @Override public PseudoClass getPseudoClass() { return failed; } }; private static final PseudoClass normal = PseudoClass.getPseudoClass(\u0026#34;normal\u0026#34;); private static final PseudoClass warning = PseudoClass.getPseudoClass(\u0026#34;warning\u0026#34;); private static final PseudoClass error = PseudoClass.getPseudoClass(\u0026#34;error\u0026#34;); private static final PseudoClass failed = PseudoClass.getPseudoClass(\u0026#34;failed\u0026#34;); } This is a little bit different from the Kotlin version, which could have also been done this way. Here, instead of having a switch() statement to determine the PseudoClass, we\u0026rsquo;re overriding the Interface for each value of the Enum. Essentially, we\u0026rsquo;re going with inheritance here instead of branching, which is maybe a little bit more of a Java approach.\nNow, in Java we don\u0026rsquo;t have extension functions. You could create a static method in a library class, and pass it both the Node and the Property and have it create the Subscription, and that would be fine. In fact, if you were already building a library of static methods to provide builders and helper methods (and you should be), then it\u0026rsquo;s probably more than fine because it would fit in with that approach.\nBut for this example, I wanted something a little more self-contained. So what we have here is an \u0026ldquo;Action Property\u0026rdquo;:\npublic class EnumPseudoClassProperty\u0026lt;T extends PseudoClassProvider\u0026gt; extends ObjectPropertyBase\u0026lt;T\u0026gt; { private final Node node; private PseudoClassProvider oldValue; public EnumPseudoClassProperty(Node node) { this.node = node; } public EnumPseudoClassProperty(Node node, T initialValue) { this.node = node; this.set(initialValue); } @Override protected void invalidated() { if (oldValue != null) { node.pseudoClassStateChanged(oldValue.getPseudoClass(), false); } if (getValue() != null) { node.pseudoClassStateChanged(getValue().getPseudoClass(), true); } oldValue = getValue(); } @Override public Object getBean() { return node; } @Override public String getName() { return \u0026#34;\u0026#34;; } } This is a generic class that wraps some class, T, that extends PseudoClassProvider, meaning that it implements getPseudoClass(). So T can be any class that implements PseudoClassProvider.\nNext, we get to the \u0026ldquo;Action Property\u0026rdquo; part. You can read more about this in this article if you like. We are overriding the method, invalidated(), which JavaFX provides for us so that we can automatically make our Property take some action whenever its value changes. It\u0026rsquo;s equivalent to addListener() or subscribe(), except that it\u0026rsquo;s contained within the Property itself.\nThe only other quirk here is that we have to keep track of the old value ourselves in those cases where we care about the old value. And here, we do. It\u0026rsquo;s not a big deal, though, we just introduce a field and update it at the end of our invalidated() code.\nOf course, to make this work, we need to provide the Node associated with the PseudoClasses, so it\u0026rsquo;s passed in the constructor. Also, since this extends from ObjectPropertyBase, we need to provide implementations for getName() and getBean(). Once again, if you want to know more about Action Properties (my name for them), then you should read the other article.\nAll that\u0026rsquo;s left is to implement it. Here\u0026rsquo;s the Application:\npublic class EnumPseudoApplication extends Application { @Override public void start(Stage stage) throws Exception { Scene scene = new Scene(createContent(), 300, 240); scene.getStylesheets().add(EnumPseudoApplication.class.getResource(\u0026#34;test.css\u0026#34;).toExternalForm()); stage.setScene(scene); stage.show(); } private Region createContent() { VBox results = new VBox(20); Label label = new Label(\u0026#34;This is the label\u0026#34;); label.getStyleClass().add(\u0026#34;status-label\u0026#34;); EnumPseudoClassProperty\u0026lt;StatusEnum\u0026gt; status = new EnumPseudoClassProperty\u0026lt;\u0026gt;(label, StatusEnum.FAILED); results.getChildren().addAll(label, createButton(\u0026#34;Failed\u0026#34;, status, StatusEnum.FAILED), createButton(\u0026#34;Normal\u0026#34;, status, StatusEnum.NORMAL), createButton(\u0026#34;Warning\u0026#34;, status, StatusEnum.WARNING), createButton(\u0026#34;Error\u0026#34;, status, StatusEnum.ERROR)); return results; } private Region createButton(String text, EnumPseudoClassProperty\u0026lt;StatusEnum\u0026gt; property, StatusEnum newValue) { Button button = new Button(text); button.setOnAction(evt -\u0026gt; property.set(newValue)); return button; } } Here we instantiate the Property as a variable so that we can pass it around to the Button builder method so that it can be updated in the onAction EventHandler.\nThe style sheet is the same one from the Kotlin example.\nYou Don\u0026rsquo;t Have to Use Enum # Enum is a natural fit for this kind of a use because it works much like ToggleButtons in a ToggleGroup. Only one can be selected at a time, and selecting any one value automatically turns any other ones off. Enum gives the framework, and all we need to do is provide the mechanics for turning the selected and de-selected values on and off.\nBut there\u0026rsquo;s no rule that we have to use an Enum. Any class that implements the PseudoClassProvider Interface, or that can be extended to implement that Interface will work. In Kotlin, you could probably use a Sealed Class very effectively for this.\nBut you don\u0026rsquo;t have to stick to this kind of approach. You could have a custom class that implements PseudoClassProvider but stores an immutable reference to a PseudoClass instance in a field. Then put different instances of this class into a Property.\nClasses That Can\u0026rsquo;t Implement PseudoClassSupplier # It would be really cool to be able to extend Int in Kotlin (or Integer in Java) to implement PseudoClassSupplier. Then you could define Int.getPseudoClass() to return a PseudoClass based on a value or range of values for Int.\nBut you can\u0026rsquo;t. So what do you do?\nYou can use an Action Property to contain everything:\nclass IntRangePseudoProperty(private val node:Node) : ObjectPropertyBase\u0026lt;Int\u0026gt;() { private var oldValue : Int? = null init { invalidated() } override fun invalidated() { oldValue?.let{node.pseudoClassStateChanged(getPseudoClassFromValue(it), false)} value?.let{node.pseudoClassStateChanged(getPseudoClassFromValue(it), true)} oldValue = value } private fun getPseudoClassFromValue(theValue: Int): PseudoClass = when { theValue \u0026lt; 0 -\u0026gt; failedPseudoClass theValue == 0 -\u0026gt; normalPseudoClass theValue \u0026lt; 3 -\u0026gt; warningPseudoClass else -\u0026gt; errorPseudoClass } companion object PseudoClasses { val normalPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;normal\u0026#34;) val warningPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;warning\u0026#34;) val errorPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;error\u0026#34;) val failedPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;failed\u0026#34;) } override fun getBean(): Any = node override fun getName(): String = \u0026#34;Integer Range\u0026#34; } Once again, we extend from ObjectPropertyBase and provide getBean() and getName() implementations that do reasonable things, even though it\u0026rsquo;s highly unlikely these will ever be called.\nWe override invalidated() and put the PseudoClass state change logic in there. To avoid repetition, the actual range calculation has been pulled out into its own function.\nBut what if you wanted to abstract it a little bit? Splitting out that getPseudoClassFromValue() logic into its own method suggests that a functional element could be used. What if we did this:\nclass PseudoProperty\u0026lt;T\u0026gt;(private val node: Node, private val translator: (T) -\u0026gt; PseudoClass) : ObjectPropertyBase\u0026lt;T\u0026gt;() { private var oldValue: T? = null init { invalidated() } override fun invalidated() { oldValue?.let { node.pseudoClassStateChanged(translator(it), false) } value?.let { node.pseudoClassStateChanged(translator(it), true) } oldValue = value } override fun getBean(): Any = node override fun getName(): String = \u0026#34;PseudoClass Property\u0026#34; } Now we have a generic Action Property\u0026lt;T\u0026gt; that will use a Function (that\u0026rsquo;s what (T) -\u0026gt; PseudoClass is) that will take an T and return a PseudoClass. That Function is given in its constructor.\nHowever, since we have static PseudoClasses, we can\u0026rsquo;t provide an Anonymous InnerClass via a lambda expression, because we cannot declare static (or companion objects) inside those. So we\u0026rsquo;ll have to create a class for it:\nclass IntRangeTranslator : (Int) -\u0026gt; PseudoClass { override fun invoke(theValue: Int): PseudoClass = when { theValue \u0026lt; 0 -\u0026gt; failedPseudoClass theValue == 0 -\u0026gt; normalPseudoClass theValue \u0026lt; 3 -\u0026gt; warningPseudoClass else -\u0026gt; errorPseudoClass } companion object PseudoClasses { val normalPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;normal\u0026#34;) val warningPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;warning\u0026#34;) val errorPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;error\u0026#34;) val failedPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;failed\u0026#34;) } } That\u0026rsquo;s nifty. Now we have a Property\u0026lt;T\u0026gt; that we can stick into a libary somewhere so we don\u0026rsquo;t have to worry about it any more, then we can put the code that\u0026rsquo;s specific to our use case (the translator), adjacent to our layout code.\nclass EnumPseudoClassApp : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 380.0, 200.0).apply { EnumPseudoClassApp::class.java.getResource(\u0026#34;test.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.title = \u0026#34;PseudoClasses Wow!\u0026#34; stage.show() } private fun createContent(): Region = VBox(10.0).apply { val amount: ObjectProperty\u0026lt;Int\u0026gt; = SimpleObjectProperty(-1) children += Label().apply { styleClass += \u0026#34;status-label\u0026#34; textProperty().bind(amount.map { \u0026#34;This is a Label: $it\u0026#34; }) PseudoProperty(this, IntRangeTranslator()).bind(amount) } children += Button(\u0026#34;Add 1\u0026#34;).apply { setOnAction { amount.value += 1 } } padding = Insets(50.0) } } I have two issues with this approach:\nWe have this \u0026ldquo;hanging\u0026rdquo; instantiation of PseudoProperty without any references to it or uses of it anywhere. So it feels more like it should be a method invocation rather than an instantiation.\nWe need to hang on to amount and then bind it to our anonymous PseudoProperty inside the Label().apply{} block. This is because we need a reference to the Label in order to instantiate the PseudoProperty so we cannot instantiate it outside the apply{} block unless we make a variable refernce to the Label so that we can pass it to the constructor of PseudoProperty. And all of that feels wrong to me.\nWhat if we remove the requirement to pass the Node in the constructor of PseudoProperty?\nclass SimplePseudoProperty\u0026lt;T\u0026gt;(initialValue: T) : ObjectPropertyBase\u0026lt;T\u0026gt;() { private var translator: ((T) -\u0026gt; PseudoClass)? = null private var oldValue: T? = null private var node: Node? = null infix fun setNode(newNode: Node)= apply { node = newNode } infix fun setTranslator(newTranslator: ((T) -\u0026gt; PseudoClass)?) = apply { translator = newTranslator } init { value = initialValue invalidated() } override fun invalidated() { println(\u0026#34;Invalidated $value\u0026#34;) translator?.let { translator -\u0026gt; oldValue?.let { node?.pseudoClassStateChanged(translator(it), false) } value?.let { node?.pseudoClassStateChanged(translator(it), true) } } oldValue = value } override fun getBean(): Any = node ?: Unit override fun getName(): String = \u0026#34;PseudoClass Property\u0026#34; } Additionally, we\u0026rsquo;ve also gotten rid of the reequirement to supply the translator in the constructor. Now both the Node and the translator can be supplied via decorators. If you were writing this as a library utility class, you\u0026rsquo;d probably want to make node and translator effectively immutable, or supply logic to handle changes to these fields in a way that makes sense to you.\nNow, we can transform amount into our PseudoProperty instead:\nclass EnumPseudoClassApp : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 380.0, 200.0).apply { EnumPseudoClassApp::class.java.getResource(\u0026#34;test.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.title = \u0026#34;PseudoClasses Wow!\u0026#34; stage.show() } private fun createContent(): Region = VBox(10.0).apply { val amount = PseudoProperty(-1) amount.setTranslator(IntRangeTranslator()) children += Label().apply { styleClass += \u0026#34;status-label\u0026#34; textProperty().bind(amount.map { \u0026#34;This is a Label: $it\u0026#34; }) amount.setNode(this) } children += Button(\u0026#34;Add 1\u0026#34;).apply { setOnAction { amount.value += 1 } } padding = Insets(50.0) } } Finally, if we want to get rid of IntRangeTranslator and use a lambda instead, we\u0026rsquo;ll need to find a home for the static PseudoClasses. But if we\u0026rsquo;re using a lambda, then we are pretty much into creating something specific for this layout, so there\u0026rsquo;s no problem putting the PseudoClasses close to the layout, like this:\nclass EnumPseudoClassApp : Application() { private val amount = SimplePseudoProperty(-1) setTranslator { theValue -\u0026gt; when { theValue \u0026lt; 0 -\u0026gt; failedPseudoClass theValue == 0 -\u0026gt; normalPseudoClass theValue \u0026lt; 3 -\u0026gt; warningPseudoClass else -\u0026gt; errorPseudoClass } } companion object PseudoClasses { val normalPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;normal\u0026#34;) val warningPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;warning\u0026#34;) val errorPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;error\u0026#34;) val failedPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;failed\u0026#34;) } override fun start(stage: Stage) { stage.scene = Scene(createContent(), 380.0, 200.0).apply { EnumPseudoClassApp::class.java.getResource(\u0026#34;test.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.title = \u0026#34;PseudoClasses Wow!\u0026#34; stage.show() } private fun createContent(): Region = VBox(10.0).apply { children += Label().apply { styleClass += \u0026#34;status-label\u0026#34; textProperty().bind(amount.map { \u0026#34;This is a Label: $it\u0026#34; }) amount.setNode(this) } children += Button(\u0026#34;Add 1\u0026#34;).apply { setOnAction { amount.value += 1 } } padding = Insets(50.0) } } What I like about this approach is that the things which are specific to this layout; the tranlation and the actual PseudoClasses are close to, but not in the layout code. But the things that are generic, the plumbing, is somewhere else.\nBut\u0026hellip;\nUsing This in a Framework # Here\u0026rsquo;s the question\u0026hellip;Is the translation from number to a classification really part of the View?\nNot the translation to PseudoClasses, that\u0026rsquo;s definitely the role of the View, but the definition of the ranges - \u0026lt;0, 0,1-2 and 3+ - are those ranges purely for the View? Or are they an expression of some business/application rule?\nBecause, if the answer is that the range determination is business/application logic, then that code belongs in the Interactor (or the Model, if you\u0026rsquo;re using MVC or MVVM).\nLet\u0026rsquo;s look at the example of a custom Enum with the PseudoClasses baked in. Does the inclusion of the PseudoClasses make it a View-only element? Is it acceptable to have a Model field with a type of ObjectProperty\u0026lt;StatusPseudoClass\u0026gt; that the Interactor can see and access?\nPersonally, I think that, although there\u0026rsquo;s no way that the Interactor should ever be calling getPseudoClass(), any attempt to remove that method from the Enum and then replace its functionality with something visible only to the View just gets needlessly complicated. An understanding that the Interactor should simple treat StatusPseudoClass and a vanilla Enum should be sufficient. This is in much the same way that the Interactor should treat the ObservableValues in the Model simply as vanilla wrappers for data that it accesses via get() and set(), while ignoring their observability and doing things like adding Listeners to them.\nWhat about SimplePseudoProperty\u0026lt;T\u0026gt;? On the one hand, you can instantiate it as a field in the Model without breaking the framework, although you\u0026rsquo;ll have to expose it as SimplePseudoProperty\u0026lt;T\u0026gt;. The View can then provide the link to the Node, and possibly the translator if it\u0026rsquo;s truly just View only.\nOn the other hand, if the translator rules count as application/business logic, then you\u0026rsquo;ll have to put it in the Interactor. Technically, this is possible, because the Interactor can invoke SimplePseudoProperty.setTranslator(). However\u0026hellip;\nYou\u0026rsquo;re then in a framework pickle because there\u0026rsquo;s no way to define the translator without providing PseudoClasses as an output. And this means that your Interactor is going to have to directly deal with PseudoClasses, which is probably one step too far over the line in terms of coupling.\nSpecifically, it\u0026rsquo;s OK for your Interactor to say, \u0026ldquo;This value of this property is catagorized as such and such\u0026rdquo;, but it\u0026rsquo;s totally different for it to say, \u0026ldquo;This value of this property needs to be styled this way\u0026rdquo;.\nThat means that it\u0026rsquo;s best to create an Enum class that encapsulates the PseudoClasses and then create a Property in the Model to hold it. Then the Interactor can establish the relationship between the data Property and the Enum Property through a Binding using ObservableValue.map() and the View can use the Enum Property to style the Nodes. It would look something like this:\nAn Example in the MVCI Framework # Let\u0026rsquo;s look at the previous example transformed to use the MVCI framework. First, the basic components: Model, View, Controller and Interactor\u0026hellip;\nclass Model { val enumProperty = SimpleObjectProperty\u0026lt;StatusPseudoClass\u0026gt;() val amountProperty = SimpleObjectProperty\u0026lt;Int\u0026gt;(-1) } class Interactor(private val model: Model) { init { model.enumProperty.bind(model.amountProperty.map { theValue -\u0026gt; when { theValue \u0026lt; 0 -\u0026gt; StatusPseudoClass.FAILED theValue == 0 -\u0026gt; StatusPseudoClass.NORMAL theValue \u0026lt; 3 -\u0026gt; StatusPseudoClass.WARNING else -\u0026gt; StatusPseudoClass.ERROR } }) } } class Controller { private val model = Model() private val interactor = Interactor(model) private val viewBuilder = ViewBuilder(model) fun getView() = viewBuilder.build() } class ViewBuilder(private val model: Model) : Builder\u0026lt;Region\u0026gt; { private val statusProperty = PseudoClassSupplierProperty().apply { bind(model.enumProperty) } override fun build(): Region = VBox(10.0).apply { children += Label().apply { styleClass += \u0026#34;status-label\u0026#34; textProperty().bind(model.amountProperty.map { \u0026#34;This is a Label: $it\u0026#34; }) statusProperty.setNode(this) } children += Button(\u0026#34;Add 1\u0026#34;).apply { setOnAction { model.amountProperty.value += 1 } } padding = Insets(50.0) } } We have a Model with just two fields, one is a Property\u0026lt;Int\u0026gt; and the other is a Property\u0026lt;StatusPseudoClass\u0026gt;. StatusPseudoClass is the same as documented earlier. The Interactor has just one function, and that\u0026rsquo;s to bind the Enum Property of the model to the Int Property via a map{}. The logic inside the map{} is the business logic.\nThe Controller is boilerplate, and the View is virtually the same output as createContent() gave us in prior examples.\nIt would have been simpler to just use Node.bindPseudoClassEnum(), but this approach shows how you\u0026rsquo;d need to do it in Java. We have a \u0026ldquo;View-only\u0026rdquo; Action Property which handles the PseudoClass changes for us:\nclass PseudoClassSupplierProperty : ObjectPropertyBase\u0026lt;PseudoClassSupplier?\u0026gt;() { private var node: Node? = null private var oldValue: PseudoClassSupplier? = null fun setNode(theNode: Node) { node = theNode } init { invalidated() } override fun invalidated() { node?.let { theNode -\u0026gt; oldValue?.let { theNode.pseudoClassStateChanged(it.getPseudoClass(), false) } value?.let { theNode.pseudoClassStateChanged(it.getPseudoClass(), true) } } oldValue = value } override fun getBean() = node override fun getName() = \u0026#34;\u0026#34; } This is equivalent to the one we used in the Java example, just in Kotlin.\nThe important thing here is that, to the Interactor, Model.enumProperty is just a wrapper for an Enum. The View, however, doesn\u0026rsquo;t even use the Enum nature of the Property at all, it just sees it as PseudoClassSupplier. In this way, the Model is a pipeline for business logic to interact with the View without the View or the Interactor knowing about each other.\nConclusion # This ended up being a long discussion about what is basically a simple concept. Almost more important than the actual idea of non-binary PseudoClasses are the considerations around how to implement any new idea in a JavaFX environment.\nThere are three key points to keep in mind:\nIsolate the \u0026ldquo;plumbing\u0026rdquo; from the implementation.\nThis allows you to adhere to one of the key concepts of good programming \u0026ldquo;Don\u0026rsquo;t Repeat Yourself\u0026rdquo; (DRY). This also means that you can build the infrastructure once, test it and then use it over and over again.\nAs much as possible, keep your layout exclusively layout.\nOnly include configuration code if it\u0026rsquo;s truly trivial. By encapsulating the implementation of a particular set of PseudoClasses into an Enum, an Action Property or a translator away from your layout, it\u0026rsquo;s easy to maintain and declutters your layout code.\nDon\u0026rsquo;t mingle your layout and your business logic.\nUsing a framework is a start, but you have to understand its purpose, and avoid \u0026ldquo;breaking\u0026rdquo; it by exposing elements of the View and Model/Interactor to each other. Provide constructs that allow them to communicate with each other without knowing about each other.\nAbove all, your implementation of something like this has to maintain a pragmatic focus. Loose coupling (which is what this is all about) is supposed to simplify building and maintaining a system. Implementing a super complex solution to achieve loose coupling is often counter-productive.\n","date":"4 June 2024","externalUrl":null,"permalink":"/javafx/elements/stylesheets/nonbinary-pseudoclass/","section":"JavaFX","summary":"What if you need to control the styling of a Node over a range of values?  How can you do that, and is there any way create a framework that you can use over and over again?","title":"Non-Binary PseudoClasses","type":"javafx"},{"content":"ChangeListeners are way to manually monitor a Property for changes in its value, and then to trigger some code when a change happens.\n90%+ of the functionality that you need to build in a Reactive application can be achieved via Bindings, which allow the \u0026ldquo;State\u0026rdquo; of your application to be share back and forth with the Nodes that make up your GUI. But, sometimes you need an \u0026ldquo;Action\u0026rdquo; to take place in response to some change in your Presentation Model, or in one of the Properties inside a screen Node. In those cases you\u0026rsquo;ll need to use a Listener to listen for the change to take place and trigger your code.\nChangeListeners are way to manually monitor a Property for changes in its value, and then to trigger some code when a change happens. Generally speaking, Bindings are much preferred over listeners, but there are times when only a listener will work. The reason for this is that Bindings link \u0026ldquo;State\u0026rdquo;, while ChangeListeners transform changes in \u0026ldquo;State\u0026rdquo; to actions. Programming to State is usually better, but events work better in some circumstances, such as when you need to deal with some part of your application outside the JavaFX domain that doesn\u0026rsquo;t understand JavaFX observables.\nThe Articles # ","date":"1 April 2024","externalUrl":null,"permalink":"/javafx/elements/observables/listeners/","section":"JavaFX","summary":"In Reactive design there are two different and distinct elements that add to the dynamic feel of the GUI.  The first is the “State” and changes to the “State”.  The second is “Events” and their associated actions.  In this section we talk about the second of those, the “Events”, and how to transform changes to “State” into actions.","title":"Listeners and Events","type":"javafx"},{"content":" Introduction # For some reason, tables (so in JavaFX, TableView) are one of the first elements that programmers want to include in their screens. Yet at the same time, they are one of the more complicated screen elements and the concepts behind them are a bit more advanced than most beginners are ready to handle.\nIn all honesty, you won\u0026rsquo;t find anything in this article that isn\u0026rsquo;t covered in just about every \u0026ldquo;TableView 101\u0026rdquo; article out there. What I\u0026rsquo;ve tried to do, however, is to explain why things are done the way that they are done, and how to think about TableView as a data presentation tool.\nWhat is a TableView? # A TableView is a screen element for displaying tabular data. You can find lots of examples on StackOverflow and elsewhere on the web where programmers ask questions like, \u0026ldquo;How do I print my TableView?\u0026rdquo;, \u0026ldquo;How do I create a PDF from my TableView?\u0026rdquo;, \u0026ldquo;How do I populate my TableView from this SQL command\u0026rdquo;, or \u0026ldquo;How do I import my TableView from JSON?\u0026rdquo;. And really, none of these questions have anything to do with TableView. TableView is a screen element, and all of these questions are data-centric and don\u0026rsquo;t really have anything to do with TableView at all.\nYou should understand this first: TableView is all about letting you display your data in rows and columns on the screen. Nothing else (well, you can edit it too - but that\u0026rsquo;s a subject for another article).\nTableView is an excellent choice when you have dense data that fits naturally into a row and column presentation like a spreadsheet. If users need to move columns around, or sort and filter based on column values, then that\u0026rsquo;s also a good reason to use a TableView.\nMy experience is that screen real-estate is always at a premium. If you have a column in your TableView that only has data in about 10% of its rows, then that is a huge waste of screen space. If your data is sparsely populated, or has a great deal of variation in its content and structure, then you should probably think about using a ListView instead.\nUnderstanding the Structure of TableView # Before we look at some examples, you should understand the basic structure of TableView.\nTableView is a Generic Class # This means that any instantiation of TableView should include a type. So you can have a TableView\u0026lt;PersonModel\u0026gt; or TableView\u0026lt;Customer\u0026gt; or TableView\u0026lt;InventoryItem\u0026gt;. Things like that.\nIdeally, the type should be a class which is constructed like a typical JavaFX presentation data model - meaning that it has fields which are Observables and with appropriate getters and setters for the fields. Beyond that, your TableView type can be any class you like, but it needs to provide different data elements for each column.\nTableView Data # Every TableView has a property called items. Items is a wrapper around an ObservableList\u0026lt;TableType\u0026gt;. So it\u0026rsquo;s a list of whatever class you\u0026rsquo;ve defined as your generic type for your TableView. Since it\u0026rsquo;s an ObservableList, any changes to its contents are going to be instantly reflected on the screen in your TableView.\nColumns # The first internal elements of TableView are the columns. Columns are all instances of TableColumn, which is also a generic class. TableColumn has two type parameters: the first is the generic type of the TableView itself, and the second is the type of the data in the TableColumn.\nTableColumn defines three important things: the layout of the column including things like width and heading, how to extract the data from the TableView generic type class, and how to create cells. We\u0026rsquo;ll look at all of these later.\nCells # At the intersection of rows and columns are the cells. Cells are all instances of the class TableCell. Customizing TableCell is one of the key ways to alter the display of data in your TableView. TableCell is a subclass of Labeled; which means that it has two visual elements, the Text and the Graphic. Just as with Labels, you can customize the Graphic to be whatever you want.\nThe default TableCell will simply take whatever data you give it, run it through toString() and put it in the Text element. Essentially this is just like using Label.setText().\nRows # Much less useful on a day-to-day basis are rows, which are instances of the class TableRow. For the most part, you\u0026rsquo;ll deal with rows in order to handle special click actions and styling. Notionally, a TableRow is associated with a single instance of the TableView's generic type, but there\u0026rsquo;s seldom any need to access the data this way. Just like any other scroll-able screen element, TableView has the concept of a \u0026ldquo;viewport\u0026rdquo;. This is the area of the TableView which is currently visible on the screen. TableViews can scroll horizontally and vertically, but vertical scrolling is the most important part to understand.\nThe key concept that you absolutely have to understand is this:\nNote TableView only instantiates enough cells to populate the viewport plus a little bit extra. These cells are then recycled as you scroll up or down through the TableView.\nWhat this means is that if you have a TableView with 1,000,000 items in its list, but a viewport that can only show 10 rows at a time, then you\u0026rsquo;ll probably have around 12-15 rows of cells instantiated. As a row scrolls off the top or the bottom of the viewport, its cells are released, and then populated with new data as new rovs scroll onto the screen.\nThe other implication of this is that virtually all the cells that are used are instantiated when the TableView itself is instantiated or when the viewport is expanded. Data loading into the cells is performed constantly, and potentially frequently. This has two important impacts on how you design your cells:\nThe cell constructor needs to run in the absence of any table data. The data load function needs to be as efficient as possible. From a practical standpoint, this means that any layout code in your TableCells should happen in the constructor, and the data load function should only put data into the layout.\nPerhaps the best way to understand this is to put some console output statements into the constructor and the data load method of a custom TableCell. Maybe put a unique identifier into each TableCell which you include in the output. Then you can see how TableCells are instantiated and recycled.\nA Simple Example # With the background info out of the way, let\u0026rsquo;s build a simple example that doesn\u0026rsquo;t rely on understanding any of that.\nI feel that it\u0026rsquo;s a good practice to separate out the inner workings of your TableViews from the rest of your layout. Years ago, I would have created a custom class extending TableView, but now I prefer to use the builder pattern. So that\u0026rsquo;s what we\u0026rsquo;ll do here.\nThis example is just a table of orders of some type. It\u0026rsquo;s typical of the kind of thing you\u0026rsquo;d see in a business application, and it has lots of potential for customization and fancy presentation. But we\u0026rsquo;ll start of with the basics.\nThe Model # Let\u0026rsquo;s take a look at the data first. We\u0026rsquo;ll create some kind of inventory item:\npublic class OrderModel_1 { private final StringProperty sku = new SimpleStringProperty(\u0026#34;\u0026#34;); private final ObjectProperty\u0026lt;Integer\u0026gt; uid = new SimpleObjectProperty\u0026lt;\u0026gt;(0); private final ObjectProperty\u0026lt;Integer\u0026gt; quantity = new SimpleObjectProperty\u0026lt;\u0026gt;(0); private final ObjectProperty\u0026lt;LocalDate\u0026gt; date = new SimpleObjectProperty\u0026lt;\u0026gt;(LocalDate.now()); private final BooleanProperty shipped = new SimpleBooleanProperty(false); public OrderModel_1(int uid, String sku, int quantity, LocalDate date, Boolean shipped) { this.uid.set(uid); this.sku.set(sku); this.quantity.set(quantity); this.date.set(date); this.shipped.set(shipped); } public StringProperty skuProperty() { return sku; } public ObjectProperty\u0026lt;Integer\u0026gt; uidProperty() { return uid; } public ObjectProperty\u0026lt;Integer\u0026gt; quantityProperty() { return quantity; } public ObjectProperty\u0026lt;LocalDate\u0026gt; dateProperty() { return date; } public BooleanProperty shippedProperty() { return shipped; } public static List\u0026lt;OrderModel_1\u0026gt; generateList() { List\u0026lt;OrderModel_1\u0026gt; results = new ArrayList\u0026lt;\u0026gt;(); int uid = 0; results.add(new OrderModel_1(uid++, \u0026#34;PART 1\u0026#34;, 20, LocalDate.of(2022, 1, 20), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART 17\u0026#34;, 20, LocalDate.of(2021, 7, 15), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 20\u0026#34;, 20, LocalDate.of(2020, 5, 5), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART 101\u0026#34;, 20, LocalDate.of(2021, 4, 17), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART 171\u0026#34;, 20, LocalDate.of(2022, 12, 27), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 35\u0026#34;, 20, LocalDate.of(2021, 11, 4), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART 18\u0026#34;, 20, LocalDate.of(2020, 6, 10), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 59\u0026#34;, 20, LocalDate.of(2021, 9, 19), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 28837\u0026#34;, 20, LocalDate.of(2022, 10, 23), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART A73B\u0026#34;, 20, LocalDate.of(2021, 4, 15), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 4J1\u0026#34;, 20, LocalDate.of(2020, 2, 14), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART FFC32\u0026#34;, 20, LocalDate.of(2021, 7, 13), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 1832-882B\u0026#34;, 20, LocalDate.of(2022, 3, 1), false)); results.add(new OrderModel_1(uid++, \u0026#34;SUBPART 25\u0026#34;, 20, LocalDate.of(2022, 1, 6), false)); results.add(new OrderModel_1(uid++, \u0026#34;ASSEMBLY 7K22\u0026#34;, 20, LocalDate.of(2022, 11, 12), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 9983\u0026#34;, 20, LocalDate.of(2021, 9, 24), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART GH887\u0026#34;, 20, LocalDate.of(2020, 4, 30), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART 1003\u0026#34;, 20, LocalDate.of(2021, 3, 17), false)); results.add(new OrderModel_1(uid++, \u0026#34;PART 31\u0026#34;, 20, LocalDate.of(2021, 10, 5), true)); results.add(new OrderModel_1(uid++, \u0026#34;PART 98XB\u0026#34;, 20, LocalDate.of(2022, 3, 22), false)); return results; } } This is a pretty basic JavaFX data model. I\u0026rsquo;ve not included any of the delegated setters and getters, since we won\u0026rsquo;t be using them.\nTo keep things clean, we\u0026rsquo;ve got a static method that supplies some dummy data included here. It\u0026rsquo;s just a list of OrderModel with random values put in them. I think this took longer to write than all the rest of the code.\nThe TableView # Now, let\u0026rsquo;s take a look at the Builder for the TableView:\npublic class TableViewBuilder1 implements Builder\u0026lt;Region\u0026gt; { private final ObservableList\u0026lt;OrderModel_1\u0026gt; model; public TableViewBuilder1(ObservableList\u0026lt;OrderModel_1\u0026gt; model) { this.model = model; } @Override public Region build() { TableView\u0026lt;OrderModel_1\u0026gt; results = new TableView\u0026lt;\u0026gt;(); TableColumn\u0026lt;OrderModel_1, Integer\u0026gt; uidCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;UID\u0026#34;); uidCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().uidProperty()); results.getColumns().add(uidCol); TableColumn\u0026lt;OrderModel_1, String\u0026gt; skuCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;SKU\u0026#34;); skuCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().skuProperty()); results.getColumns().add(skuCol); TableColumn\u0026lt;OrderModel_1, Integer\u0026gt; quantityCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;Quantity\u0026#34;); quantityCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().quantityProperty()); results.getColumns().add(quantityCol); TableColumn\u0026lt;OrderModel_1, LocalDate\u0026gt; dateCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;Date\u0026#34;); dateCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().dateProperty()); results.getColumns().add(dateCol); TableColumn\u0026lt;OrderModel_1, Boolean\u0026gt; shippedCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;Shipped?\u0026#34;); shippedCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().shippedProperty()); results.getColumns().add(shippedCol); results.setItems(model); results.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); return results; } } This is pretty straight-forward. First we instantiate the TableView itself, then we instantiate and configure each of five columns and add them to the TableView. Finally, we connect the TableView to the Model and set the ColumnResizePolicy.\nConfiguring the Columns # TableColumns are generic, and each one needs to specify the type of data in the TableView, and then the type of data in the column. In this case the generic type of our TableView is OrderModel_1, and we have columns with String, Integer, Boolean and LocalDate. The constructor parameter for each TableColumn specifies the column heading.\nWe also need to tell each TableColumn how to extract the data from the Model for this column. You do this with TableColumn.setCellValueFactory(). This needs a little explanation\u0026hellip;\nIf you look at the JavaDocs for TableColumn, you\u0026rsquo;ll find this:\npublic final void setCellValueFactory​(Callback\u0026lt;TableColumn.CellDataFeatures\u0026lt;S,​T\u0026gt;,​ObservableValue\u0026lt;T\u0026gt;\u0026gt; value) Yikes! What\u0026rsquo;s that?\nIt\u0026rsquo;s a Callback which is identical to a Function in its definition. What makes a Callback a Callback is how it\u0026rsquo;s used. A Callback is intended to provide a \u0026ldquo;hook\u0026rdquo; back to some data or functionality from some particular context that we don\u0026rsquo;t really know about. In this case, the TableView internal code is going to need to extract an ObservableValue from our table model, and we need to tell it how to do that. We don\u0026rsquo;t know, and don\u0026rsquo;t want to know, the particulars of how or when it\u0026rsquo;s going to do this - the only piece of context we need is to know that it will have an instance of CellDataFeatures at hand when it does it.\nThis means that we need to provide a Function that, given a CellDataFeatures will return some kind of ObservableValue, and that ObservableValue will be the data for the TableCell.\nSo now we need to know a little about CellDataFeatures. It\u0026rsquo;s an internal class of TableColumn, and it\u0026rsquo;s, \u0026ldquo;a wrapper class to provide all necessary information for a particular Cell\u0026rdquo; - whatever that means. Most importantly, it contains a reference to the data in the TableRow that the TableCell is in. There\u0026rsquo;s a method TableColumn.CellDataFeatures.getValue() that returns the row data.\nAt this point, that\u0026rsquo;s all you need to know about CellDataFeatures.\nLet\u0026rsquo;s look at our SKU column:\nTableColumn\u0026lt;OrderModel_1, String\u0026gt; skuCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;SKU\u0026#34;); skuCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().skuProperty()); SKU is a String, so that\u0026rsquo;s the column data type, and OrderModel_1 is the class that has our row data. Our non-generic version of setCellValueFactory will look like this:\npublic final void setCellValueFactory​(Callback\u0026lt;TableColumn.CellDataFeatures\u0026lt;OrderModel_1,String\u0026gt;,​ObservableValue\u0026lt;String\u0026gt;\u0026gt; value) We have our Callback (Function) defined by the lambda expression: cdf.getValue().skuProperty(). We know that getValue() is going to return an OrderModel_1 object, and that skuProperty() is going to return us a StringProperty, which satisfies the need for an ObservableValue\u0026lt;String\u0026gt;.\nConfiguring the TableView # There\u0026rsquo;s only two things we need to do with the TableView as a whole:\nConnect it to the Data We use the method TableView.setItems() to do this. Remember that items is actually a ObjectProperty\u0026lt;ObservableList\u0026lt;OrderModel_1\u0026gt;\u0026gt;. So by calling setItems() we are installing a new ObservableList on the TableView. Disable the Last Empty Column This is a fussy, annoying thing, but it just takes one line to fix. If you don\u0026rsquo;t include setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY) then you\u0026rsquo;ll get an empty column on the right side of your TableView. The Application # And now, finally, the rest of the application:\npublic class TableViewDemo1 extends Application { private final ObservableList\u0026lt;OrderModel_1\u0026gt; tableItems = FXCollections.observableArrayList(); public static void main(String[] args) { launch(); } @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(createContent())); primaryStage.show(); fetchData(); } private Region createContent() { VBox results = new VBox(new TableViewBuilder1(tableItems).build()); results.setPadding(new Insets(20)); results.setMinWidth(500); return results; } private void fetchData() { Task\u0026lt;List\u0026lt;OrderModel_1\u0026gt;\u0026gt; loadTask = new Task\u0026lt;\u0026gt;() { @Override protected List\u0026lt;OrderModel_1\u0026gt; call() throws InterruptedException { Thread.sleep(2000); return OrderModel_1.generateList(); } }; loadTask.setOnSucceeded(evt -\u0026gt; tableItems.setAll(loadTask.getValue())); Thread loadThread = new Thread(loadTask); loadThread.start(); } } A lot of this is the usual Application.start() boilerplate. The content of the screen is just a VBox with the TableView in it. The VBox is included so that we can put some padding around the TableView, so it won\u0026rsquo;t butt up against the edges of the window.\nWe also have a simulated data load routine. This has a Thread.sleep() in it because databases always have some amount of latency, and this way we can see how the GUI behaves in a realistic environment. You can also see that we\u0026rsquo;re using Task for this to run our simulated database access in a background thread and loading the results into the TableView on the FXAT.\nWhen it starts, it looks like this for a few seconds:\nThen it changes to this:\nThe first screen snap shows us the \u0026ldquo;placeholder\u0026rdquo; for the TableView. This is the graphic that is shown when the TableView has no data in it. You can customize this, but that\u0026rsquo;s a topic for a later article.\nCustomizing a Cell # The only topic left to cover in TableView basics is how to customize a TableCell. This is done by configuring the TableColumn to use a custom CellFactory. Once again, this is done via a Callback, which we\u0026rsquo;ve already noted is really just a Function.\nThe declaration of the method is this:\npublic final void setCellFactory​(Callback\u0026lt;TableColumn\u0026lt;S,​T\u0026gt;,​TableCell\u0026lt;S,​T\u0026gt;\u0026gt; value) The input is just our TableColumn, and the return value is a TableCell with the generic types set to match our TableColumn generic types. Now we need to look at how to customize a TableCell\u0026hellip;\nIn our example we have a Boolean column called shippedCol. The data displayed in this column is either true or false, which is pretty ugly. We\u0026rsquo;ll look at some better ways to do this.\nThe main thing to remember is that TableCells are created once and re-used many times. This means that we need to define the layout of the TableCell in its constructor, and then provide a mechanism to load data into it as required.\nUsing a CheckBox # This is, in my opinion, the least satisfying way to show Boolean data, but it does have the advantage of being a simple load of the cell data into a Node. This technique will translate nicely into any type of data that you would display in any appropriate JavaFX Node, so it\u0026rsquo;s worth taking a look at:\npublic class BooleanTableCell1\u0026lt;S\u0026gt; extends TableCell\u0026lt;S, Boolean\u0026gt; { CheckBox checkBox = new CheckBox(); StackPane container; public BooleanTableCell1() { container = new StackPane(checkBox); container.setPadding(new Insets(8)); setText(null); setGraphic(null); } @Override protected void updateItem(Boolean item, boolean empty) { super.updateItem(item, empty); if (!empty) { if (item != null) { checkBox.setSelected(item); } else { checkBox.setSelected(false); } setGraphic(container); } else { setGraphic(null); } } } First, you should note that it\u0026rsquo;s more common to just declare a custom TableCell as an anonymous inner class, but it\u0026rsquo;s shown here as a stand-alone class for clarity. Second, it\u0026rsquo;s been left as a partially generic class to make the point that it can be used with any TableView type - you could design this as a utility class to use across your application to have the presentation of different data types consistent in all TableViews.\nFinally, I\u0026rsquo;m aware that there is a CheckBoxTableCell in the JavaFX library that does pretty much the same thing.\nTo use this custom TableCell, there\u0026rsquo;s been one line of code added to the TableViewBuilder1 class:\nshippedCol.setCellFactory(tableColumn -\u0026gt; new BooleanTableCell1\u0026lt;\u0026gt;()); The Callback here is defined with a lambda, and it does nothing more than call the constructor of our BooleanTableCell1 class. The generic type can be inferred inside the lambda with the \u0026lt;\u0026gt; operator.\nThe result looks like this:\nThe Custom TableCell Code # Let\u0026rsquo;s have a look at actual code in our custom BooleanTableCell1\nThe Custom TableCell Gotcha! # The most important line in all of this class is:\nsuper.updateItem(item, empty); Eventually, you will forget to include this line. The result will be hours of lost time and frustration, messing with all the different parts of your TableCell layout, testing, testing, testing and retesting. But still weird things happen. Finally, you\u0026rsquo;ll have the, \u0026ldquo;Ay! Carumba!\u0026rdquo; moment and you\u0026rsquo;ll realize the super.updateItem() call is missing. We all go through this. You will too.\nThe Constructor # Just like any Labeled subclass, TableCell has a Text and a Graphic property, either or both of which you can use. In this case, we\u0026rsquo;re creating a layout that will go into the Graphic property. We won\u0026rsquo;t be using the Text property at all.\nIn the constructor we create a layout, which is just a StackPane with a CheckBox in it. We probably don\u0026rsquo;t need the StackPane, but it\u0026rsquo;s been included to make the point that Graphic can be any kind of layout that you want. Then we set the Graphic and the Text properties to null, just to make sure that the TableCell is initially empty.\nThe updateItem() Method # Every time that the TableView decides that it needs to reload or re-use the TableCell, it will call the updateItem() method. This is the method that you need to customize in order to load the data into your custom layout.\nFirst, a TableCell can either be in a row with an item, or it may be in an empty row. This is why one of the parameters is the Boolean empty. If this flag is true, then the normal action is to just clear out the Graphic property (or the Text property if you are using it), of the TableCell, and you\u0026rsquo;re all done. That\u0026rsquo;s what we do here.\nIf empty is false, then we\u0026rsquo;re not displaying in empty row. However, since the value for this cell is unpacked from an ObservableObject wrapper, it\u0026rsquo;s possible that the value might be Null, so we need to account for this. Here we are just displaying an unselected CheckBox.\nIf you have a non-Null value in a non-empty cell, then the value is simply loaded into the appropriate property in the layout Node. In this case, we\u0026rsquo;re calling CheckBox.setSelected().\nUsing an Image # An image can be a bit more satisfying. Here we\u0026rsquo;re using images of checkboxes filled in with checkmarks and crossmarks in green and red. Visually it looks a bit better than using the CheckBox control.\npublic class BooleanTableCell2 { public BooleanTableCell2() { checkMark = createImageView(this.getClass().getResource(\u0026#34;check-mark.png\u0026#34;).toExternalForm()); crossMark = createImageView(this.getClass().getResource(\u0026#34;cross-mark.png\u0026#34;).toExternalForm()); container = new StackPane(checkMark, crossMark); container.setPadding(new Insets(2)); setText(null); setGraphic(null); } private ImageView createImageView(String path) { ImageView imageView = new ImageView(path); imageView.setPreserveRatio(true); imageView.setFitHeight(24); imageView.setVisible(false); return imageView; } @Override protected void updateItem(Boolean item, boolean empty) { super.updateItem(item, empty); if (!empty) { if (item != null) { checkMark.setVisible(item); crossMark.setVisible(!item); } else { checkMark.setVisible(false); crossMark.setVisible(false); } setGraphic(container); } else { setGraphic(null); } } } Here we use the same StackPane, but this time we put two ImageViews in it. One is a checkmark and the other is a crossmark. The updateItem() method now manipulates the visibility of these to ImageViews so that only the appropriate on is shown in any given row. If the row is empty, or the ObservableValue contains Null then we just hide both of the images.\nThe most important take-away from this is that the layout of the Graphic is not changed in updateItem(). Any code that create in updateItem() which instantiates Nodes, or adds or removes Nodes to the layout is doomed to have performance issues. Yes, go ahead and change properties of the Nodes in the layout, but never, never change the layout itself.\nYou don\u0026rsquo;t have to stick to checkbox style images, either. Here\u0026rsquo;s one using emojis:\nUsing a Shape # Finally, we\u0026rsquo;re going to look at using a simple shape, in this case a Circle, for the boolean value:\npublic class BooleanTableCell3\u0026lt;S\u0026gt; extends TableCell\u0026lt;S,Boolean\u0026gt; { private final Shape circle; public BooleanTableCell3() { circle = new Circle(5); circle.setFill(Color.GREEN); circle.setStroke(Color.GREEN); setText(null); setGraphic(null); } @Override protected void updateItem(Boolean item, boolean empty) { super.updateItem(item, empty); setGraphic(null); if (!empty \u0026amp;\u0026amp; (item != null) \u0026amp;\u0026amp; item) { setGraphic(circle); } } } In this case, we\u0026rsquo;re only going to have the one shape, and we\u0026rsquo;re going to only display it when the value of the boolean is present and true. In any other case, we\u0026rsquo;ll simply leave the cell empty. It looks like this:\nThis can be an extremely effective approach, especially when you have multiple boolean columns and you want the user to be able to quickly see which rows have positive values. Any kind of graphic for the false value clutters up the TableView and makes it harder to see the positives. Of course, this is subjective and the kind of decision that needs to be made as part of the UX design.\nIf you want to have red Circles for the false values, then make the Circle always visible for non-empty, non-Null cells, and manipulate the Fill and Stroke colours of the Circle.\nConclusion # At this point, you have all the information you need to create a basic TableView, populate it with data, and do some basic customization of the TableCells.\nWe looked at the TableCell and how it\u0026rsquo;s layout is very much the same as Label, and we looked at how data is loaded into it. While we looked at different types of Nodes to put in the Graphic, the most important aspect of these examples was to see the different ways that TableCell.updateItem() interacted with the layout to change the way the Graphic looked, or whether to show it at all.\nThere\u0026rsquo;s a lot more that can be done with TableView that involves customizing the TableRows, creating complicated TableCells and dealing with row selections. There\u0026rsquo;s the idea of having TableCells update automatically when data inside the Properties inside the data model change. Not to mention TableView styling, which can be a learning odyssey all of its own.\nWe\u0026rsquo;ll look these topics in future articles.\n","date":"10 October 2023","externalUrl":null,"permalink":"/javafx/elements/tableview-basics/","section":"JavaFX","summary":"What is a TableView and how do you use it?  Here’s enough information to get you started with TableView the right way.","title":"TableView Basics","type":"javafx"},{"content":" Introduction # There are three ways to create a Binding; the Fluent API, using the Bindings class builder methods and extending one of the abstract Binding classes to create a custom binding. We\u0026rsquo;re going to look at this last method in this article.\nYou should understand how to create your own custom Binding class just so that you understand how Bindings work internally. This makes it much easier to understand how to use the builder method in the Bindings class - which you will probably use a lot.\nHow This Fits in the JavaFX Hierarchy # The Binding Interfaces # In JavaFX, Binding is an interface which extends Observable, and ObservableValue. These two super-interfaces are important because they supply the addListenter(), and removeListener() methods for both InvalidationListeners and ChangeListeners.\nBindings themselves rely on InvalidationListeners to control their updates. We\u0026rsquo;ll see how this works. In fact, the two main methods that the Binding interface adds are the invalidate() and isValid methods. In practice, you\u0026rsquo;ll rarely need to directly deal with either of those methods.\nThe Abstract Classes # In practice, virtually all custom Binding classes are created by extending one of the abstract Binding classes contained in the JavaFX library. These are all designed to handle bindings on different types of data. There is BooleanBinding, DoubleBinding, IntegerBinding, ListBinding, LongBinding, MapBinding, ObjectBinding, SetBinding, and StringBinding.\nIn general, all of these abstract classes are similar, differing only in the data types that they are concerned with. The Binding classes for collections have some additional methods that relate to aspects of collections, such as size or \u0026ldquo;empty\u0026rdquo;. Also, all of these classes implement observable interfaces for their data types. So, an IntegerBinding implements ObservableIntegerValue.\nSince they implement Binding, they will implement methods for addListener() (for both InvalidationListeners, and ChangeListeners). The bind() method for all of these classes is protected, so you cannot call it from your layout code - meaning that your bound dependencies need to be defined at the time that you instantiate the Binding.\nThe computeValue() Method # The vast majority of the time, all of the code that you write for a custom Binding class will be it\u0026rsquo;s constructor and the computeValue() method. The computeValue() method is abstract in all of these classes, so it\u0026rsquo;s mandatory when you define your own class. You\u0026rsquo;ll need to call the protected bind() method from the parent abstract class, and the only place to do that is generally the constructor.\nThe computeValue() method is also protected. It\u0026rsquo;s called from the public get() method, and it returns the data type encapsulated by the Binding class. So a class extending IntegerBinding will return int from both its get() and computeValue() methods.\nThis is very important to understand: {% include notice type=\u0026ldquo;primary\u0026rdquo; content=\u0026ldquo;The Binding itself is all about linking Observable values and Properties, but the computeValue() method is concerned about the underlying data type of the binding. It can calculate its return value from anywhere, and it generally deals with normal Java data types and classes.\u0026rdquo; %}\nAn Example # Now, let\u0026rsquo;s look at an example:\npublic class CustomBindingDemo extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 620, 340); stage.setScene(scene); stage.show(); } private Region createContent() { ObjectProperty\u0026lt;Integer\u0026gt; number1 = new SimpleObjectProperty\u0026lt;\u0026gt;(100); ObjectProperty\u0026lt;Integer\u0026gt; number2 = new SimpleObjectProperty\u0026lt;\u0026gt;(30); BooleanProperty evensOnly = new SimpleBooleanProperty(false); Label label1 = new Label(); label1.textProperty().bind(number1.asString()); Label label2 = new Label(); label2.textProperty().bind(number2.asString());posts Label results = new Label(); results.textProperty().bind(new CustomIntegerBinding(number1, number2, evensOnly).asString()); HBox labelBox = new HBox(10, new Label(\u0026#34;Number 1:\u0026#34;), label1, new Label(\u0026#34;Number2: \u0026#34;), label2, new Label(\u0026#34;Results: \u0026#34;), results); Button button1 = new Button(\u0026#34;Increase Value 1\u0026#34;); button1.setOnAction(evt -\u0026gt; number1.set(number1.get() + 1)); Button button2 = new Button(\u0026#34;Increase Value 2\u0026#34;); button2.setOnAction(evt -\u0026gt; number2.set(number2.get() + 1)); CheckBox checkBox = new CheckBox(\u0026#34;Evens Only\u0026#34;); evensOnly.bind(checkBox.selectedProperty()); HBox controlBox = new HBox(5, button1, button2, checkBox); VBox vBox = new VBox(40, labelBox, controlBox); vBox.setPadding(new Insets(30)); return vBox; } } class CustomIntegerBinding extends IntegerBinding { private final ObservableObjectValue\u0026lt;Integer\u0026gt; property1; private final ObservableObjectValue\u0026lt;Integer\u0026gt; property2; private final ObservableBooleanValue evenOnly; private int divisor = 1; public CustomIntegerBinding(ObservableObjectValue\u0026lt;Integer\u0026gt; property1, ObservableObjectValue\u0026lt;Integer\u0026gt; property2, ObservableBooleanValue evenOnly) { super.bind(property1, property2, evenOnly); this.property1 = property1; this.property2 = property2; this.evenOnly = evenOnly; } @Override protected int computeValue() { int result = (property1.get() + property2.get()) / divisor; System.out.println(\u0026#34;computing \u0026#34; + property1.get() + \u0026#34; \u0026#34; + property2.get() + \u0026#34; divisor: \u0026#34; + divisor + \u0026#34; result: \u0026#34; + result); divisor++; if (evenOnly.get()) { return ((result % 2) == 0) ? result : 0; } return result; } } This is a bit goofy, and it certainly doesn\u0026rsquo;t do anything too useful, but it does demonstrate all the key points you need to understand about custom bindings. When it runs, it looks like this:\nand the console output looks like this:\ncomputing 100 30 divisor: 1 result: 130 computing 100 30 divisor: 2 result: 65 computing 101 30 divisor: 3 result: 43 computing 102 30 divisor: 4 result: 33 computing 102 31 divisor: 5 result: 26 computing 102 32 divisor: 6 result: 22 computing 102 33 divisor: 7 result: 19 computing 102 33 divisor: 8 result: 16 computing 103 33 divisor: 9 result: 15 computing 104 33 divisor: 10 result: 13 computing 105 33 divisor: 11 result: 12 The Layout Code # Here we have three Property variables instantiated, which together are the Model for this application. Two are Integer properties used as counters, and the third is a Boolean property. So that we can see the results, we have a set of Labels in a row, the first two of which are bound to our counter properties, and the last is bound to our custom binding.\nThere are two Buttons and a CheckBox in a second row. Each Button increments one of the counter properties, and the CheckBox is bound to the Boolean property.\nThe Custom Binding # This custom Binding extends IntegerBinding, so its get() and computeValue() are going to return int. Since this is an externally declared class, we need to create fields for all of the dependency properties, and they need to be passed in the constructor. That\u0026rsquo;s pretty much all that the constructor does; initialize the fields and call super.bind().\nThe call to super.bind() is going to register each of these properties with IntegerBinding so that they are monitored and will invalidate the Binding when one of them changes. This invalidation will trigger whatever Properties that this Binding is bound to to call the get() method, which will in turn call the computeValue() method.\nThere\u0026rsquo;s also a private int field in the Binding called divisor. This is there to show that this is just an ordinary class, and computeValue() is just an ordinary method like any other.\nWhat does computeValue() do? Basically, it adds the current values of both Integer properties together, and then divides them by the divisor, which it then increments by one. If the Boolean property is true then it checks to see if the result is even or odd, and if it\u0026rsquo;s odd, it returns zero instead.\nAbout Invalidation # At this point we need to talk about Invalidation and how it relates to Bindings.\nWhat is it? # Every Observable has two data elements: The observed value and a Valid boolean. The Valid boolean signals whether or not the observed value has changed since the last time it was accessed, with true meaning that the last value accessed is still valid. When you set a listener on an Observable, what you\u0026rsquo;re really doing is asking to be notified whenever the Valid value changes to \u0026ldquo;false\u0026rdquo;. In other words, we\u0026rsquo;re listening for when the value becomes \u0026ldquo;invalid\u0026rdquo; or, if you like, has been \u0026ldquo;invalidated\u0026rdquo;.\nResetting Validation # Important This is super important: The only way to re-validate an Observable is to call the get() method of the Observable.\nThink about those Labels in the sample code. They are bound to the IntegerProperties. There\u0026rsquo;s a lot of functionality that we don\u0026rsquo;t see (or really care about), behind Label. It needs to take the data from its Text property and turn it into pixels on the screen. Somewhere in there, there\u0026rsquo;s going to be an InvalidationListener on that Text property, and that listener is going to call the property\u0026rsquo;s get() method.\nIn turn, the property is going to call the binding\u0026rsquo;s get() method, which in turn is going to call the get() method of the IntegerProperty. And all three elements are going to have their Valid field set to true.\nInvalidating an Observable # A property can become invalidated in two ways: The set() method can be called on it or the Observable that it\u0026rsquo;s been bound to has become invalidated. Since a bound Property cannot be set(), only one of these two reasons can ever apply to a Property at any given time.\nBindings can only become invalidated when one of their bound dependencies becomes invalidated. In turn, any Observables that are bound to this Binding will also become invalidated.\nWhat you\u0026rsquo;re seeing is that calling super.bind() in your custom Binding simply creates an InvalidationListener on each bound dependency which does nothing except invalidate your custom Binding. Any InvalidationListener on your Binding will be notified of the change.\nImportant This is super, super important: A Binding that\u0026rsquo;s already invalidated cannot be invalidated again until it\u0026rsquo;s reset.\nThe Big \u0026ldquo;Gotcha\u0026rdquo; With Custom Bindings # While it\u0026rsquo;s true that the computeValue() method of a Binding can perform its calculation any way it wants and doesn\u0026rsquo;t have to use any value from its bound dependencies, you really need to make sure that you call the get() method of every bound dependency inside computeValue().\nWhy?\nBecause you need to re-validate all of those bound dependencies. The InvalidationListeners are only going to trigger once when the Observable is invalidated. If it\u0026rsquo;s not re-validated before its value is changed again, it\u0026rsquo;s already invalidated and won\u0026rsquo;t trigger the InvalidationListeners a second time.\nEveryone who\u0026rsquo;s created enough custom Bindings has fallen into this trap at least once. It usually happens when you have Bindings dependent on other Bindings so that it obscures where the problem is. Then, Poof! Three hours of your life are gone trying to figure out why the Binding updates sometimes but not others.\nAn Example of Not Re-validating # Here\u0026rsquo;s the first example modified to remove all of the get() calls on the number2 property:\npublic class CustomBindingDemo2 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 620, 340); stage.setScene(scene); stage.show(); } private Region createContent() { AtomicInteger number2Value = new AtomicInteger(30); ObjectProperty\u0026lt;Integer\u0026gt; number1 = new SimpleObjectProperty\u0026lt;\u0026gt;(100); ObjectProperty\u0026lt;Integer\u0026gt; number2 = new SimpleObjectProperty\u0026lt;\u0026gt;(number2Value.get()); Label label1 = new Label(); label1.textProperty().bind(number1.asString()); Label results = new Label(); results.textProperty().bind(new CustomIntegerBinding2(number1, number2).asString()); HBox labelBox = new HBox(10, new Label(\u0026#34;Number 1:\u0026#34;), label1, new Label(\u0026#34;Results: \u0026#34;), results); Button button1 = new Button(\u0026#34;Increase Value 1\u0026#34;); button1.setOnAction(evt -\u0026gt; number1.set(number1.get() + 1)); Button button2 = new Button(\u0026#34;Increase Value 2\u0026#34;); button2.setOnAction(evt -\u0026gt; number2.set(number2Value.incrementAndGet())); HBox controlBox = new HBox(5, button1, button2); VBox vBox = new VBox(40, labelBox, controlBox); vBox.setPadding(new Insets(30)); return vBox; } } class CustomIntegerBinding2 extends IntegerBinding { private final ObservableObjectValue\u0026lt;Integer\u0026gt; property1; private int divisor = 1; public CustomIntegerBinding2(ObservableObjectValue\u0026lt;Integer\u0026gt; property1, ObservableObjectValue\u0026lt;Integer\u0026gt; property2) { super.bind(property1, property2); this.property1 = property1; } @Override protected int computeValue() { System.out.println(\u0026#34;computing \u0026#34; + property1.get() + \u0026#34;divisor: \u0026#34; + divisor); return property1.get() / divisor++; } } The computeValue() method has been changed so that it only uses number1 and divisor, while the Binding itself still has a dependency on number2.\nThe Label showing the value of number2 has been removed because it was automatically calling get() on number2, and re-validating the Property. Finally, the action in button2 has been changed because it was calling number2.get() in order to change it. So a separate counter, number2Value was introduced so it\u0026rsquo;s no longer necessary to call number2.get() in order to increment it.\nFinally, the BooleanProperty has been removed since we\u0026rsquo;re done with it.\nNow, when you click on button1 the computeValue() method will be called every time. But after the first click of button2, which invalidates number2, it won\u0026rsquo;t have any impact on the screen any more. The value in the Property will change, but it won\u0026rsquo;t cause the custom binding to invalidate, because number2 has already been invalidated and there\u0026rsquo;s no code in the program to re-validate it.\nBinding Lists # Creating Bindings that calculate totals and averages, or which find the minimum or maximum of an ObservableList appears to be very difficult to do at first. It\u0026rsquo;s nearly impossible if you\u0026rsquo;re just using the Fluent API for binding, but actually quite easy with a custom Binding class.\nHere we\u0026rsquo;re going to calculate the total of an ObservableList\u0026lt;Integer\u0026gt; which is loaded into a ListView:\npublic class CustomBindingDemo3 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) { Scene scene = new Scene(createContent(), 620, 340); stage.setScene(scene); stage.show(); } private Region createContent() { Random numberGenerator = new Random(); ObservableList\u0026lt;Integer\u0026gt; theList = FXCollections.observableArrayList(); for (int x = 0; x \u0026lt; 5; x++) { theList.add(numberGenerator.nextInt(101)); } ListView\u0026lt;Integer\u0026gt; listView = new ListView\u0026lt;\u0026gt;(); listView.setItems(theList); Label results = new Label(\u0026#34;\u0026#34;); results.textProperty().bind(new CustomIntegerBinding3(theList).asString()); HBox labelBox = new HBox(10, new Label(\u0026#34;Total: \u0026#34;), results); Button button1 = new Button(\u0026#34;Add Value\u0026#34;); button1.setOnAction(evt -\u0026gt; theList.add(numberGenerator.nextInt(101))); VBox vBox = new VBox(10, listView, labelBox, button1); vBox.setPadding(new Insets(30)); return vBox; } } class CustomIntegerBinding3 extends IntegerBinding { ObservableList\u0026lt;Integer\u0026gt; theList; public CustomIntegerBinding3(ObservableList\u0026lt;Integer\u0026gt; theList) { super.bind(theList); this.theList = theList; } @Override protected int computeValue() { System.out.println(\u0026#34;computing \u0026#34; + theList.size()); return theList.stream().reduce(0, Integer::sum); } } It looks something like this:\nOn the screen we have a ListView, a couple of Labels and a Button. The Button generates a new random number and adds it to the ObservableList. One of the Labels is bound to a custom Binding that calculates the total of values in the ObservableList. So when the Button is clicked, the ObservableList becomes invalidated, which invalidates the custom Binding and triggers the Label to call CustomIntegerBinding3.get(), which invokes its computeValue() method.\nThe calculation inside computeValue() is trivial; stream through the List and add up all the values.\nCustom Bindings as Anonymous Inner Classes # This is probably the most common way that you\u0026rsquo;ll use custom Bindings.\nAnonymous Inner Classes have the advantage that you can access any variable or field which is available in the scope in which they are created, but the limitation that you can\u0026rsquo;t use a constructor with parameters. For custom Bindings, you still need to implement the default constructor because you have to call super.bind().\nHere\u0026rsquo;s the previous List binding example, but rewritten as an anonymous inner class:\npublic class CustomBindingDemo4 extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) { Scene scene = new Scene(createContent(), 620, 340); stage.setScene(scene); stage.show(); } private Region createContent() { Random numberGenerator = new Random(); ObservableList\u0026lt;Integer\u0026gt; theList = FXCollections.observableArrayList(); for (int x = 0; x \u0026lt; 5; x++) { theList.add(numberGenerator.nextInt(101)); } IntegerBinding customListBinding = new IntegerBinding() { { super.bind(theList); } @Override protected int computeValue() { System.out.println(\u0026#34;computing \u0026#34; + theList.size()); return theList.stream().reduce(0, Integer::sum); } }; ListView\u0026lt;Integer\u0026gt; listView = new ListView\u0026lt;\u0026gt;(); listView.setItems(theList); Label results = new Label(\u0026#34;\u0026#34;); results.textProperty().bind(customListBinding.asString()); HBox labelBox = new HBox(10, new Label(\u0026#34;Total: \u0026#34;), results); Button button1 = new Button(\u0026#34;Add Value\u0026#34;); button1.setOnAction(evt -\u0026gt; theList.add(numberGenerator.nextInt(101))); VBox vBox = new VBox(10, listView, labelBox, button1); vBox.setPadding(new Insets(30)); return vBox; } } Once again, it\u0026rsquo;s fairly trivial and looks almost the same as the external custom Binding. There\u0026rsquo;s no need to create a field for ObservableList, since it\u0026rsquo;s available in the scope in which the Binding is created, so it can be directly referenced.\nThere is the slightly strange looking hanging {}, which is the Java way of defining default constructors for anonymous inner classes, and it does nothing other than call super.bind() to set theList as a bound dependency for the Binding.\nSomething to Watch Out For # Since anonymous inner classes have access to all the fields and local variables in their scope, it can be very tempting to just access any of these in computeValue(). However, if any of those fields or variables are mutable and they are NOT set as bound dependencies for the Binding, then changes in them will NOT trigger invalidation of your Binding.\nChanges to those variables will be reflected in the value returned by computeValue() the next time that it is called, but that will only happen when one of the bound dependencies becomes invalidated.\n","date":"19 March 2022","externalUrl":null,"permalink":"/javafx/elements/custom_binding/","section":"JavaFX","summary":"Custom binding classes aren’t used that often, but understanding them is the key to understanding how to use the Bindings library builder methods.","title":"Custom Binding Classes","type":"javafx"},{"content":"If you are like me, when you started programming with JavaFX you found that InvalidationListeners are a mysterious and unfathomable thing. It\u0026rsquo;s not at all clear how they work, and why you would want to use them. So you stay away, and use ChangeListener instead because it\u0026rsquo;s pretty to understand what a \u0026ldquo;change\u0026rdquo; is, and how ChangeListener works.\nBut this is probably a mistake. InvalidationListener is badly explained in the JavaDocs, but the concepts behind them are actually pretty simple and you should learn how to use them. There\u0026rsquo;s a good chance that InvalidationListener is a better fit for your needs that ChangeListener.\nObservable Values and Listeners # The concept of \u0026ldquo;observability\u0026rdquo; is key to any Reactive design, and in JavaFX it\u0026rsquo;s baked in right from the start, but what does it mean?\nIn programming, the \u0026ldquo;Observer\u0026rdquo; pattern is a construct which allows observers to be registered with a value, and those observers will be notified when that value changes. \u0026ldquo;Observable\u0026rdquo; in JavaFX turns that on it\u0026rsquo;s head, and changes the focus from the observer to the observed. It really just the same thing, but from a slightly different perspective.\nIn JavaFX the Observer pattern is implemented by wrapping the data values into classes which which present methods to allow other classes to be notified when the enclosed value changes. The notification mechanism is called a \u0026ldquo;Listener\u0026rdquo;.\nWhat Are Listeners For? # For the most part, listeners are used to trigger imperative code due to changes in the State of your application. Most often, they are used to perform operations that need to be done off the FXAT, or to invoke functionality which simply isn\u0026rsquo;t designed to be Reactive. For instance, Animations are actions that need to be triggered, and this can be done through a listener. In most cases, however, it\u0026rsquo;s better to implement a more direct trigger like an ActionEvent if it\u0026rsquo;s possible.\nSometimes a listener can be useful when it\u0026rsquo;s too complicated to implement a Binding. Perhaps, if there is an arbitrarily large number of elements that need to be connected in a complicated way, attempting to install a Binding on each one may be prohibitive. In that case, having a single listener that then updates all of the dependent values might be less complicated and more clear.\nBefore we look at the listeners, let\u0026rsquo;s take a look at how the data values are wrapped into observables.\nThe JavaFX Observable Interfaces # In JavaFX, the great-great-grandfather of Properties and Bindings is the Observable interface. The great-grandfather is the ObservableValue interface. These two interfaces define the most important aspect of anything observable - that it can be listened to.\nLet\u0026rsquo;s have a quick look at both of these interfaces, and see what they do.\nObservable # Observable is a top level interface, so it doesn\u0026rsquo;t inherit any methods from any other interfaces. It has just two methods:\naddListener(InvalidationListener listener) removeListener(InvalidationListener listener) That\u0026rsquo;s it. Pretty simple. We\u0026rsquo;ll look at what an InvalidationListener is in a little bit.\nObservableValue # ObservableValue extends Observable and adds three new methods:\naddListener(ChangeListener\u0026lt;? super T\u0026gt; listener) removeListener(ChangeListener\u0026lt;? super T\u0026gt; listener) getValue() Okay, so ObservableValue adds the ability to add and remove a ChangeListener onto a value, in addition to the InvalidationListener from Observable. It also adds the ability to get the current value of whatever is inside the ObservableValue.\nYou should also notice that Observable has no way to get the current value of it\u0026rsquo;s object, which you\u0026rsquo;ll see makes it not that useful by itself.\nObservableValue, though, is pretty useful if all you want is to catch when a value changes, grab the new value and do something with it. Which, to be honest, is about 90% of the use cases for any observable class.\nRemember that these are just interfaces, so while you can use them as a data type you can\u0026rsquo;t actually instantiate them and for that you\u0026rsquo;ll have to use some class that implements them. Presumably, any class that you use to do this is also going to have some way to put data into the ObservableValue and change it. We\u0026rsquo;re not going to talk about that at all in this article.\nThe Listeners # The two listeners are also interfaces, and they\u0026rsquo;re both fairly simple. Both of these are functional interfaces, so you\u0026rsquo;ll most likely come across them defined as lambda statements.\nInvalidationListener # InvalidationListener has only one method:\ninvalidated(Observable observable) This method will be called whenever the value in the Observable becomes invalid, and it will pass the observed object itself to that method. This is convenient if you are going to reuse the same listener for several Observables.\nOkay, but what is it, and when is it called?\nThe JavaDocs are less than helpful:\nQuote An InvalidationListener is notified whenever an Observable becomes invalid. It can be registered and unregistered with Observable.addListener(InvalidationListener) respectively Observable.removeListener(InvalidationListener)\nIt says to look at the JavaDocs for ObservableValue. You\u0026rsquo;ll find this:\nQuote An ObservableValue generates two types of events: change events and invalidation events. A change event indicates that the value has changed. An invalidation event is generated, if the current value is not valid anymore.\nAnd then it goes on about \u0026ldquo;lazy evaluation\u0026rdquo; without really explaining anything.\nSo now we \u0026ldquo;know\u0026rdquo; that invalidation events are triggered when the value becomes \u0026ldquo;invalid\u0026rdquo;, and that change events are triggered when the value changes. Let\u0026rsquo;s look at ChangeListener before we try to figure out what \u0026ldquo;invalid\u0026rdquo; means.\nChangeListener # ChangeListener also has only one method:\nchanged(ObservableValue\u0026lt;? extends T\u0026gt; observable, T oldValue, T newValue) This is a little bit more clear. The method is called an it\u0026rsquo;s passed the ObservableValue class itself, plus we get the old value and we get the new value. That makes sense.\nWhich Listener to Choose? # If you are like I was years ago, you got to that \u0026ldquo;becomes invalid\u0026rdquo; part in the JavaDocs and you said to yourself, \u0026ldquo;I don\u0026rsquo;t get it. ChangeListener is the way to go, because I understand that idea\u0026rdquo;. The Web wasn\u0026rsquo;t much help either, because every site seemed to get caught up in details around lazy evaluation and \u0026ldquo;weak listeners\u0026rdquo;.\nSo let\u0026rsquo;s clear this up.\nWhat Does \u0026ldquo;Invalid\u0026rdquo; Mean? # Here are the things that define what \u0026ldquo;Invalid\u0026rdquo; is all about:\nFirst and foremost, \u0026ldquo;Invalid\u0026rdquo; is a state of the Observable. The Observable is either \u0026ldquo;Valid\u0026rdquo; or \u0026ldquo;Invalid\u0026rdquo;, and the Observable itself is going to keep track of this for us.\nThe most important thing to understand is that the Observable is always set to \u0026ldquo;Valid\u0026rdquo; when getValue() is called on it. That\u0026rsquo;s the only way to set it \u0026ldquo;Valid\u0026rdquo;.\nThe next most important thing to understand is that it can\u0026rsquo;t became \u0026ldquo;more invalid\u0026rdquo;. If an Observable is already \u0026ldquo;Invalid\u0026rdquo; and something happens that would otherwise have turned it from \u0026ldquo;Valid\u0026rdquo; to \u0026ldquo;Invalid\u0026rdquo;, nothing\u0026rsquo;s going to change in the Observable. It\u0026rsquo;s already \u0026ldquo;Invalid\u0026rdquo;.\nFinally, an Observable is flipped to \u0026ldquo;Invalid\u0026rdquo; when it\u0026rsquo;s possible that it\u0026rsquo;s value might have changed.\nMight?\nThis is usually going to happen through Bindings. In the example we\u0026rsquo;re going to look at, we\u0026rsquo;re going to have an ObservableValue\u0026lt;Boolean\u0026gt; that is based on whether or not an ObservableValue\u0026lt;Integer\u0026gt; is greater than 10. If the initial value of the ObservableValue is 8 and then 1 is added to it, then your ObservableValue\u0026lt;Boolean\u0026gt; might have changed. You\u0026rsquo;re not going to know if it has unless you run the code for the Binding, which won\u0026rsquo;t happen until you call getValue() on the your ObservableValue\u0026lt;Boolean\u0026gt;.\nThat\u0026rsquo;s where Invalidation comes into play. As soon as that 8 is changed to a 9, JavaFX is going to detect that your Boolean ObservableValue depends on it, and flag both of the ObservableValues as \u0026ldquo;Invalid\u0026rdquo;.\nIn this case, calling getValue() on the Boolean ObservableValue is going to call it to call getValue() on the Integer ObservableValue, and both of them are going to get flipped back to \u0026ldquo;Valid\u0026rdquo; as a result.\nAn Example # Let\u0026rsquo;s take a look at how this works. Here\u0026rsquo;s a small program that puts a Button on the screen. The program has two Properties, one is a BooleanProperty and the other is an IntegerProperty. The BooleanProperty is bound to the IntegerProperty using the \u0026ldquo;Fluent\u0026rdquo; API, and it tests whether the IntegerProperty\u0026rsquo;s value is greater than 10. The IntegerProperty starts off with a value of 7, and is incremented each time the Button is clicked.\nThere are two InvalidationListeners, one on each Property. Each one simply does a System.out.println(), with some text that identifies it, and then it invokes the getValue() method on the IntegerProperty to show the current value of that Property.\npublic class InvalidationDemo extends Application { private final BooleanProperty overTen = new SimpleBooleanProperty(false); private final IntegerProperty counter = new SimpleIntegerProperty(7); public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { overTen.bind(counter.greaterThan(10)); overTen.addListener(ob -\u0026gt; System.out.println(\u0026#34;Boolean invalid \u0026#34; + counter.getValue())); counter.addListener(ob -\u0026gt; System.out.println(\u0026#34;Counter Invalid: \u0026#34; + counter.getValue())); Scene scene = new Scene(createContent(), 320, 240); stage.setScene(scene); stage.show(); } private Region createContent() { Button button = new Button(\u0026#34;Click Me!\u0026#34;); button.setOnAction(evt -\u0026gt; { System.out.println(\u0026#34;Button Clicked\u0026#34;); counter.set(counter.getValue() + 1); }); VBox vBox = new VBox(button); vBox.setPadding(new Insets(30)); return vBox; } } The output looks like this:\nButton Clicked Boolean invalid 8 Counter Invalid: 8 Button Clicked Counter Invalid: 9 Button Clicked Counter Invalid: 10 Button Clicked Counter Invalid: 11 What\u0026rsquo;s going on here?\nNotice that the InvalidationListener doesn\u0026rsquo;t have any code that invokes getValue() on the BooleanProperty. So the BooleanProperty is set to \u0026ldquo;Valid\u0026rdquo; when the Binding is established and there\u0026rsquo;s no other code that will set it to \u0026ldquo;Valid\u0026rdquo; in this application. At this point also, the BooleanProperty has a value of false.\nAfter the first click on the Button, the IntegerProperty is set to \u0026ldquo;Invalid\u0026rdquo;, and the BooleanProperty follows suit, as you would expect. So both InvalidationListeners are triggered.\nThis is important: Even though the BooleanProperty is now \u0026ldquo;Invalid\u0026rdquo;, if it were to be recalculated it would remain \u0026ldquo;false\u0026rdquo;.\nInvalidation simply means that it needs to be checked to see what it\u0026rsquo;s current value is. And if you don\u0026rsquo;t check it, it remains \u0026ldquo;Invalid\u0026rdquo;.\nBoth of these InvalidationListeners invoke getValue() on the IntegerProperty. So it\u0026rsquo;s status is set back to \u0026ldquo;Valid\u0026rdquo;. But the status of the BooleanProperty remains \u0026ldquo;Invalid\u0026rdquo;, since its getValue() is never called.\nOn to click number 2. We see the click, and then we see the InvalidationListener called for the IntegerProperty, but not for the BooleanProperty. This is because the status of the BooleanProperty was already \u0026ldquo;Invalid\u0026rdquo;, so there\u0026rsquo;s no change, and no triggering of that InvalidationListener.\nA few more clicks, and now the IntegerProperty is 11, and the BooleanProperty, if we called its getValue() would report a new value of true.\nBut nothing happens.\nThis is because the status never was reset to \u0026ldquo;Valid\u0026rdquo; by calling getValue().\nNow, let\u0026rsquo;s change one line of code, the InvalidationListener on the BooleanProperty:\noverTen.addListener(ob -\u0026gt; System.out.println(\u0026#34;Boolean invalid \u0026#34; + counter.getValue() + \u0026#34; \u0026#34; + overTen.getValue())); All we\u0026rsquo;ve done here is to add in a call to getValue() on the BooleanProperty. So now every time it\u0026rsquo;s invalidated, we\u0026rsquo;re calling getValue() and resetting it to \u0026ldquo;Valid\u0026rdquo;.\nThe output now looks like this:\nButton Clicked Boolean invalid 8 false Counter Invalid: 8 Button Clicked Boolean invalid 9 false Counter Invalid: 9 Button Clicked Boolean invalid 10 false Counter Invalid: 10 Button Clicked Boolean invalid 11 true Counter Invalid: 11 Now we see that the BooleanProperty in invalidated every time the IntegerProperty is invalidated, and the InvalidationListener on the BooleanProperty is triggered.\nOne last change, to show another aspect you need to be aware of. This time we\u0026rsquo;re going to remove the getValue() calls on the IntegerProperty. So it will never directly be reset to \u0026ldquo;Valid\u0026rdquo;:\noverTen.addListener(ob -\u0026gt; System.out.println(\u0026#34;Boolean invalid \u0026#34; + overTen.getValue())); counter.addListener(ob -\u0026gt; System.out.println(\u0026#34;Counter Invalid: \u0026#34;)); And this is what the output looks like:\nButton Clicked Boolean invalid false Counter Invalid: Button Clicked Boolean invalid false Counter Invalid: Button Clicked Boolean invalid false Counter Invalid: Button Clicked Boolean invalid true Counter Invalid: You can see that the InvalidationListener on the IntegerProperty is invoked every time the Button is clicked. This is because, in order to recalculate the value of the BooleanProperty, the Binding has to implicitly invoke the getValue() on the IntegerProperty. So it actually does get reset each time.\nIt is possible to write a Binding that uses an ObservableValue as a trigger, but doesn\u0026rsquo;t call getValue() on that ObservableValue when it\u0026rsquo;s own getValue() is called. This is something you need to watch out for.\nUsing ChangeListener # We\u0026rsquo;ve seen how \u0026ldquo;Invalidation\u0026rdquo; is different from \u0026ldquo;Changed\u0026rdquo;, and how InvalidationListeners work. But what about ChangeListener?\nChangeListeners are invoked whenever the value in the ObservableValue actually changes. This means that, behind the scenes, the getValue() method of the ObservableValue is going to be called. This is probably why getValue() is defined in ObservableValue - which introduces ChangleListener - and not in Observable: You need getValue() in order to implement ChangeListener.\nAn Example # Let\u0026rsquo;s go back to that same example, but modify it to use ChangeListeners instead:\npublic class ChangeListenerDemo extends Application { private final BooleanProperty overTen = new SimpleBooleanProperty(false); private final IntegerProperty counter = new SimpleIntegerProperty(7); public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { overTen.bind(counter.greaterThan(10)); overTen.addListener((ob, oldValue, newValue) -\u0026gt; System.out.println(\u0026#34;Boolean changed: \u0026#34; + newValue)); counter.addListener((ob, oldValue, newValue) -\u0026gt; System.out.println(\u0026#34;Counter changed: \u0026#34; + newValue)); Scene scene = new Scene(createContent(), 320, 240); stage.setScene(scene); stage.show(); } private Region createContent() { Button button = new Button(\u0026#34;Click Me!\u0026#34;); button.setOnAction(evt -\u0026gt; { System.out.println(\u0026#34;Button Clicked\u0026#34;); counter.set(counter.getValue() + 1); }); VBox vBox = new VBox(button); vBox.setPadding(new Insets(30)); return vBox; } } Just two lines have been changed. You can see that ChangeListener gets both the old value and the new value of the Property as parameters. We\u0026rsquo;re just going to look at the new value here.\nThe output looks like this:\nButton Clicked Counter changed: 8 Button Clicked Counter changed: 9 Button Clicked Counter changed: 10 Button Clicked Boolean changed: true Counter changed: 11 This is quite different. The ChangeListener for the IntegerProperty is called every time, but the ChangeListener for the Boolean is only called once, when the value changes from false to true.\nHow Do They Interact? # Let\u0026rsquo;s see. Back to the same example, but we\u0026rsquo;ll put both an InvalidationListener and a ChangeListener on each Property. The InvalidationListeners are just going to output that they were triggered, without calling getValue().\npublic class ChangeListenerDemo extends Application { private final BooleanProperty overTen = new SimpleBooleanProperty(false); private final IntegerProperty counter = new SimpleIntegerProperty(7); public static void main(String[] args) { launch(); } @Override public void start(Stage stage) throws IOException { overTen.bind(counter.greaterThan(10)); overTen.addListener((ob, oldValue, newValue) -\u0026gt; System.out.println(\u0026#34;Boolean changed: \u0026#34; + newValue)); counter.addListener((ob, oldValue, newValue) -\u0026gt; System.out.println(\u0026#34;Counter changed: \u0026#34; + newValue)); overTen.addListener(ob -\u0026gt; System.out.println(\u0026#34;Boolean invalid \u0026#34;)); counter.addListener(ob -\u0026gt; System.out.println(\u0026#34;Counter invalid \u0026#34;)); Scene scene = new Scene(createContent(), 320, 240); stage.setScene(scene); stage.show(); } private Region createContent() { Button button = new Button(\u0026#34;Click Me!\u0026#34;); button.setOnAction(evt -\u0026gt; { System.out.println(\u0026#34;Button Clicked\u0026#34;); counter.set(counter.getValue() + 1); }); VBox vBox = new VBox(button); vBox.setPadding(new Insets(30)); return vBox; } } The output looks like this:\nButton Clicked Boolean invalid Counter invalid Counter changed: 8 Button Clicked Boolean invalid Counter invalid Counter changed: 9 Button Clicked Boolean invalid Counter invalid Counter changed: 10 Button Clicked Boolean invalid Boolean changed: true Counter invalid Counter changed: 11 Okay, so both of the InvalidationListeners are invoked every time the value in the IntegerProperty changes. This means that both Properties are reset to \u0026ldquo;Valid\u0026rdquo; by their ChangeListener even if the ChangeListener isn\u0026rsquo;t triggered on that Property.\nYes, But Which Should You Use? # The truth is, for virtually every use case where you call the ObservableValue\u0026rsquo;s getValue() method, there is no practical difference between ChangeListener and InvalidationListener. So use whichever you feel more comfortable with.\nI Prefer InvalidationListener # Personally, I find that InvalidationListener is a little bit simpler to implement, because there\u0026rsquo;s only one parameter and calling ob.getValue() is no more difficult than accessing the newVal parameter of ChangeListener.\nI find that InvalidationListener requires you to think a little bit more about the underlying mechanism that triggers the listener, and this is a good thing. You should understand what\u0026rsquo;s really going on and base your design on that understanding. Finally, you should be aware the Bindings are based on InvalidationListeners under-the-hood, and if your Bindings are doing strange things, being comfortable with the concepts behind InvalidationListeners is going to make it easier to fix the problems.\nSometimes the Invalidation is More Important Than the Change # There are occasions where having a Property become \u0026ldquo;Invalid\u0026rdquo; may be more important than seeing the value change. Perhaps there are elements of you application that aren\u0026rsquo;t represented by Properties that need to be re-evaluated at certain times. In that case, an InvalidationListener might be more appropriate than a ChangeListener.\nAnd remember, it\u0026rsquo;s very possible that an InvalidationListener might trigger even when a ChangeListener wouldn\u0026rsquo;t.\nUse ChangeListener If You Care About the Old Value # Since ChangeListener gives you both the old and the new value of the property, if your logic requires that you do something different based on different values the Property might changed from, then ChangeListener is the way to go. Another way of saying this is: Use ChangeListener if you care about the nature of the change.\n","date":"1 February 2022","externalUrl":null,"permalink":"/javafx/elements/listeners/","section":"JavaFX","summary":"ChangeListener or InvalidationListener, which is right kind of listener to use?","title":"Observable Listeners in JavaFX","type":"javafx"},{"content":" What\u0026rsquo;s a PseudoClass? # Have you ever noticed how a Button changes a little when you hover your mouse over it? Or darkens to look like it\u0026rsquo;s pressed down when you hold down a mouse button in it? These presentation changes are usually accomplished through the use of PseudoClasses.\nPseudoClasses are specialized CSS selectors which represent on/off states of some value of a Node. One or more aspects of the styling of a Node will be changed when the PseudoClass is turned on, and then returned back to their original state when the PseudoClass is turned off again.\nThey\u0026rsquo;re useful because the styling is all handled for you behind the scenes. The normal use case is to tie the PseudoClass state to a Boolean Property, and then bind that Property to a Property in the Model or some other part of the GUI. Sometimes the PseudoClass state is control by EventHandlers.\nBuilt-In PseudoClasses # There are a number of predefined PseudoClasses in JavaFX that you can use without any special programming. For instance, many controls have a disabled pseudo class which is automatically maintained for you whenever the control\u0026rsquo;s Disabled property changes.\nThe standard Modena CSS has definitions like this:\n.spinner:disabled { -fx-opacity: 0.4; } You can override these in your own stylesheets to make the control behave differently when it\u0026rsquo;s disabled.\nCreate Your Own PseudoClass # The built-in PseudoClasses are useful, but sometimes you need to create your own PseudoClass to handle state changes in ways unique to your application. For instance, you might want to change the colour of a Label if the data it displays indicates an error condition.\nThis article is about how to create a brand new PseudoClass and how to link it into your screen Node so that you can style it via your CSS.\nThe JavaDocs for Pseudoclass are Horrible # If you want to look, you can find them here.\nThe introduction at the top reads like this:\nPseudoClass represents one unique pseudo-class state. Introducing a pseudo-class into a JavaFX class only requires that the method Node.pseudoClassStateChanged(javafx.css.PseudoClass, boolean) be called when the pseudo-class state changes. Typically, the pseudoClassStateChanged method is called from the protected void invalidated() method of one of the property base classes in the javafx.beans.property package.\nNote that if a node has a default pseudo-class state, a horizontal orientation for example, pseudoClassStateChanged should be called from the constructor to set the initial state.\nThe following example would allow \u0026ldquo;xyzzy\u0026rdquo; to be used as a pseudo-class in a CSS selector.\nYikes! That doesn\u0026rsquo;t really explain anything unless you already understand it.\nAnd then they\u0026rsquo;ve included the following example code:\npublic boolean isMagic() { return magic.get(); } public BooleanProperty magicProperty() { return magic; } public BooleanProperty magic = new BooleanPropertyBase(false) { @Override protected void invalidated() { pseudoClassStateChanged(MAGIC_PSEUDO_CLASS, get()); } @Override public Object getBean() { return MyControl.this; } @Override public String getName() { return \u0026#34;magic\u0026#34;; } } private static final PseudoClass MAGIC_PSEUDO_CLASS = PseudoClass.getPseudoClass(\u0026#34;xyzzy\u0026#34;); Seriously, I don\u0026rsquo;t think this is a great way to explain how it works. They\u0026rsquo;ve even called it magicProperty, which might be an admission that they understood they weren\u0026rsquo;t explaining it very well when they wrote it.\nMyControl.this isn\u0026rsquo;t defined anywhere, but it appears that this snippet of code is supposed to be embedded into a class which is an extension of Node. The idea being that this class now exposes a BooleanProperty through magicProperty() that the layout can then bind to some other property to control the PseudoClass.\nAlso, there\u0026rsquo;s a typo (which I\u0026rsquo;ve fixed here) in the call to pseudoClassStateChanged(). That doesn\u0026rsquo;t help either.\nHow Does it Really Work? # In reality, PseudoClass is a lot easier to understand than you might think. There\u0026rsquo;s no magic, and it\u0026rsquo;s really easy to use PseudoClass.\nCreating the PseudoClass # In the example code from the JavaDocs, the first part that\u0026rsquo;s really important is the single line:\nprivate static final PseudoClass MAGIC_PSEUDO_CLASS = PseudoClass.getPseudoClass(\u0026#34;xyzzy\u0026#34;); What does this do?\nIt does two things, one you can see, and one that you can\u0026rsquo;t.\nThe first is to create an instance of PseudoClass as a static field. Does it need to be static? Technically, no, but as we\u0026rsquo;ll see in a little bit, it\u0026rsquo;s probably a good idea to implement it this way.\nThe second thing it does, which you cannot see, is that it updates a static Map deep inside PseudoClass, adding whatever name you used to initialize your PseudoClass - in this case, \u0026ldquo;xyzzy\u0026rdquo;. The Map makes sure that only one element with the same name exists in your application.\nIf you try to instantiate another PseudoClass anywhere in your application with the same name, it will just reuse the Map entry it already has for that name. That\u0026rsquo;s not really a big deal, but you should avoid using names that are already defined in JavaFX, like \u0026ldquo;hover\u0026rdquo; or \u0026ldquo;disabled\u0026rdquo;, because there might already be code that\u0026rsquo;s going to be manipulating any PseudoClasses with that name.\nSetting the PseudoClass Value # The only other part of the example code you need to understand is this:\n@Override protected void invalidated() { pseudoClassStateChanged(MAGIC_PSEUDO_CLASS, get()); } Even this is too complicated. You could use something like this:\npseudoClassStateChanged(MAGIC_PSEUDO_CLASS, true); What does this do? pseudoClassStateChanged() is a method in Node. You pass it a PseudoClass, and a new value (true or false) for that PseudoClass in that Node. Under the hood, this method then adds or removes this PseudoClass to its list of active PseudoClasses. In reality, this list is really just a list of the names that were given to the PseudoClasses when they were instantiated.\nImportant Things to Know About PseudoClass # PseudoClass isn\u0026rsquo;t part of the Node The PseudoClass itself isn\u0026rsquo;t installed into the Node, formally declared as part of it, or prepped in the Node in any special way at all. You can instantiate a PseudoClass anywhere and pass it to any Node\u0026rsquo;s pseudoClassStateChanged() method, and it will work. Furthermore, you can call a Node's pseudoClassStateChanged() from any number of places using completely different instances of a PseudoClass with the same name, and it will work exactly the same as if you had only used one instance of the same PseudoClass repeatedly. Node.pseudoClassStateChanged() is a public method. This means that there\u0026rsquo;s no need to extend any Node just to implement a PseudoClass on it. You can manage the PseudoClasses on a Node from outside the Node. This is absolutely not clear from the JavaDoc example. The mechanism connecting PseudoClass to CSS isn\u0026rsquo;t our concern How the Node actually turns the list of active PseudoClasses into styling changes an your screen is something that we simply don\u0026rsquo;t need to know. As long as your CSS has the selectors defined, it will work. PseudoClass itself isn\u0026rsquo;t Reactive It\u0026rsquo;s important to note that pseudoClassStateChanged() itself has NOTHING to do with Properties or Bindings. It\u0026rsquo;s a plain vanilla Java method. PseudoClass instances aren\u0026rsquo;t functionally unique There is nothing particularly special or specific about any instance of PseudoClass other than its name and the way that names are managed. The following code is perfectly fine: PseudoClass xyzzy = PseudoClass.getPseudoClass(\u0026#34;xyzzy\u0026#34;); myButton.pseudoClassStateChanged(xyzzy, true); It\u0026rsquo;s probably a bit inefficient if it\u0026rsquo;s going to be called a lot, just because there\u0026rsquo;s a fair bit of code running inside of PseudoClass.getPseudoClass(). So it might be best to declare it as a static field inside of whatever class holds this code.\nConnecting PseudoClasses to Properties and Bindings # We\u0026rsquo;ve seen that PseudoClasses are just normal Java and are implemented through normal imperative programming techniques. But if you want to build a reactive JavaFX application, then you need to control the PseudoClasses through Properties and Bindings.\nHow do you do that?\nThe way to connect imperative code to Properties is through Invalidation Listeners. This is exactly what the example from the JavaDocs does, although it does it in the most complicated way possible.\nEssentially, what an Invalidation Listener does is to define a block of code that will run whenever the system decides that the value held in a property is no longer valid because it\u0026rsquo;s potentially been changed, or some other properties that it\u0026rsquo;s been bound to have changed. Calling get() on the Property will cause it to recalculate it\u0026rsquo;s value, but you can also run whatever other code you want in the Listener. This is where you can call Node.pseudoClassStateChanged().\nThe Property that you\u0026rsquo;re listening to can be anything, anywhere. It doesn\u0026rsquo;t need to be a field in the Node, and it doesn\u0026rsquo;t need to be a BooleanProperty either. The Listener just needs to be defined in some scope that has access to both the Node and the Property. The Listener would then contain whatever logic you need to turn a change in your Property value into a boolean value to be sent to Node.pseudoClassStateChanged().\nA Simple Example # In this example, we\u0026rsquo;re going to look at the \u0026ldquo;normal\u0026rdquo; use case for PseudoClass, a BooleanProperty which is part of the Model and which is connected to a PseudoClass through an InvalidationListener:\npublic class PseudoClassDemo extends Application { private BooleanProperty displayInRed = new SimpleBooleanProperty(false); @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 320, 240); scene.getStylesheets().add(PseudoClassDemo.class.getResource(\u0026#34;test.css\u0026#34;).toString()); stage.setScene(scene); stage.show(); } private Region createContent() { PseudoClass redDisplay = PseudoClass.getPseudoClass(\u0026#34;red\u0026#34;); Label label = new Label(\u0026#34;This is the label text\u0026#34;); displayInRed.addListener(inv -\u0026gt; { label.pseudoClassStateChanged(redDisplay, displayInRed.get()); }); Button button = new Button(\u0026#34;Click Me\u0026#34;); button.setOnAction(evt -\u0026gt; displayInRed.set(!displayInRed.get())); return new VBox(20, label, button); } public static void main(String[] args) { launch(); } } Here\u0026rsquo;s the CSS file for this:\n.label:red { -fx-text-fill: red; } When it runs, it looks like this:\nAfter the Button is clicked, it looks like this:\nI think this strips virtually all of the mystery out of PseudoClass creation. It is possible to cut the BooleanProperty completely out of this if you want and call label.pseudoClassStateChanged() directly from the Button action - although this is not a good idea in terms of creating a Reactive JavaFX program:\npublic class PseudoClassDemo2 extends Application { private boolean showInRed = false; @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 320, 240); scene.getStylesheets().add(PseudoClassDemo2.class.getResource(\u0026#34;test.css\u0026#34;).toString()); stage.setScene(scene); stage.show(); } private Region createContent() { PseudoClass redDisplay = PseudoClass.getPseudoClass(\u0026#34;red\u0026#34;); Label label = new Label(\u0026#34;This is the label text\u0026#34;); Button button = new Button(\u0026#34;Click Me\u0026#34;); button.setOnAction(evt -\u0026gt; { showInRed = !showInRed; label.pseudoClassStateChanged(redDisplay, showInRed); }); return new VBox(20, label, button); } public static void main(String[] args) { launch(); } } Once again, this isn\u0026rsquo;t a good practice. It\u0026rsquo;s included here just to show how PseudoClass is just regular Java code.\nA More Complicated Example # At this point, we\u0026rsquo;re not really talking about PseudoClass itself any more, but about strategies around transforming non-boolean Properties into CSS pseudo-class changes.\nLet\u0026rsquo;s look at how to turn a StringProperty into pseudo-class states:\npublic class PseudoClassDemo3 extends Application { private final StringProperty alertLevel = new SimpleStringProperty(\u0026#34;Normal\u0026#34;); @Override public void start(Stage stage) throws IOException { Scene scene = new Scene(createContent(), 300, 100); scene.getStylesheets().add(PseudoClassDemo3.class.getResource(\u0026#34;test.css\u0026#34;).toString()); stage.setScene(scene); stage.show(); } private Region createContent() { PseudoClass redDisplay = PseudoClass.getPseudoClass(\u0026#34;red\u0026#34;); PseudoClass orangeDisplay = PseudoClass.getPseudoClass(\u0026#34;orange\u0026#34;); Label label = new Label(\u0026#34;This is the label text\u0026#34;); alertLevel.addListener(inv -\u0026gt; { label.pseudoClassStateChanged(redDisplay, false); label.pseudoClassStateChanged(orangeDisplay, false); switch (alertLevel.get()) { case \u0026#34;Warning\u0026#34; -\u0026gt; label.pseudoClassStateChanged(orangeDisplay, true); case \u0026#34;Emergency\u0026#34; -\u0026gt; label.pseudoClassStateChanged(redDisplay, true); } }); return new VBox(20, label, createButtons()); } private Node createButtons() { Button warningButton = new Button(\u0026#34;Warning\u0026#34;); warningButton.setOnAction(evt -\u0026gt; alertLevel.set(\u0026#34;Warning\u0026#34;)); Button emergencyButton = new Button(\u0026#34;Emergency\u0026#34;); emergencyButton.setOnAction(evt -\u0026gt; alertLevel.set(\u0026#34;Emergency\u0026#34;)); Button normalButton = new Button(\u0026#34;All Clear\u0026#34;); normalButton.setOnAction(evt -\u0026gt; alertLevel.set(\u0026#34;All Clear\u0026#34;)); return new HBox(10, normalButton, warningButton, emergencyButton); } public static void main(String[] args) { launch(); } } And the CSS file:\n.label:red { -fx-text-fill: red; } .label:orange { -fx-text-fill: darkorange; } Which looks like this when the \u0026ldquo;Warning\u0026rdquo; button is clicked:\nThe Buttons just update the StringProperty, putting a new value into it. The StringProperty has an InvalidationListener added to it, in a scope that has access to the Label so that the InvalidationListener can call its pseudoClassStateChanged() method.\nThe InvalidationListener just turns off all of the custom PseudoClasses in the Label, and then uses a switch statement to turn on a specific PseudoClass depending on the value in the StringProperty.\nConclusion # One good thing about the way that PseudoClass is introduced in the JavaDocs is that it reaffirms that the designers of JavaFX intended it to be used as a Reactive framework. You can tell this because they take care to explain it in the context of a BooleanProperty embedded into a Node.\nTo be perfectly fair, though, the second sentence in the JavaDocs introduction is a complete and concise summary of everything in this tutorial:\nIntroducing a pseudo-class into a JavaFX class only requires that the method Node.pseudoClassStateChanged(javafx.css.PseudoClass, boolean) be called when the pseudo-class state changes.\nHowever, everything else in the JavaDocs makes it very difficult to believe it could be that simple. Hopefully this tutorial has cleared that up.\n","date":"24 January 2022","externalUrl":null,"permalink":"/javafx/elements/pseudo_classes/","section":"JavaFX","summary":"Pseudo Classes are the best way to handle on/off state changes in a Node in JavaFX.  But it’s very badly explained in the JavaDocs and hard to understand.  This article should clear that up.","title":"Demystifying PseudoClasses in JavaFX","type":"javafx"},{"content":"Way, way back in 2014 when I started learning JavaFX, I tried using Scene Builder and FXML and found it to be an amazing waste of time and effort. One of the best decisions I ever made was to abandon it early on, and just code screens by hand. I advise everyone else to do the same.\nThere seems to be some confusion as to whether you can even hand code a screen in JavaFX, not to mention how you would go about doing it. This is probably because virtually all of the beginners\u0026rsquo; tutorials available make the assumption that \u0026ldquo;drag and drop\u0026rdquo; is only method that\u0026rsquo;s going to be palatable to new learners.\nIn this article I\u0026rsquo;m going to build a complete screen, bound to a data model, that accepts input and works. It will be fully configured and styled. You\u0026rsquo;ll see how just applying some of the basic concepts of Clean Coding will guide the design to produce an amazing amount of functionality in a small amount of code that\u0026rsquo;s takes very little time to write.\nYou\u0026rsquo;ll see that the end result is simple and easy to read and understand, and should be equally easy to maintain and expand.\nThe Beginning # We are going to build an application from the start. So we\u0026rsquo;ll begin with the usual main class that extends Application, and it will look something like this:\npublic class NoFxmlExample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Region sceneRoot = new ScreenBuilder().build(); Scene scene = new Scene(sceneRoot); primaryStage.setScene(scene); scene.getStylesheets().add(\u0026#34;/css/default.css\u0026#34;); primaryStage.show(); } } Now, most of that is boilerplate, and the only thing you really need to work out to actually build the screen is to create sceneRoot. I\u0026rsquo;ve defined it as a Region, and that\u0026rsquo;s a good starting place, because anything that extends Region is going to work as the root of a Scene. Examples of Regions are BorderPane, ScrollPane, AnchorPane, and StackPane as well as VBox and HBox. Your region can be any one of those, plus a few others I haven\u0026rsquo;t listed.\nOne thing that we\u0026rsquo;ll get back to later is the stylesheet. It\u0026rsquo;s good practice to put your styling into a stylesheet and then use selectors to apply it to various nodes in your application. I have a \u0026ldquo;default\u0026rdquo; stylesheet I use for a lot of projects, so I just went and added it to the Scene here.\nA general rule of thumb is that you should only extend classes that you are going to add new functionality to. If you\u0026rsquo;re just configuring them, then you should use a builder class - which is what we are doing here. Personally, I like to use the Builder interface, because it\u0026rsquo;s built-in, obvious and works well. That\u0026rsquo;s what the ScreenBuilder class is, it\u0026rsquo;s an implementation of Builder\u0026lt;Region\u0026gt;.\nSo let\u0026rsquo;s get to that.\nThe ScreenBuilder Class # We\u0026rsquo;ll start with just the bare essentials, to get to something that will compile and run as quickly as possible. We want to be able to see our screen as we build it out, so it needs to run right from the get-go.\nBecause it\u0026rsquo;s an excellent all-round start for a screen that has a basic structure, I\u0026rsquo;ve chosen to use a BorderPane:\npublic class ScreenBuilder implements Builder\u0026lt;Region\u0026gt; { @Override public Region build() { BorderPane results = new BorderPane(); results.setTop(createTop()); return results; } private Node createTop() { return new Text(\u0026#34;This is the Screen Title\u0026#34;); } } It looks like this:\nNot pretty, but it\u0026rsquo;s a start. The key point is that now you can see what you\u0026rsquo;ve built and exactly how it will look on the screen. In about 6 lines of code!\nSome Notes About Code Structure and Organization # I\u0026rsquo;ve started this out the way that I would structure most screen builders. The build() method instantiates the container - in this case a BorderPane - and then each of the sections is built in its own method. These methods all return something generic, like a Node or a Region, and not the actual class of the object being returned. This is for two reasons:\nThe calling method should never reach inside the returned value to fiddle with it. All of the configuration should go inside that creation method. It\u0026rsquo;s easier to change it later, or to wrap another container around the original returned value. We\u0026rsquo;ll see this in action later on. By the same token, the creation method should never attempt to reach up to the container to tinker with the layout.\nAdding Some Styling to the Heading # To illustrate how the design grows, and where the code belongs, I wrapped the Text in a HBox with a border and made the text bigger. When that was done, it was clear that everything was cramped together too much. The Text was too close to the border, and the border was too close to the edge of the screen. So some space was added. The code now looks like this:\npublic class ScreenBuilder implements Builder\u0026lt;Region\u0026gt; { @Override public Region build() { BorderPane results = new BorderPane(); Node headingBox = createHeadingBox(); BorderPane.setMargin(headingBox, new Insets(4)); results.setTop(headingBox); return results; } private Node createHeadingBox() { HBox results = new HBox(); Text headingText = new Text(\u0026#34;This is the Screen Title\u0026#34;); headingText.getStyleClass().add(\u0026#34;heading-text\u0026#34;); results.getStyleClass().add(\u0026#34;standard-border\u0026#34;); results.getChildren().add(headingText); results.setPadding(new Insets(6)); return results; } } There are some things you should notice:\nThe creation method for the heading has now been renamed to something more descriptive. The creation method for the heading now returns an HBox, instead of a Text, yet the signature of the method did not need to be changed. The styling for the Text and the HBox are done in the creation method. The internal spacing for the HBox (the padding) is done in the creation method. The external spacing for the heading Node is done in the build() method, as this is an element of the layout of the BorderPane. Just to be really, really clear about this. No way, no how, should the line:\nBorderPane.setMargin(headingBox, new Insets(4)); go inside the createHeadingBox() method.\nThe createHeadingBox() method has no knowledge that it\u0026rsquo;s return value is going to be placed into a BorderPane. So it cannot do this.\nAlso, since the createHeadingBox() method returns a Node, there\u0026rsquo;s no way that build() can call setPadding() on it.\nLayout vs Configuration # An important concept when writing clean JavaFX code is to understand the difference between \u0026ldquo;Layout\u0026rdquo; and \u0026ldquo;Configuration\u0026rdquo;.\nConfiguration # Configuration is the setting of properties and behaviours of a Node. This can include things like styling, scaling, setting minimum or maximum height and width, visibility, and binding to properties in a Model.\nLayout # Layout is the population of a Region with child Nodes and determining their position and bounds, as well as sizing parameters in relation to other elements of the layout.\nIt\u0026rsquo;s possible, even probable, that a Region will be defined in your code with both Configuration and Layout. We have that so far in our example. The createHeadingBox() method configures the styling for an HBox and then defines its layout.\nYou will find that Configuration tends to be less specific to a particular screen than layout. For instance, you\u0026rsquo;ll create a styling for Text that works in a particular context, and then you\u0026rsquo;ll want to re-use that styling throughout a screen, or throughout an application, or even across multiple applications. Once again, this is an important point, as we\u0026rsquo;ll see in a little bit.\nAdding A Data Entry Section # We\u0026rsquo;re going bring these ideas together now by adding a GridPane with some prompts and data entry fields. It\u0026rsquo;s going to go in the centre of the BorderPane:\npublic class ScreenBuilder implements Builder\u0026lt;Region\u0026gt; { @Override public Region build() { BorderPane results = new BorderPane(); Node headingBox = createHeadingBox(); BorderPane.setMargin(headingBox, new Insets(4)); results.setTop(headingBox); results.setCenter(createDataEntrySection()); return results; } private Node createHeadingBox() { HBox results = new HBox(); Text headingText = new Text(\u0026#34;This is the Screen Title\u0026#34;); headingText.getStyleClass().add(\u0026#34;heading-text\u0026#34;); results.getStyleClass().add(\u0026#34;standard-border\u0026#34;); results.getChildren().add(headingText); results.setPadding(new Insets(6)); return results; } private Node createDataEntrySection() { GridPane results = createTwoColumnGridPane(); results.add(createPromptText(\u0026#34;First Name:\u0026#34;), 0, 0); results.add(createInputField(), 1, 0); results.add(createPromptText(\u0026#34;Last Name:\u0026#34;), 0, 1); results.add(createInputField(), 1, 1); results.add(createPromptText(\u0026#34;Email:\u0026#34;), 0, 2); results.add(createInputField(), 1, 2); results.add(createPromptText(\u0026#34;Phone Number:\u0026#34;), 0, 3); results.add(createInputField(), 1, 3); return results; } private GridPane createTwoColumnGridPane() { GridPane results = new GridPane(); results.getColumnConstraints().addAll(createJustifiedConstraint(HPos.RIGHT), createJustifiedConstraint(HPos.LEFT)); results.setHgap(6); results.setVgap(4); results.setPadding(new Insets(4)); results.getStyleClass().add(\u0026#34;test-border\u0026#34;); return results; } private ColumnConstraints createJustifiedConstraint(HPos alignment) { ColumnConstraints results = new ColumnConstraints(); results.setHalignment(alignment); return results; } private Node createInputField() { TextField results = new TextField(); results.setMaxWidth(100); return results; } private Node createPromptText(String prompt) { Text results = new Text(prompt); results.getStyleClass().add(\u0026#34;label-text\u0026#34;); return results; } } Now it looks like this:\nLet\u0026rsquo;s look at what\u0026rsquo;s been done here. First, a new call was added to the main layout section to populate the Centre region of the BorderPane through the createDataEntrySection() method.\nThat new method creates a GridPane with two columns, and then populates the left column of the GridPane with prompt Texts, and the right column with TextFields.\nNotice that the Configuration has been completely split away from the Layout. It might be argued that most of the \u0026ldquo;configuration\u0026rdquo; of a GridPane has to do with defining how the layout works, but the important thing is that contents of the GridPane and their positioning in the GridPane are isolated from everything else. That makes it easy to see how the layout works. To that end, all of the configuration of the Texts and TextFields is handled by separate methods.\nNote I\u0026rsquo;ve also added a \u0026ldquo;test-border\u0026rdquo; styling to the GridPane that will be removed later. One of the things that can be challenging with JavaFX is understanding which region owns the whitespace on the screen. Putting a temporary border around regions is a great way to understand what\u0026rsquo;s really going on.\nUsing the DRY Principle # There is a lot of boilerplate code in JavaFX.\nThe key to writing good JavaFX code is to aggressively apply the DRY (Don\u0026rsquo;t Repeat Yourself) Principle whenever you can. The layout of that GridPane would be agonizing to read if the boilerplate for the Texts was repeated inside code, like this:\nText firstNamePrompt = new Text(\u0026#34;First Name:\u0026#34;); firstNamePrompt.getStyleClass().add(\u0026#34;label-text\u0026#34;); results.add(firstNamePrompt, 0,0); That\u0026rsquo;s going to add up to 8 extra lines of code in a method that only has 10 lines to start with! Not to mention another 8 lines for the TextFields if they\u0026rsquo;re all defined in-line as well.\nThis concept is carried over to the creation of the ColumnConstraints for the GridPane as well. It\u0026rsquo;s just two lines of code each time, but the single line of code that\u0026rsquo;s used instead has the advantage of having the method name describing what the ColumnConstraints are for, so it\u0026rsquo;s even easier to read.\nAdding Some More Data Entry # We\u0026rsquo;re going to add a TextArea to the screen, to show how the screen design grows and the code is morphed to accommodate it. Rather than repeat the whole class, I\u0026rsquo;ll just show the changed code:\nprivate Node createDataEntrySection() { HBox results = new HBox(10); results.setPadding(new Insets(2, 10, 4, 2)); results.getChildren().addAll(createGridPane(), createBioBox()); return results;Ugly But It\u0026#39;s Something } private Node createBioBox() { TextArea bioTextArea = new TextArea(); bioTextArea.setWrapText(true); bioTextArea.setMaxWidth(200); bioTextArea.setMaxHeight(200); return new VBox(4, createPromptText(\u0026#34;Short Bio:\u0026#34;), bioTextArea); } Now it looks like this:\nThe old createDataEntrySection() was renamed to createGridPane() and a new createDataEntrySection() was written. Essentially, this \u0026ldquo;pushes\u0026rdquo; the GridPane down one level in the container structure, as it\u0026rsquo;s now contained in a new HBox which comprises the BorderPane's centre region.\nThe \u0026ldquo;BioBox\u0026rdquo; area is just a VBox with a Text and a TextArea. There\u0026rsquo;s no particular configuration to the VBox, so there no need to even instantiate it into a variable. Just create it and return it. In my opinion, the layout is so simple that there\u0026rsquo;s no need to split it apart from the configuration of the TextArea\u0026hellip;.yet.\nAdding a \u0026ldquo;Save\u0026rdquo; Button and Linking to the Outside World # We have this \u0026ldquo;data entry\u0026rdquo; section, but the data isn\u0026rsquo;t going anywhere. So we need to add a Model to get the information out of the screen. This article isn\u0026rsquo;t about MVC, so I won\u0026rsquo;t go into much detail about this, but you should be able to see how it works.\nHere\u0026rsquo;s a Model:\npublic class ExampleModel { private final StringProperty firstName = new SimpleStringProperty(\u0026#34;Alloysius\u0026#34;); private final StringProperty LastName = new SimpleStringProperty(\u0026#34;Smith\u0026#34;); private final StringProperty email = new SimpleStringProperty(\u0026#34;a_smith@reallyexpensiveisp.com\u0026#34;); private final StringProperty phone = new SimpleStringProperty(\u0026#34;(999)888-7777\u0026#34;); private StringProperty bio = new SimpleStringProperty(\u0026#34;This is the life story of Alloysius Smith\\n\\nHe was born a while ago\\n\u0026#34;); public StringProperty firstNameProperty() { return firstName; } public StringProperty lastNameProperty() { return LastName; } public StringProperty emailProperty() { return email; } public StringProperty phoneProperty() { return phone; } public StringProperty bioProperty() { return bio; } } I\u0026rsquo;ve put some default values into the properties of the Model so that we can see how the populated data looks on the screen without having to type it in each time.\nI\u0026rsquo;m not going to do anything with it other than bindings, so I haven\u0026rsquo;t included any getters or setters for the fields, just the \u0026ldquo;\u0026hellip;Property()\u0026rdquo; methods. It\u0026rsquo;s going to be passed to the ScreenBuilder class in its constructor along with a Runnable to handle the \u0026ldquo;Save\u0026rdquo; action (which isn\u0026rsquo;t going to actually do anything in this example):\npublic class NoFxmlExample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Region sceneRoot = new ScreenBuilder(new ExampleModel(), () -\u0026gt; { }).build(); Scene scene = new Scene(sceneRoot); primaryStage.setScene(scene); scene.getStylesheets().add(\u0026#34;/css/default.css\u0026#34;); primaryStage.show(); } } Now, lets link up the Model and add the Button:\npublic class ScreenBuilder implements Builder\u0026lt;Region\u0026gt; { private ExampleModel model; private Runnable saveAction; public ScreenBuilder(ExampleModel model, Runnable saveAction) { this.model = model; this.saveAction = saveAction; } public Region build() { BorderPane results = new BorderPane(); Node headingBox = createHeadingBox(); BorderPane.setMargin(headingBox, new Insets(4)); results.setTop(headingBox); results.setCenter(createDataEntrySection()); results.setBottom(createButtonBox()); return results; } private Node createHeadingBox() { HBox results = new HBox(); Text headingText = new Text(\u0026#34;This is the Screen Title\u0026#34;); headingText.getStyleClass().add(\u0026#34;heading-text\u0026#34;); results.getStyleClass().add(\u0026#34;standard-border\u0026#34;); results.getChildren().add(headingText); results.setPadding(new Insets(6)); return results; } private Node createDataEntrySection() { HBox results = new HBox(10); results.setPadding(new Insets(2, 10, 4, 2)); results.getChildren().addAll(createGridPane(), createBioBox()); return results; } private Node createBioBox() { TextArea bioTextArea = new TextArea(); bioTextArea.setWrapText(true); bioTextArea.setMaxWidth(200); bioTextArea.setMaxHeight(200); bioTextArea.textProperty().bindBidirectional(model.bioProperty()); return new VBox(4, createPromptText(\u0026#34;Short Bio:\u0026#34;), bioTextArea); } private Node createGridPane() { GridPane results = createTwoColumnGridPane(); results.add(createPromptText(\u0026#34;First Name:\u0026#34;), 0, 0); results.add(createInputField(model.firstNameProperty()), 1, 0); results.add(createPromptText(\u0026#34;Last Name:\u0026#34;), 0, 1); results.add(createInputField(model.lastNameProperty()), 1, 1); results.add(createPromptText(\u0026#34;Email:\u0026#34;), 0, 2); results.add(createInputField(model.emailProperty()), 1, 2); results.add(createPromptText(\u0026#34;Phone Number:\u0026#34;), 0, 3); results.add(createInputField(model.phoneProperty()), 1, 3); return results; } private Node createButtonBox() { HBox results = new HBox(); results.setAlignment(Pos.CENTER_RIGHT); results.setPadding(new Insets(8)); Button button = new Button(\u0026#34;Save\u0026#34;); button.setOnAction(evt -\u0026gt; saveAction.run()); results.getChildren().add(button); return results; } private GridPane createTwoColumnGridPane() { GridPane results = new GridPane(); results.getColumnConstraints().addAll(createJustifiedConstraint(HPos.RIGHT), createJustifiedConstraint(HPos.LEFT)); results.setHgap(6); results.setVgap(4); results.setPadding(new Insets(4)); results.getStyleClass().add(\u0026#34;test-border\u0026#34;); return results; } private ColumnConstraints createJustifiedConstraint(HPos alignment) { ColumnConstraints results = new ColumnConstraints(); results.setHalignment(alignment); return results; } private Node createInputField(StringProperty boundProperty) { TextField results = new TextField(); results.setMinWidth(100); results.textProperty().bindBidirectional(boundProperty); return results; } private Node createPromptText(String prompt) { Text results = new Text(prompt); results.getStyleClass().add(\u0026#34;label-text\u0026#34;); return results; } } Which looks like this:\nAs you can see, we\u0026rsquo;ve now updated createInputField() to take a StringProperty as a parameter, and it\u0026rsquo;s now bound to the Text property of the TextField. Back up in createGridPane(), it\u0026rsquo;s easy to see how each TextField is bound to a Model property.\nThe \u0026ldquo;Save\u0026rdquo; Button is wrapped in an HBox so that it can be set up at the right-hand side of the screen. You could use an AnchorPane if you want to, and then attach it to the right side. The setOnAction() method of the Button just runs the Runnable.\nOne Last Refinement # The last 4 methods in ScreenBuilder don\u0026rsquo;t really have anything to do with our entry screen at all. They\u0026rsquo;re just generic methods to configure nodes that could appear on any screen in our application. So, let\u0026rsquo;s make them static and move them to a new class, Widgets:\npublic class Widgets { static GridPane createTwoColumnGridPane() { GridPane results = new GridPane(); results.getColumnConstraints().addAll(createJustifiedConstraint(HPos.RIGHT), createJustifiedConstraint(HPos.LEFT)); results.setHgap(6); results.setVgap(4); results.setPadding(new Insets(4)); return results; } private static ColumnConstraints createJustifiedConstraint(HPos alignment) { ColumnConstraints results = new ColumnConstraints(); results.setHalignment(alignment); return results; } static Node createInputField(StringProperty boundProperty) { TextField results = new TextField(); results.setMinWidth(100); results.textProperty().bindBidirectional(boundProperty); return results; } static Node createPromptText(String prompt) { Text results = new Text(prompt); results.getStyleClass().add(\u0026#34;label-text\u0026#34;); return results; } } Now our ScreenBuilder class only has about 70 lines of executable code, it\u0026rsquo;s easy to read, and it\u0026rsquo;s easy to understand:\npublic class ScreenBuilder implements Builder\u0026lt;Region\u0026gt; { private ExampleModel model; private Runnable saveAction; public ScreenBuilder(ExampleModel model, Runnable saveAction) { this.model = model; this.saveAction = saveAction; } public Region build() { BorderPane results = new BorderPane(); Node headingBox = createHeadingBox(); BorderPane.setMargin(headingBox, new Insets(4)); results.setTop(headingBox); results.setCenter(createDataEntrySection()); results.setBottom(createButtonBox()); return results; } private Node createHeadingBox() { HBox results = new HBox(); Text headingText = new Text(\u0026#34;This is the Screen Title\u0026#34;); headingText.getStyleClass().add(\u0026#34;heading-text\u0026#34;); results.getStyleClass().add(\u0026#34;standard-border\u0026#34;); results.getChildren().add(headingText); results.setPadding(new Insets(6)); return results; } private Node createDataEntrySection() { HBox results = new HBox(10); results.setPadding(new Insets(2, 10, 4, 2)); results.getChildren().addAll(createGridPane(), createBioBox()); return results; } private Node createBioBox() { TextArea bioTextArea = new TextArea(); bioTextArea.setWrapText(true); bioTextArea.setMaxWidth(200); bioTextArea.setMaxHeight(200); bioTextArea.textProperty().bindBidirectional(model.bioProperty()); return new VBox(4, Widgets.createPromptText(\u0026#34;Short Bio:\u0026#34;), bioTextArea); } private Node createGridPane() { GridPane results = Widgets.createTwoColumnGridPane(); results.add(Widgets.createPromptText(\u0026#34;First Name:\u0026#34;), 0, 0); results.add(Widgets.createInputField(model.firstNameProperty()), 1, 0); results.add(Widgets.createPromptText(\u0026#34;Last Name:\u0026#34;), 0, 1); results.add(Widgets.createInputField(model.lastNameProperty()), 1, 1); results.add(Widgets.createPromptText(\u0026#34;Email:\u0026#34;), 0, 2); results.add(Widgets.createInputField(model.emailProperty()), 1, 2); results.add(Widgets.createPromptText(\u0026#34;Phone Number:\u0026#34;), 0, 3); results.add(Widgets.createInputField(model.phoneProperty()), 1, 3); return results; } private Node createButtonBox() { HBox results = new HBox(); results.setAlignment(Pos.CENTER_RIGHT); results.setPadding(new Insets(8)); Button button = new Button(\u0026#34;Save\u0026#34;); button.setOnAction(evt -\u0026gt; saveAction.run()); results.getChildren().add(button); return results; } } The Stylesheet # .root{ -theme-colour : #017987; -theme-deep-colour : derive(-theme-colour, -15%); -theme-font-colour : derive(-theme-colour, -50%); -theme-deep-font-colour : derive(-theme-font-colour, -50%); -theme-light-colour : derive(-theme-colour, +90%); -contrast-colour : #f89642; -contrast-deep-colour : derive(-contrast-colour, -20%); -near-black : #252535; -theme-font-family : \u0026#39;Calibri\u0026#39;; } .label-text { -fx-font-family : -theme-font-family; -fx-font-size: 17; -fx-fill: -theme-colour; -fx-font-weight: bold; } .heading-text { -fx-font-family : -theme-font-family; -fx-font-size: 24; -fx-fill: -contrast-colour; -fx-stroke: -theme-font-colour; -fx-font-weight: bold; } .standard-border { -fx-border-color : -theme-colour, -theme-deep-colour; -fx-border-insets: 0, 0 -1.4 -1.4 0; -fx-border-radius : 4px, 6px; -fx-border-width : 1.5px, 0 2px 2px 0; } .test-border { -fx-border-color : red; -fx-border-width : 0.5; } Wrapping Up # This article ended up being more about code style than JavaFX, but that doesn\u0026rsquo;t really surprise me. There\u0026rsquo;s nothing magical about JavaFX, it\u0026rsquo;s just Java.\nIt took me much longer to write the text of this article than it did to write the code.\nMuch longer.\nI wrote it and modified the code as I wrote the article, so I can\u0026rsquo;t say for sure, but my guess is that the whole finished product would have taken about one cup of coffee for me to complete from scratch. Even less if I had used my own library of configuration methods and classes (a bit like the Widgets class we just created).\nBut yet it\u0026rsquo;s not really a trivial screen. It does something. Add a proper Controller and an adapter to an external API or persistence layer and it\u0026rsquo;s a fully functional application.\nThe Methodology # As you can see, building a screen by hand in JavaFX is fairly simple.\nCreate a container, put some stuff in it and run it.\nPut some more stuff inside.\nIf the new stuff needs a layout that your current container can\u0026rsquo;t supply, then put your new stuff into its own container, and then add that to your original container.\nKeep on adding stuff to the containers, creating new containers as necessary until you\u0026rsquo;re done. Keep running it over and over to see what the changes look like.\nFinally # I hope this was helpful. I\u0026rsquo;ve seen quite a few people express confusion about how to start writing JavaFX code by hand, and there doesn\u0026rsquo;t seem to be anything out there (until now) to show them how.\nLet me know if this helps you, or if you have more questions.\nCreating a screen is just one part of building an application, and you\u0026rsquo;ll need to apply a framework to get to the finish line. If you want to use Model-View-Controller with JavaFX, you can carry on with this article here.\n","date":"24 August 2021","externalUrl":null,"permalink":"/javafx/techniques/nofxml/layouts-without-fxml/","section":"JavaFX","summary":"I’ve always advised that Scene Builder and FXML are a waste of time, and that you should just write your screens in Java code.\nIt’s easier to build, it’s easier to customize, easier to maintain, and much, much easier to do really sophisticated things. But how do you start?","title":"How to Write a JavaFX Screen without FXML \u0026 Scene Builder","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # How to use the Button class. Some principles of good programming The Single Responsibility Principle Avoiding excess coupling How to organize your layout code to make it easy to understand Events How to use Bindings Adding User Interaction # Two of the most common tools for user interaction and input are TextFields and Buttons. We\u0026rsquo;re going to add both of these two our application to make it actually do something.\nThe Code So Far # public class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContent(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContent() { HBox results = new HBox(new Label(\u0026#34;Name:\u0026#34;), new TextField(\u0026#34;\u0026#34;)); results.setSpacing(6); results.setPadding(new Insets(0,0,0,50)); results.setAlignment(Pos.CENTER_LEFT); return results; } } Adding the Output and a Button # Now we are going to add some interaction to the application. We\u0026rsquo;ll add a Button and put the output Label back on the screen. We\u0026rsquo;ll set up the Button so that when it\u0026rsquo;s clicked, it loads \u0026ldquo;Hello \u0026quot; plus the name into the output Label.\nThe code now looks like this:\npublic class Main extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContent(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContent() { TextField inputTextField = new TextField(\u0026#34;\u0026#34;); HBox inputRow = new HBox(new Label(\u0026#34;Name:\u0026#34;), inputTextField); inputRow.setSpacing(6); inputRow.setAlignment(Pos.CENTER); Label outputLabel = new Label(\u0026#34;\u0026#34;); Button actionButton = new Button(\u0026#34;Hello\u0026#34;); actionButton.setOnAction(evt -\u0026gt; outputLabel.setText(\u0026#34;Hello \u0026#34; + inputTextField.getText())); VBox results = new VBox(20, inputRow, outputLabel, actionButton); results.setAlignment(Pos.CENTER); return results; } } I\u0026rsquo;ve wrapped the entire content in a VBox, which presents the contained Nodes vertically stacked on the screen. Then I\u0026rsquo;ve added the Label for the output (called outputLabel), and the Button to trigger the work (called actionButton). An EventHandler was added to the actionButton to take an ActionEvent which will trigger an update on the contents of the outputLabel.\nAnd the output looks almost like this:\nI\u0026rsquo;ve put a green border around the outer VBox and the red border remains around the HBox.\nButton Actions # There\u0026rsquo;s something new in this code:\nButton actionButton = new Button(\u0026#34;Hello\u0026#34;); actionButton.setOnAction(evt -\u0026gt; outputLabel.setText(\u0026#34;Hello \u0026#34; + inputTextField.getText())); What\u0026rsquo;s this all about?\nIt\u0026rsquo;s best to think of a Button as a nothing more than a trigger for an action. You can do other things with them, but for now let\u0026rsquo;s just stick to this simplest use.\nMost of the JavaFX classes can trigger \u0026ldquo;Events\u0026rdquo;. Events are a way for these classes to signal that something significant has happened to them. Maybe they\u0026rsquo;ve changed some kind of state, or received user input - these are things that cause Events to be fired. For every Event that can be triggered, you can define an EventHandler, which is a piece of code that will be executed when that Event is fired.\nThere are lots and lots of Events that are constantly fired in JavaFX and the vast majority of them have no associated EventHandler and won\u0026rsquo;t cause anything to happen. For instance, if you move the cursor around a JavaFX window, you\u0026rsquo;ll cause hundreds of mouse events to be fired, and virtually none of them will result in any EventHandlers being invoked.\nWhen you think of a Button as a trigger, then there is one type of Event that is supremely important, the \u0026ldquo;OnAction\u0026rdquo; Event. OnAction is fired when the user clicks and releases the mouse over the Button, or when the user hits the \u0026ldquo;Enter\u0026rdquo; key when the Button is \u0026ldquo;default\u0026rdquo; or has focus. In other words, when the Button is activated.\nThe way to set up an EventHandler for the OnAction event is to call Button.setOnAction(). The single parameter for this method is an EventHandler.\nEventHandler is a functional interface that has a single method called handle(Event event). Since it\u0026rsquo;s a functional interface, we can define it using a lambda function, which is what we\u0026rsquo;ve done in our code so far.\nIf you are treating a Button as nothing more than a trigger for an action, then you probably won\u0026rsquo;t ever reference the Event that was fired in your EventHandler. But we still need to pass it in, as handle() expects it.\nIn this case, we\u0026rsquo;re simply going to update the text in outputLabel with a String that\u0026rsquo;s built up of some static text and the name from our TextField.\nThere\u0026rsquo;s a Lot Wrong with this code # It\u0026rsquo;s important that you learn how to write clean, clear layout code or your applications will become very difficult to understand and maintain. Even with the smallest of applications, it\u0026rsquo;s a good idea to get into the habit of making your layout code as clean as possible.\nLet\u0026rsquo;s take a look at the problems with the code we\u0026rsquo;ve written so far:\nIt\u0026rsquo;s Getting Busy # This method no longer follows the \u0026ldquo;Single Responsibility Principle\u0026rdquo;. It does the following:\nPopulates the input row and configures its layout Creates the output Label Creates the Button Defines the code which updates the output Label It\u0026rsquo;s often very easy to come up with a definition of your code that describes something that seems to be a \u0026ldquo;single responsibility\u0026rdquo;, yet isn\u0026rsquo;t really. In this case we have createContent(), which does one thing - creates the content. Except it actually does a lot of stuff, doesn\u0026rsquo;t it?\n\u0026ldquo;Single Responsibility\u0026rdquo; really refers to a direct responsibility. A method can \u0026ldquo;do\u0026rdquo; a lot of things by delegating them to other methods. Then its single responsibility is to bring them all together.\nThe trick is to break your code down into pieces which do one thing that can\u0026rsquo;t be broken down any further, or where breaking down further would make the code less clear. Then you\u0026rsquo;re following the \u0026ldquo;Single Responsibility Principle\u0026rdquo;.\nThere\u0026rsquo;s Way Too Much Coupling # This is a big problem with our code, and something that we need to fix right away, before it gets out of hand.\nWhat is Coupling, and Why is it Bad? # Coupling is when components of your application rely on the implementations of other components of your application. This results in a situation where two or more components are dependent on the structure of each other, making it difficult to modify one component without needing to modify another component. And, if that second, dependent, component has other components that are dependent on it\u0026hellip; You see where this goes.\nCoupling is the thing that you need to fight every step of the way when you\u0026rsquo;re building an application because it sneaks in almost every time you stop looking for it. So get into the habit of looking for coupling and avoiding it constantly.\nWhere\u0026rsquo;s the Coupling Here? # actionButton calls inputTextField.getText() This is a big problem. These two elements are now totally coupled. Let\u0026rsquo;s say that inputTextField is changed to some other type of Node that doesn\u0026rsquo;t have a getText() method. Maybe the application is intended to be used in a situation where there are a limited number of users, and the name is picked using a ComboBox, which doesn\u0026rsquo;t have a getText() method. Then you\u0026rsquo;ll need to update the Button as well. actionButton calls outputLabel.setText() This is the same type of coupling as above. Our implementation of the EventHandler is dependent on the implementation of the output display. actionButton needs to access both outputLabel and inputTextField These elements are coupled in the code. Any attempt to move the instantiation of either of these two Nodes into another method is going to cause trouble with actionButton. The update of outputLabel happens inside the EventHandler definition. The logic about updating the contents of the Label are now encapsulated inside the EventHandler. It\u0026rsquo;s not the worst example of coupling, but it can be problematic. All of the code is in the same scope This is a direct result of violating the Single Responsibility Principle. Everything is in the same scope and references between elements of the screen are inter-related. Fixing the Coupling and Holding to the Single Responsibility Principle # Let\u0026rsquo;s look at some code that will do this:\npublic class Main extends Application { public static void main(String[] args) { launch(args); } private final StringProperty greeting = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty name = new SimpleStringProperty(\u0026#34;\u0026#34;); @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContent(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContent() { VBox results = new VBox(20, createInputRow(), createOutputLabel(), createGreetingButton()); results.setAlignment(Pos.CENTER); return results; } private Node createGreetingButton() { Button results = new Button(\u0026#34;Hello\u0026#34;); results.setOnAction(evt -\u0026gt; setGreeting()); return results; } private Node createInputRow() { TextField textField = new TextField(\u0026#34;\u0026#34;); textField.textProperty().bindBidirectional(name); HBox hBox = new HBox(6, new Label(\u0026#34;Name:\u0026#34;), textField); hBox.setAlignment(Pos.CENTER); return hBox; } private Node createOutputLabel() { Label results = new Label(\u0026#34;\u0026#34;); results.textProperty().bind(greeting); return results; } private void setGreeting() { greeting.set(\u0026#34;Hello \u0026#34; + name.get()); } } StringProperty??? # The first thing you\u0026rsquo;ll notice is that we\u0026rsquo;ve added two StringProperty's fields to the class. Together these two Properties are going to act as our \u0026ldquo;State\u0026rdquo; for the application. One of them, the name will hold whatever the user types into the TextField and the other will hold the greeting that will display in the output Label.\nPutting these two Properties into the class allows all of the other coupling to be removed. It\u0026rsquo;s also our first baby step towards \u0026ldquo;Reactive Programming\u0026rdquo;.\ncreateContents() Now Just Sets Up the Main VBox # At a glance, you can now see that createContents() is going to create a VBox with 3 things centred in it: a place for input, a place for output and a Button.\nThis method still \u0026ldquo;creates the contents\u0026rdquo; for the screen, but it delegates the creation of the components to builder methods. You can see that the \u0026ldquo;Single Responsibility Principle\u0026rdquo; really refers to direct responsibility, and bringing together the results of several delegated responsibilities is, in fact, a single responsibility.\nAll of the various Nodes Now Have Generic Names # Since the layout of the screen has now been split into tiny, clearly named methods, there\u0026rsquo;s no value in having meaningful variable names for the individual Nodes. The scope of each of these variables is so small now that there\u0026rsquo;s no added clarity in having complicated names for them, and simple names like hBox should indicate to any reader that these have a limited scope.\nAll of the Builder Methods Have Meaningful Names # On the other hand, the builder methods now have names like createInputBox() and createOutputLabel(). It\u0026rsquo;s clear what they do, and makes it easy to find the method that you\u0026rsquo;re interested in if you\u0026rsquo;re reading, enhancing or modifying this code.\nAll of the Builder Methods Return Node or Region # This is a very important concept to understand. If your builder method needs to return a very specific type of Node, then you\u0026rsquo;re probably doing it wrong.\nLet\u0026rsquo;s take createOutputLabel() as an example. As the code stands, you could return a Label instead of a Node and it would work. But if you do that, then it\u0026rsquo;s possible to something very Label specific, like set the font, inside of createContents(). That\u0026rsquo;s a potential issue, and certainly a \u0026ldquo;code smell\u0026rdquo;, because the configuration of the Label really belongs inside createOutputLabel().\nNow let\u0026rsquo;s say that you wanted to do something to the return value of createOutputLabel(). Perhaps you want to have the \u0026ldquo;Hello\u0026rdquo; in a different font from the name. The best way to do that might be to split the text between two Labels, and put them into an HBox and return the HBox.\nNow you have a problem since createContents() is expecting a Label so that it can mess with its font. But you want to return an HBox instead. This means you have to refactor createContents() in order to change things that should just be the responsibility of createOutputLabel().\nThe EventHandler in the Button is Just a Trigger to call setGreeting() # Even though the code in setGreeting() is just one line, now the action in the EventHandler has a name, \u0026ldquo;Set Greeting\u0026rdquo; which makes it easier to read the intent of the EventHandler at a glance.\nBinding # The last thing to note about this code are these two lines:\nresults.textProperty().bind(greeting); and:\ntextField.textProperty().bindBidirectional(name); What are these two functions bind() and bindBidirectional()?\nOne of the key components of JavaFX is a library of classes that are called \u0026ldquo;Observable\u0026rdquo;. These are all wrapper classes that hold particular kinds of values and notify other objects when their contents change.\nThe main types of observable classes are called Properties. There are different types of Properties for different types of content. In this example we have used StringProperty, which is an Observable wrapper around a String value.\nOne of the key ways to use this observability is with something called Binding. Binding allows you to link two Properties together, so that when one changes, the other will change too. This can be in one direction, or bidirectional.\nBoth Label and TextField have an internal property called Text. This holds the contents of the Node, and whatever is in this property will be displayed on the screen. Label is output only, so we\u0026rsquo;ve bound its Text Property to our greeting Property, while TextField allows user input, so we\u0026rsquo;ve connected it to name bidirectionally.\nBinding is the critical component that allows us to decouple all of the elements of our layout and create a data representation of the \u0026ldquo;State\u0026rdquo; of our GUI. It\u0026rsquo;s really the only way that you should be updating any non-static Nodes in your layout. Get comfortable with Binding, because you are going to be using it a lot.\n","externalUrl":null,"permalink":"/beginners/part3/","section":"JavaFX","summary":"GUI applications need a way for the user to interact with them.  In this lesson we’ll show how to allow user input with TextField, and to launch an action with a Button.","title":"Part 3 - User Interaction","type":"javafx"},{"content":" Introduction # One of the key paradigms of JavaFX is that styling should be done in an external style sheet - a CSS file. JavaFX supplies a large number of standard styling attributes that apply to the Node classes when they make sense. This includes padding, background parameters, border parameters, min/max/pref sizes and a host of other things.\nIt could be argued that anything that can be styled via CSS should be styled by CSS. Personally, I tend to use CSS for everything except padding and spacing, which feel to me more like layout than styling, and are very convenient to customize programmatically. Spacing is even a constructor parameter for HBox and VBox.\nBut what about aspects that aren\u0026rsquo;t covered by the standard JavaFX attributes?\nIt turns out that JavaFX exposes to application programmers all of the tools that you need to make your own styleable attributes. In fact, if you look at the source code for those standard attributes, you\u0026rsquo;ll find that they don\u0026rsquo;t do anything that you can\u0026rsquo;t do yourself in your applications.\nHowever, the JavaDocs for the classes that you\u0026rsquo;ll need to use are somewhat less than clear, and seem to be very complicated. In this article, we\u0026rsquo;ll take a step by step approach to implementing some custom styleable attributes, understand what\u0026rsquo;s happening, and then look at how you can deviate from the \u0026ldquo;best practices\u0026rdquo; described in the JavaDocs to make it easier to implement custom styling in your layouts.\nFinally, we\u0026rsquo;ll look at how you should go about implementing custom styling in a custom Node subclass that you create for general purpose use.\n{% include notice_kotlin %}\nCustom Styling in a Layout # Let\u0026rsquo;s take a look at a simple example that can be done with standard styling and then look at why you might want to implement custom styleable properties\u0026hellip;\nUsing Traditional Styling # class Styleable0 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent()).apply { Styleable0::class.java.getResource(\u0026#34;styleable0.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = CustomBox() padding = Insets(10.0) } } class CustomBox : HBox(20.0) { init { styleClass += \u0026#34;custom-widget\u0026#34; children += Circle(100.0).apply { styleClass += \u0026#34;circle\u0026#34; } children += Rectangle(200.0, 200.0).apply { styleClass += \u0026#34;square\u0026#34; } } } fun main() = Application.launch(Styleable0::class.java) Here\u0026rsquo;s the style sheet:\n.custom-widget { -fx-padding: 10px; -fx-border-width: 3px; -fx-border-color: green; -fx-background-color: beige; } .custom-widget .circle { -fx-fill: indianred; } .custom-widget .square { -fx-fill: darkseagreen; } And it looks like this:\nI\u0026rsquo;ve implemented this as a custom class extending HBox to emphasize a few points:\nYou\u0026rsquo;d do this when you want to reuse this component over and over. You\u0026rsquo;d probably want to limit the client code\u0026rsquo;s ability to mess with the internal workings of it. It doesn\u0026rsquo;t stop client code from messing with its internal workings. The first thing is that, as an extension of HBox, it exposes quite a few methods that client code can use to mess around with it. Most importantly, it exposes Pane.getChildren() which means that client code can get references to all of its component parts, and even add or remove them. Additionally, the presence of the CSS selectors for the component parts means that client code can alter any styleable aspect of those components, not just the colour. There\u0026rsquo;s not even an implied understanding that only the colours should be customized.\nHow do we fix this?\nLocking Down the Structure # The first thing we can do is to change the base class of our custom component from HBox to Region. This is because Region exposes a lot less public methods. Most importantly, Region.getChildren() is protected, unlike Pane.getChildren(), which is public. This means that our custom class\u0026rsquo;s code can call getChildren() but client code cannot. So now our custom class looks like this:\nclass CustomBox1 : Region() { init { children += HBox(20.0).apply { styleClass += \u0026#34;custom-widget\u0026#34; children += Circle(100.0).apply { styleClass += \u0026#34;circle\u0026#34; } children += Rectangle(200.0, 200.0).apply { styleClass += \u0026#34;square\u0026#34; } } } } And, of course, it still looks like this:\nThis is our starting point. We have a layout, and now we need to provide a means for client code and style sheets to control the colours of the shapes without letting it muck about with any other aspect of those components.\nWe do this through custom StyleableProperties.\nWe\u0026rsquo;ll start with just the circle, and when that\u0026rsquo;s working, then we\u0026rsquo;ll duplicate it for the square.\nThe first thing we need is a Property to store the colour. JavaFX gives us a special set of Properties that we can use to link back to a style sheet. We will use StyleableObjectProperty here:\nAs you can see, the parameter CIRCLE_COLOUR_META_DATA is red, meaning that it hasn\u0026rsquo;t been defined yet. It\u0026rsquo;s also in all caps with underscores, hinting that it might be a static constant - which it is.\nThis is where the JavaDocs just dump a whole pantload of details on you without any explanations and quickly get confusing. So we\u0026rsquo;ll go through this one piece at a time, and explain what\u0026rsquo;s happening.\nA StyleableProperty needs a way to link back to the CSS file, and this is done with something called CssMetaData, which is what CIRCLE_COLOUR_META_DATA is. Well, actually it will be an anonymous inner class extending CssMetaData.\nFirst off, CssMetaData is a generic class. It has two types, one is the type of the Node that contains the StyleableProperty (actually it\u0026rsquo;s any class that implements the Styleable interface) and the other is type of the data contained in the StyleableProperty. In our case, this is CustomBox2 and Color.\nLet\u0026rsquo;s look at the standard constructor for CssMetaData. It has two parameters:\nA String which is the attribute tag in the CSS file. A converter that will take the attribute value in the CSS file and convert it into the same data type as is held in the associated StyleableProperty. This second item is usually provided by a standard converter which is part of the JavaFX library. You can get an instance of one of these by calling a method in the StyleConverter class. There is one for each of the kind of values that you are likely to need, including standard JavaFX types like Color, Font and Duration. In our case we\u0026rsquo;ll use:\nCssMetaData(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter())` This constructor provides the essential connection to the CSS file.\nMoving on, CssMetaData has two abstract methods that need to be supplied by any extending class:\nA method that returns whether or not the value of the property can be set from the style sheet. A method that returns the associated StyleableProperty. Remember that CssMetaData is defined statically, but the StyleableProperty is an instance variable, or field, of the class. This means that we cannot just reference the StyleableProperty directly. But both of these methods take an instance of the class as a parameter, so we can reference the field through that parameter.\nThe official documentation treats these methods like they are just standard boilerplate that you can just copy and paste without thinking about them. But we\u0026rsquo;ll think about them here\u0026hellip;\nYou cannot set a Property that has been bound. That would generate a runtime error and would be bad. So the standard answer to whether a StyleableProperty can be set from the CSS file is just to check if it has been bound. You may have other criteria that you care about, and you\u0026rsquo;ll have to decide if just checking if the StyleableProperty is bound is enough for your situation.\nThe standard method for returning the StyleableProperty is to hard-code it in the method body. 99% of the time, this is what you are going to want to do. However, there\u0026rsquo;s no reason you have to stick to this. Let\u0026rsquo;s say that you have 7 boxes in your layout, one for each day of the week, and you want the box for today to be styled a certain way. You could have a List of StyleableProperties and you could use a DOW calculation to pick one.\nFor now, at least, we are going to stick with the standard implementation:\nval circleColour: StyleableObjectProperty\u0026lt;Color\u0026gt; = SimpleStyleableObjectProperty(CIRCLE_COLOUR_META_DATA, Color.INDIANRED) companion object CssStuff { val CIRCLE_COLOUR_META_DATA: CssMetaData\u0026lt;CustomBox2, Color\u0026gt; = object : CssMetaData\u0026lt;CustomBox2, Color\u0026gt;(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: CustomBox2): Boolean = !p0.circleColour.isBound override fun getStyleableProperty(p0: CustomBox2): StyleableProperty\u0026lt;Color\u0026gt; = p0.circleColour } } In Kotlin, a companion object is a way of encapsulating static elements. They aren\u0026rsquo;t really \u0026ldquo;static\u0026rdquo;, but more like singletons, but otherwise behave just like static elements. You can reference them exactly the same way that you would in Java. I like the companion object structure because it segregates the static elements from the instance members of the class.\nIn the JavaDocs, the next steps are generally intended for creating classes that you intend to be extended at some point. In such a case, you want the CssMetaData to be heritable and you need to set everything up properly. But in our case, where we are just building a \u0026ldquo;one-off\u0026rdquo;, and all that stuff would just be \u0026ldquo;dotting i\u0026rsquo;s and crossing t\u0026rsquo;s\u0026rdquo; for the sake of formality. So we can forgo it here.\nWe need a way for the underlying JavaFX code to see our new CssMetaData and incorporate it into its styling information. The Styleable interface has a method called getCssMetaData() that we can override for this purpose. We need to integrate our new CssMetaData into the list that the parent class, in this case Region, already has:\noverride fun getCssMetaData(): MutableList\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt; = (super.getCssMetaData() + CIRCLE_COLOUR_META_DATA) as MutableList Finally, we need to use this new Property in the layout:\nchildren += Circle(100.0).apply { fillProperty().bind(circleColour) } To summarize:\nCreate a StyleableProperty of the appropriate type, and reference a static CssMetaData class that you will create. Create a static instance of CssMetaData that matches your new StyleableProperty Add your new CssMetaData instance to the a list that will be returned from getCssMetaData() Use the new StyleableProperty in the layout somehow. Our complete code looks like this:\nclass CustomBox2 : Region() { private val circleColour: StyleableObjectProperty\u0026lt;Color\u0026gt; = SimpleStyleableObjectProperty(CIRCLE_COLOUR_META_DATA, Color.WHITE) private val squareColour: StyleableObjectProperty\u0026lt;Color\u0026gt; = SimpleStyleableObjectProperty(SQUARE_COLOUR_META_DATA, Color.WHITE) companion object CssStuff { val CIRCLE_COLOUR_META_DATA: CssMetaData\u0026lt;CustomBox2, Color\u0026gt; = object : CssMetaData\u0026lt;CustomBox2, Color\u0026gt;(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: CustomBox2): Boolean = !p0.circleColour.isBound override fun getStyleableProperty(p0: CustomBox2): StyleableProperty\u0026lt;Color\u0026gt; = p0.circleColour } val SQUARE_COLOUR_META_DATA: CssMetaData\u0026lt;CustomBox2, Color\u0026gt; = object : CssMetaData\u0026lt;CustomBox2, Color\u0026gt;(\u0026#34;-wfx-square-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: CustomBox2): Boolean = !p0.squareColour.isBound override fun getStyleableProperty(p0: CustomBox2): StyleableProperty\u0026lt;Color\u0026gt; = p0.squareColour } } override fun getCssMetaData() = (getClassCssMetaData() + CIRCLE_COLOUR_META_DATA + SQUARE_COLOUR_META_DATA) as MutableList init { styleClass += \u0026#34;custom-widget\u0026#34; children += HBox(20.0).apply { styleClass += \u0026#34;inner-box\u0026#34; children += Circle(100.0).apply { fillProperty().bind(circleColour) } children += Rectangle(200.0, 200.0).apply { fillProperty().bind(squareColour) } } } } Using an Anonymous Inner Class # All of the documentation in the JavaDocs are oriented towards creating a resuable, possibly extendable, named custom class. This means that the approach that\u0026rsquo;s documented goes through all of the ceremony required to ensure that things are done \u0026ldquo;by the book\u0026rdquo;.\nBut, is it possible to just create a StyleableProperty and CssMetaData in the layout code?\nThe answer, unfortunately, is no. This is because Node.getCssMetaData() (or something that underlies it) needs to be overriden in order to activate any custom CssMetaData that we create. And the only way to do this is to extend whatever Node subclass that we\u0026rsquo;re working with.\nThe next problem that we have is that we cannot declare companion objects (or, in Java, static members) for inner classes. This shouldn\u0026rsquo;t be a huge problem. If our layout element that we\u0026rsquo;re creating the custom styling for is really a \u0026ldquo;one-off\u0026rdquo;, then it shouldn\u0026rsquo;t make too big a difference if we create the custom CssMetaData in a non-static way.\nThe next issue that we need to deal with is this:\noverride fun isSettable(p0: CustomBox2): Boolean = !p0.squareColour.isBound If our layout element is an anoymous inner class, then we don\u0026rsquo;t have a way to specify the type of p0. We could try this variation:\noverride fun isSettable(p0: Styleable): Boolean = !(p0 as CustomBox2).squareColour.isBound That\u0026rsquo;s going to let us create a method that\u0026rsquo;s compatible with the inner workings of JavaFX, but we\u0026rsquo;re still stuck with the as CustomBox2 because we don\u0026rsquo;t have a name for our anonymous inner class. So we cannot do this.\nBut\u0026hellip;\nThis is a one-off, right? So we should be able to just create a StyleableProperty and then just reference it directly. No need to create it as a field of our anonymous inner class get it from there. We could do something like this:\nval circleColour: StyleableObjectProperty\u0026lt;Color\u0026gt; = SimpleStyleableObjectProperty\u0026lt;Color\u0026gt;(circleColourMetaData,Color.BLUEVIOLET) val circleColourMetaData: CssMetaData\u0026lt;Styleable, Color\u0026gt; = object : CssMetaData\u0026lt;Styleable, Color\u0026gt;(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: Styleable): Boolean = !circleColour.isBound override fun getStyleableProperty(p0: Styleable): StyleableProperty\u0026lt;Color\u0026gt; = circleColour } Here, we are just referencing circleColour. We don\u0026rsquo;t care about p0 because it can only ever be one thing, we only ever want to return circleColour.\nBut\u0026hellip;\nThere\u0026rsquo;s a problem. In my IDE, it looks like this:\nI cannot refer to circleColourMetaData yet because it\u0026rsquo;s declaration comes later in the code. And I can\u0026rsquo;t just flip the declarations around, because those two functions need to refer to circleColour. So I\u0026rsquo;m stuck.\nBut\u0026hellip;\nWe need a class that extends StyleableObjectProperty but there\u0026rsquo;s nothing that says that we have to use SimpleStyleableObjectProperty. And it\u0026rsquo;s only SimpleStyleableObjectProperty's implementation that is demanding that we supply the corresponding CssMetaData in its constructor.\nIt turns out that, just like SimpleObjectProperty, SimpleStyleableObjectProperty doesn\u0026rsquo;t actually do much. It satisfies the interface requirements for the \u0026ldquo;Bean\u0026rdquo; stuff, and provides the constructors to populate the Bean data\u0026hellip;but that\u0026rsquo;s about all. The one extra thing it does is to provide the getCssMetaData() method, and initialize it via the constructor.\nThis means that it\u0026rsquo;s trivial to create a version which doesn\u0026rsquo;t take the CssMetaData in the constructor, but rather exposes a setCssMetaData() method that can be called later. I\u0026rsquo;ve called it DeferredStyleableObjectProperty:\nclass DeferredStyleableObjectProperty\u0026lt;T\u0026gt;( private val bean: Any?, private val name: String, initialValue: T? = null ) : StyleableObjectProperty\u0026lt;T\u0026gt;() { private var cssMetaData: CssMetaData\u0026lt;out Styleable?, T\u0026gt;? = null constructor() : this(null, \u0026#34;\u0026#34;, null) constructor(initialValue: T) : this(null, \u0026#34;\u0026#34;, initialValue) init { initialValue?.let { super.setValue(it) } } override fun getBean(): Any? { return this.bean } override fun getName(): String { return this.name } override fun getCssMetaData(): CssMetaData\u0026lt;out Styleable?, T\u0026gt;? = cssMetaData fun setCssMetaData(newVal: CssMetaData\u0026lt;out Styleable?, T\u0026gt;) { if (cssMetaData == null) cssMetaData = newVal } } As you can see, it\u0026rsquo;s pretty simple. One thing to watch out for is that it would probably be very bad to change the value of cssMetaData after it has been set. I\u0026rsquo;ve provided some protection against this, and it will ignore any attempts to set cssMetaData if its current value is not null.\nNow we can do this:\nfun customBox3(): Region { val circleColour = DeferredStyleableObjectProperty(Color.BLUEVIOLET) val circleColourMetaData: CssMetaData\u0026lt;Styleable, Color\u0026gt; = object : CssMetaData\u0026lt;Styleable, Color\u0026gt;(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: Styleable): Boolean = !circleColour.isBound override fun getStyleableProperty(p0: Styleable): StyleableProperty\u0026lt;Color\u0026gt; = circleColour } circleColour.setCssMetaData(circleColourMetaData) val squareColour = DeferredStyleableObjectProperty(Color.BLUEVIOLET) val squareColourMetaData: CssMetaData\u0026lt;Styleable, Color\u0026gt; = object : CssMetaData\u0026lt;Styleable, Color\u0026gt;(\u0026#34;-wfx-square-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: Styleable): Boolean = !squareColour.isBound override fun getStyleableProperty(p0: Styleable): StyleableProperty\u0026lt;Color\u0026gt; = squareColour } squareColour.setCssMetaData(squareColourMetaData) return object : HBox(20.0) { override fun getCssMetaData(): MutableList\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt; = (getClassCssMetaData() + circleColourMetaData + squareColourMetaData) as MutableList }.apply { styleClass += \u0026#34;custom-widget\u0026#34; children += Circle(100.0).apply { fillProperty().bind(circleColour) } children += Rectangle(200.0, 200.0).apply { fillProperty().bind(squareColour) } } } You can see that we create the StyleableProperties using the new DeferredStyleableObjectProperty class, then declare the CssMetaData using those DeferredStyleableObjectProperties, and then we call DeferredStyleableObjectProperty.setCssMetaData() to complete the initialization.\nWe still need to extend our class in order to override getCssMetaData(), but then we can just use .apply{} to complete the rest of the layout. However, those StyleableProperties don\u0026rsquo;t have to be declared in this method, they could be fields in the ViewBuider itself:\nclass Styleable3 : Application() { private val circleColour = DeferredStyleableObjectProperty(Color.BLUEVIOLET) private val circleColourMetaData: CssMetaData\u0026lt;Styleable, Color\u0026gt; = object : CssMetaData\u0026lt;Styleable, Color\u0026gt;(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: Styleable): Boolean = !circleColour.isBound override fun getStyleableProperty(p0: Styleable): StyleableProperty\u0026lt;Color\u0026gt; = circleColour init { circleColour.setCssMetaData(this) } } private val squareColour = DeferredStyleableObjectProperty(Color.BLUEVIOLET) private val squareColourMetaData: CssMetaData\u0026lt;Styleable, Color\u0026gt; = object : CssMetaData\u0026lt;Styleable, Color\u0026gt;(\u0026#34;-wfx-square-colour\u0026#34;, StyleConverter.getColorConverter()) { override fun isSettable(p0: Styleable): Boolean = !squareColour.isBound override fun getStyleableProperty(p0: Styleable): StyleableProperty\u0026lt;Color\u0026gt; = squareColour init { squareColour.setCssMetaData(this) } } override fun start(stage: Stage) { stage.scene = Scene(createContent()).apply { Styleable3::class.java.getResource(\u0026#34;styleable3.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = customBox3() padding = Insets(10.0) } private fun customBox3(): Region = object : HBox(20.0) { override fun getCssMetaData(): MutableList\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt; = (getClassCssMetaData() + circleColourMetaData + squareColourMetaData) as MutableList }.apply { styleClass += \u0026#34;custom-widget\u0026#34; children += Circle(100.0).apply { fillProperty().bind(circleColour) } children += Rectangle(200.0, 200.0).apply { fillProperty().bind(squareColour) } } } fun main() = Application.launch(Styleable3::class.java) We don\u0026rsquo;t have an official ViewBuilder in this simple example, so we\u0026rsquo;re just using Application itself, but you can see how it would work. I like this coding arrangement. The StyleableProperties and the CssMetaData are more like utility classes in this implementation, and they are out of the layout code and don\u0026rsquo;t clutter it up. The layout code now looks very much like it did before, and it\u0026rsquo;s fairly easy to read.\nYou\u0026rsquo;ll just have to make sure that you don\u0026rsquo;t re-use that CssMetaData in another part of the layout.\nThe CSS looks like this:\n.custom-widget { -wfx-circle-colour: indianred; -wfx-square-colour: teal; -fx-border-color: blue; -fx-border-width: 3px; -fx-padding: 5px; } Encapsulated CssMetaData # This gymnastics with the delayed initialization of the CssMetaData in the StyleableProperty goes away if you encapsulate the CssMetaData definition in the StyleableProperty itself. If you make the not unreasonable decision that the Java Bean stuff should be ignored, then you can keep the constructor simple. Like this:\nclass StandardStyleableObjectProperty\u0026lt;T\u0026gt;( attributeName: String, styleConverter: StyleConverter\u0026lt;*, T\u0026gt;, initialValue: T? = null ) : StyleableObjectProperty\u0026lt;T\u0026gt;() { private var cssMetaData: CssMetaData\u0026lt;out Styleable, T\u0026gt; = object : CssMetaData\u0026lt;Styleable, T\u0026gt;(attributeName, styleConverter) { override fun isSettable(p0: Styleable): Boolean = !this@StandardStyleableObjectProperty.isBound override fun getStyleableProperty(p0: Styleable): StyleableProperty\u0026lt;T\u0026gt; = this@StandardStyleableObjectProperty } init { initialValue?.let { super.setValue(it) } } override fun getBean(): Any? = null override fun getName(): String = \u0026#34;Styleable Property\u0026#34; override fun getCssMetaData(): CssMetaData\u0026lt;out Styleable, T\u0026gt; = cssMetaData } I\u0026rsquo;ve called this StandardStyleableObjectProperty because it uses the default !this@StandardStyleableObjectProperty.isBound to respond to isSettable(). If you want to generalize this class, you\u0026rsquo;d need to provide a constructor parameter that takes a Function or Supplier to use here. It\u0026rsquo;s also pretty simple, coming in at around a dozen lines of code.\nOnce you\u0026rsquo;ve created this StandardStyleableObjectProperty class, the layout code gets much simpler:\nclass Styleable4 : Application() { private val circleColour = StandardStyleableObjectProperty(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter(), Color.BLUEVIOLET) private val squareColour = StandardStyleableObjectProperty(\u0026#34;-wfx-square-colour\u0026#34;, StyleConverter.getColorConverter(), Color.BLUEVIOLET) override fun start(stage: Stage) { stage.scene = Scene(createContent()).apply { Styleable3::class.java.getResource(\u0026#34;styleable3.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = customBox3() padding = Insets(10.0) } private fun customBox3(): Region = object : HBox(20.0) { override fun getCssMetaData(): MutableList\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt; = (getClassCssMetaData() + circleColour.cssMetaData + squareColour.cssMetaData) as MutableList }.apply { styleClass += \u0026#34;custom-widget\u0026#34; children += Circle(100.0).apply { fillProperty().bind(circleColour) } children += Rectangle(200.0, 200.0).apply { fillProperty().bind(squareColour) } } } fun main() = Application.launch(Styleable4::class.java) Now, the custom StyleableProperty hardly takes up any space in your mind when you read the code.\nRemoving the Anonymous Inner Class # The need to extend HBox in order to implement the modified getCssMetaData() is the biggest problem with the code readability. It would be nicer if there was some sort of setLayoutCssMetaData() method that we could use to inject our custom StyleableProperties into our HBox. Like this:\nclass StyleableHBox(spacing: Double) : HBox(spacing) { val layoutCssMetaData = mutableListOf\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt;() override fun getCssMetaData(): MutableList\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt; { return (super.getCssMetaData() + layoutCssMetaData) as MutableList } } We can use this new class this way:\nclass Styleable5 : Application() { private val circleColour = StandardStyleableObjectProperty(\u0026#34;-wfx-circle-colour\u0026#34;, StyleConverter.getColorConverter(), Color.BLUEVIOLET) private val squareColour = StandardStyleableObjectProperty(\u0026#34;-wfx-square-colour\u0026#34;, StyleConverter.getColorConverter(), Color.BLUEVIOLET) override fun start(stage: Stage) { stage.scene = Scene(createContent()).apply { Styleable3::class.java.getResource(\u0026#34;styleable3.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = customBox() padding = Insets(10.0) } private fun customBox(): Region = StyleableHBox(20.0).apply { layoutCssMetaData += listOf(circleColour.cssMetaData, squareColour.cssMetaData) styleClass += \u0026#34;custom-widget\u0026#34; children += Circle(100.0).apply { fillProperty().bind(circleColour) } children += Rectangle(200.0, 200.0).apply { fillProperty().bind(squareColour) } } } fun main() = Application.launch(Styleable5::class.java) This achieves all of the objectives. The layout code now looks like bog standard layout code and nothing gets in the way of understanding it at a glance. The custom class StyleableHBox is generic enough that it could be put into a library and used over and over. For me, and the way that I write layouts, I\u0026rsquo;d probably never need to deal with any of the other HBox constructors, but a rigorous implementation would have to handle them as well.\nShould You Actually Do This? # I should note at this point that this approach spurns almost all of the \u0026ldquo;best practices\u0026rdquo; advocated in the JavaDocs. Specifically, the JavaDocs say this:\nThe method Node.getCssMetaData() is called to obtain the List. This method is called frequently and it is prudent to return a static list rather than creating the list on each call.\nWhen I was preparing the code for these examples I made some simple stupid coding errors that prevented it from working (more on that later). In the course of debugging the problems, I placed some println() statements in various key places, including in my implementation of Node.getCssMetaData(). I found that it wasn\u0026rsquo;t called more than once or twice at runtime.\nWhile it may be true that, in general, that Node.getCssMetaData() is called a gazillion times collectively from all the Nodes in your layout, or that it might be called frequently in some other use case, this does not appear to be a problem when used in this manner.\nThere are caveats to using this approach. You would have to be very careful using the same StyleableProperties to supply CssMetaData to two or more layout elements. It would be difficult to say what would happen if two different style class selectors ended up controlling the same StyleableProperty, it would probably be difficult to debug.\nYou also need to take a good look at whether or not some configurable aspect of a layout element should be styleable from the style sheet, or whether it\u0026rsquo;s better to simply configure it programmatically. This less likely to be the case with custom layout elements.\nAll that being said, this approach does work. Actually quite well. If it\u0026rsquo;s an approach that you want to use extensively, then it would probably be worth creating versions of StlyeleableHBox for VBox, Pane and StackPane as well.\nI think that this is a very important technique to master if you want to use a lot of custom styling in your layouts without cluttering up your layout code with single-use classes and boilerplate code.\nAlmost more importantly, I feel that going through this demonstration gives a really good understanding of how this CssMetaData stuff really does work. This is an insight that you probably won\u0026rsquo;t get from reading the JavaDocs.\nA Proper Custom Class # This previous example was aimed at solving the issue of how you would cope with the need to provide custom styling for a specific layout, in a situation where you weren\u0026rsquo;t going to be using that styling repeatedly across multiple layouts, or within the same layout. Because of this, huge amounts of the boilerplate and ceremony around creating CssMetaData that are described in the JavaDocs could simply be tossed out, with little impact on performance.\nHowever, if you are going to be reusing your custom styling, either within or between layouts, then you do need to take a more rigorous approach to implementing your custom styling. So let\u0026rsquo;s take a look at that.\nHere\u0026rsquo;s our initial screen layout:\nWhich was achieved via this code:\nclass Styleable6 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent()).apply { Styleable3::class.java.getResource(\u0026#34;styleable6.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = customBox() padding = Insets(10.0) } private fun customBox(): Region = VBox(7.0).apply { println(\u0026#34;Got here\u0026#34;) children += HBox().apply { children += Label(\u0026#34;Parts:\u0026#34;).apply { minWidth = 150.0 } children += Label(\u0026#34;1783.98\u0026#34;) } children += HBox().apply { children += Label(\u0026#34;Labour:\u0026#34;).apply { minWidth = 150.0 } children += Label(\u0026#34;300\u0026#34;) } children += HBox().apply { children += Label(\u0026#34;Tax:\u0026#34;).apply { minWidth = 150.0 } children += Label(\u0026#34;103.76\u0026#34;) } children += HBox().apply { children += Label(\u0026#34;Discount:\u0026#34;).apply { minWidth = 150.0 } children += Label(\u0026#34;-10\u0026#34;) } children += HBox().apply { children += Label(\u0026#34;Total:\u0026#34;).apply { minWidth = 150.0 } children += Label(\u0026#34;2177.74\u0026#34;) } } } fun main() = Application.launch(Styleable6::class.java) Obviously, this would be better achieved via a GridPane, but we want to keep the layout as simple as possible. So a VBox of HBoxes is what we\u0026rsquo;ll use. The problem that we\u0026rsquo;re going to concentrate on is the number display, because it\u0026rsquo;s horrible. We are going to customize these aspects:\nNumeric input Justification Number of decimal places Formatting of negative values Obviously, we are going to be doing this 5 times in this layout, and this is an issue that arises all the time in many applications, so we\u0026rsquo;re going to create a custom Label class called DecimalLabel to handle all of this.\nNumeric Input and Justification # These don\u0026rsquo;t actually take any custom StyleableProperties to achieve, at least at first:\nclass DecimalLabel(value : ObservableDoubleValue): Label() { init { textProperty().bind(value.map { it.toString() }) alignment = Pos.CENTER_RIGHT } } I\u0026rsquo;m not providing a constructor that takes a Double value because this class, virtually by definition, is intended to display data values. Any data values that I might have to display are going to be stored in Observable wrappers, so that\u0026rsquo;s what I\u0026rsquo;m going to accept in my constructor. This is then bound to the textProperty of the Label. Our layout code now changes to this:\nclass Styleable6 : Application() { val partsAmount: DoubleProperty = SimpleDoubleProperty(1783.98) val labourAmount: DoubleProperty = SimpleDoubleProperty(300.0) val taxAmount: DoubleProperty = SimpleDoubleProperty(103.76) val discountAmount: DoubleProperty = SimpleDoubleProperty(-10.0) val totalAmount: DoubleProperty = SimpleDoubleProperty(2177.74) override fun start(stage: Stage) { stage.scene = Scene(createContent()).apply { Styleable3::class.java.getResource(\u0026#34;styleable6.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = customBox() padding = Insets(10.0) } private fun customBox(): Region = VBox(7.0).apply { println(\u0026#34;Got here\u0026#34;) children += HBox().apply { children += Label(\u0026#34;Parts:\u0026#34;).apply { minWidth = 150.0 } children += DecimalLabel(partsAmount) } children += HBox().apply { children += Label(\u0026#34;Labour:\u0026#34;).apply { minWidth = 150.0 } children += DecimalLabel(labourAmount) } children += HBox().apply { children += Label(\u0026#34;Tax:\u0026#34;).apply { minWidth = 150.0 } children += DecimalLabel(taxAmount) } children += HBox().apply { children += Label(\u0026#34;Discount:\u0026#34;).apply { minWidth = 150.0 } children += DecimalLabel(discountAmount) } children += HBox().apply { children += Label(\u0026#34;Total:\u0026#34;).apply { minWidth = 150.0 } children += DecimalLabel(totalAmount) } } } fun main() = Application.launch(Styleable6::class.java) But the result looks pretty much the same, even with the right justification. If we add some styling to our DecimalLabel to give it a border, then we can see why:\n.decimal-label { -fx-border-width: 1px; -fx-border-color: black; } Ah! The DecimalLabels might be right justified internally, but they are only as wide as they need to be to hold the text and the HBox aligns its elements Pos.LEFT by default. We can fix this be adding a minimum width:\n.decimal-label { -fx-border-width: 1px; -fx-border-color: black; -fx-min-width: 5em; } That\u0026rsquo;s better.\nControlling the Number of Decimal Places # In our example, it\u0026rsquo;s clear that these are monetary amounts which are usually specified to two decimal places (at least in my part of the world). Since we creating a general purpose custom Label class, we\u0026rsquo;ll make the number of decimal places a StyleableProperty so that we can specify it via the style sheet. Let\u0026rsquo;s add the StyleableProperty and the CssMetaData to our DecimalLabel:\nclass DecimalLabel(value: ObservableDoubleValue) : Label() { private val decimalPlaces: StyleableObjectProperty\u0026lt;Number\u0026gt; = SimpleStyleableObjectProperty(DECIMAL_PLACES_META_DATA,2) companion object CssStuff { val DECIMAL_PLACES_META_DATA = object : CssMetaData\u0026lt;DecimalLabel, Number\u0026gt;(\u0026#34;-wfx-decimal-places\u0026#34;, StyleConverter.getSizeConverter(), 2) { override fun isSettable(p0: DecimalLabel): Boolean = !p0.decimalPlaces.isBound override fun getStyleableProperty(p0: DecimalLabel): StyleableProperty\u0026lt;Number\u0026gt; = p0.decimalPlaces } } init { styleClass += \u0026#34;decimal-label\u0026#34; textProperty().bind(value.map { it.toString() }) alignment = Pos.CENTER_RIGHT } } We need a way to use this property to control how many decimal places are shown. To do this, we create a Binding that is dependent on decimalPlaces and value. It will use String.format() to do the conversion:\nclass ConversionBinding( private val labelValue: ObservableDoubleValue, private val decimalPlaces: ObservableValue\u0026lt;Number\u0026gt; ) : StringBinding() { init { super.bind(labelValue, decimalPlaces) } override fun computeValue(): String { return String.format(\u0026#34;%,.${decimalPlaces.value.toInt()}f\u0026#34;, labelValue.value) } } Finally, we need to make the CSS information available to JavaFX. We do this by overriding Label.getCssMetaData().\nBut\u0026hellip;\nLabel.getCssMetaData() is final! Looking at the JavaDocs for this method in Control we see that:\nThis method returns a List containing all CssMetaData for both this Control (returned from getControlCssMetaData() and its Skin, assuming the skin property is a SkinBase.\nDevelopers who wish to provide custom CssMetaData are therefore encouraged to override getControlCssMetaData() or SkinBase.getCssMetaData(), depending on where the CssMetaData resides.\nSince we aren\u0026rsquo;t working with the skin of Label, we can override getControlCssMetaData():\noverride fun getControlCssMetaData() = getClassCssMetaData() The next step deals more with inheritability. You can see that our instance method, getControlCssMetaData() is just calling the static getClassCssMetaData(). We need to work with this static method to make sure that we pull in all of the CssMetaData from the parent class, Label, and then add our new, custom, CssMetaData to it. Then, if someone extends DecimalLabel, they can follow the same methodology and inherit all the CssMetaData in DecimalLabel as well as Label.\nI\u0026rsquo;ve followed the same convention as the JavaDocs here, putting the cumulative CssMetaData into a static List variable, and then returning that from getClassCssMetaData(). I think this is going to be more readable, especially if we add more custom CssMetaData.\nWe also want to allow the StyleleableProperty to be accessible from layout code, but we don\u0026rsquo;t want to expose it as a StyleableProperty to that code, we just want it to be ObjectProperty\u0026lt;Number\u0026gt;. In Java you would create the standard three methods to get/set the value and return the Property, but in Kotlin you create a (Kotlin) property of the enclosed type and then delegate its get() and set() methods to the StyleableProperty. This will repsond to getDecimalPlaces() as well as decimalPlaces = x.\nclass DecimalLabel(value: ObservableDoubleValue) : Label() { private val decimalPlacesSP: StyleableObjectProperty\u0026lt;Number\u0026gt; = SimpleStyleableObjectProperty(DECIMAL_PLACES_META_DATA, 2) var decimalPlaces : Int get() = decimalPlacesSP.value.toInt() set(value) = decimalPlacesSP.set(value) fun decimalPlacesProperty() : ObjectProperty\u0026lt;Number\u0026gt; = decimalPlacesSP companion object CssStuff { val negativePseudoClass = PseudoClass.getPseudoClass(\u0026#34;negative\u0026#34;) val DECIMAL_PLACES_META_DATA = object : CssMetaData\u0026lt;DecimalLabel, Number\u0026gt;(\u0026#34;-wfx-decimal-places\u0026#34;, StyleConverter.getSizeConverter(), 2) { override fun isSettable(p0: DecimalLabel): Boolean = !p0.decimalPlacesSP.isBound override fun getStyleableProperty(p0: DecimalLabel): StyleableProperty\u0026lt;Number\u0026gt; = p0.decimalPlacesSP } private val cssMetaDataList = (Label.getClassCssMetaData() + DECIMAL_PLACES_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getControlCssMetaData() = getClassCssMetaData() init { styleClass += \u0026#34;decimal-label\u0026#34; textProperty().bind(ConversionBinding(value, decimalPlacesSP)) alignment = Pos.CENTER_RIGHT value.subscribe { newVal -\u0026gt; pseudoClassStateChanged(negativePseudoClass, newVal.toFloat() \u0026lt; 0) } } class ConversionBinding( private val labelValue: ObservableDoubleValue, private val decimalPlaces: ObservableValue\u0026lt;Number\u0026gt; ) : StringBinding() { init { super.bind(labelValue, decimalPlaces) } override fun computeValue(): String { return String.format( \u0026#34;%,.${decimalPlaces.value.toInt()}f\u0026#34;, labelValue.value ) } } } The other thing that I\u0026rsquo;ve done here is to introduce a PseudoClass for when the value is negative. This will allow us to style the DecimalLabel differently when the value is below zero. To enable this, I\u0026rsquo;ve added a subcription to the value ObservableValue which calls pseudoClassStateChanged().\nIf we use this style sheet:\n.decimal-label { -fx-border-width: 1px; -fx-border-color: black; -fx-min-width: 5em; -wfx-decimal-places: 3; } .decimal-label: negative { -fx-text-fill: red; } Then we get this result:\nStyling Negative Values # There are two standard stylings for negative values. One is to prefix the number with \u0026ldquo;-\u0026rdquo; and the other is to surround the number with \u0026ldquo;()\u0026rdquo;. In order to enable this as a choice, we\u0026rsquo;ll create an Enum for this called NegativeFormat with two values SIGN and PAREN.\nThe rest of this is pretty much rinse and repeat of the process for decimal places. We add a new StyleableProperty\u0026lt;NegativeFormat\u0026gt; and then add the elements to expose it to client code. Then we create the associated CssMetaData and add it to getClassCssMetaData(). It looks like this:\nclass DecimalLabel(value: ObservableDoubleValue) : Label() { private val decimalPlacesSP: StyleableObjectProperty\u0026lt;Number\u0026gt; = SimpleStyleableObjectProperty(DECIMAL_PLACES_META_DATA, 2) var decimalPlaces: Int get() = decimalPlacesSP.value.toInt() set(value) = decimalPlacesSP.set(value) fun decimalPlacesProperty(): ObjectProperty\u0026lt;Number\u0026gt; = decimalPlacesSP private val negativeFormatSP: StyleableObjectProperty\u0026lt;NegativeFormat\u0026gt; = SimpleStyleableObjectProperty(NEGATIVE_FORMAT_META_DATA, NegativeFormat.SIGN) var negativeFormat: NegativeFormat get() = negativeFormatSP.value set(value) = negativeFormatSP.set(value) fun negativeFormatProperty(): ObjectProperty\u0026lt;NegativeFormat\u0026gt; = negativeFormatSP companion object CssStuff { val negativePseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;negative\u0026#34;) val DECIMAL_PLACES_META_DATA = object : CssMetaData\u0026lt;DecimalLabel, Number\u0026gt;(\u0026#34;-wfx-decimal-places\u0026#34;, StyleConverter.getSizeConverter(), 2) { override fun isSettable(p0: DecimalLabel): Boolean = !p0.decimalPlacesSP.isBound override fun getStyleableProperty(p0: DecimalLabel): StyleableProperty\u0026lt;Number\u0026gt; = p0.decimalPlacesSP } val NEGATIVE_FORMAT_META_DATA = object : CssMetaData\u0026lt;DecimalLabel, NegativeFormat\u0026gt;( \u0026#34;-wfx-negative-format\u0026#34;, StyleConverter.getEnumConverter(NegativeFormat::class.java), NegativeFormat.SIGN ) { override fun isSettable(p0: DecimalLabel): Boolean = !p0.negativeFormatSP.isBound override fun getStyleableProperty(p0: DecimalLabel): StyleableProperty\u0026lt;NegativeFormat\u0026gt; = p0.negativeFormatSP } private val cssMetaDataList = (Label.getClassCssMetaData() + DECIMAL_PLACES_META_DATA + NEGATIVE_FORMAT_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getControlCssMetaData() = getClassCssMetaData() init { styleClass += \u0026#34;decimal-label\u0026#34; textProperty().bind(ConversionBinding(value, decimalPlacesSP, negativeFormatSP)) alignment = Pos.CENTER_RIGHT value.subscribe { newVal -\u0026gt; pseudoClassStateChanged(negativePseudoClass, newVal.toFloat() \u0026lt; 0) } } enum class NegativeFormat { SIGN, PAREN } class ConversionBinding( private val labelValue: ObservableDoubleValue, private val decimalPlaces: ObservableValue\u0026lt;Number\u0026gt;, private val negativeFormat: ObservableValue\u0026lt;NegativeFormat\u0026gt; ) : StringBinding() { init { super.bind(labelValue, decimalPlaces) } override fun computeValue(): String { return String.format( \u0026#34;%${if (negativeFormat.value == NegativeFormat.PAREN) \u0026#34;(\u0026#34; else \u0026#34;\u0026#34;},.\u0026#34; + \u0026#34;${decimalPlaces.value.toInt()}f\u0026#34;, labelValue.value ) } } } The Binding has been updated to take the new ObservableValue\u0026lt;NegativeFormat\u0026gt; as a parameter, and it uses it to decide to add a \u0026ldquo;(\u0026rdquo; at the beginning of the formatting string.\nWith the style sheet updated to look like this:\n.decimal-label { -fx-border-width: 1px; -fx-border-color: black; -fx-min-width: 12ex; -wfx-decimal-places: 2; -wfx-negative-format: paren; } .decimal-label: negative { -fx-text-fill: red; } The output looks like this:\nThere\u0026rsquo;s one small problem with this. The closing \u0026ldquo;)\u0026rdquo; shifts the negative number over one, so that the number columns don\u0026rsquo;t line up nicely. We need to change the formatting string like this:\noverride fun computeValue(): String { return String.format( \u0026#34;%${if (negativeFormat.value == NegativeFormat.PAREN) \u0026#34;(\u0026#34; else \u0026#34;\u0026#34;},.\u0026#34; + \u0026#34;${decimalPlaces.value.toInt()}f\u0026#34; + if ((labelValue.value.toFloat() \u0026gt;= 0) \u0026amp;\u0026amp; (negativeFormat.value == NegativeFormat.PAREN)) \u0026#34; \u0026#34; else \u0026#34;\u0026#34;, labelValue.value ) } Now we put an extra \u0026quot; \u0026quot; at the end of the number if it isn\u0026rsquo;t negative. This will be displayed only if the closing \u0026ldquo;)\u0026rdquo; isn\u0026rsquo;t shown. Now it looks like this:\nAnd the columns line up nicely. With the borders removed, it looks like this:\nAnd fulfills the requirements. There are more StyleableProperties that you could add. I\u0026rsquo;ve formatted the output with \u0026ldquo;,\u0026rdquo; between the thousands, but you could implement that as a Boolean styleable value to turn them on or off. You could create a StyleableStringProperty to allow the actual format string to be defined via CSS.\nYou could decide that you don\u0026rsquo;t like the implementation of negative as a PseudoClass and that you\u0026rsquo;d rather just control the colour via another StyleableProperty. Something like colourWhenNegative. That would work just fine, too.\nConclusion # One thing that I\u0026rsquo;ve noticed when doing the coding for this article is that it is very, very easy to make a small mistake that stops everything from working. It\u0026rsquo;s especially frustating because the bits that do the work are buried deep inside JavaFX, and you don\u0026rsquo;t get to see them working. This means that there\u0026rsquo;s no feedback, nothing that says, \u0026ldquo;This part broke\u0026rdquo;. It just doesn\u0026rsquo;t work, and the lines that you put into your style sheet aren\u0026rsquo;t reflected in the GUI when it\u0026rsquo;s running.\nThings that I found helpful:\nCall getCssMetaData() and print the output to the console. Make sure that your custom elements are listed. Add a Subscription to your StyleableProperties so that you can see when they change. Make sure that your custom attributes are applied to the correct selectors in your style sheet. Double-check the spelling of the attribute names in both the CssMetaData and the style sheet. Put unconvertable values in the attributes in the style sheet. This will tell you if JavaFX is trying to apply it. Overall, I think that the idea of creating custom classes with custom styling attributes is probably the correct approach for most situations. This is especially true if you take the time to create those classes so that they can be used over and over again. The idea of having a Label subclass for handling values with special display critea, like decimals, integers and dates is generally useful, in my opinion. JavaFX doesn\u0026rsquo;t supply those, but it does give you all the tools you need to implement them the way that you want to.\nLayout-centric custom styling is an idea that has been completely ignored in the JavaFX library. It\u0026rsquo;s also harder to decide if something should be configured via CSS or just implemented via code - especially since you\u0026rsquo;re already there writing code in the first place. However, if you are just changing the presentation of a layout\u0026hellip;code that hasn\u0026rsquo;t been modified is code that doesn\u0026rsquo;t need to be tested (as much). This isn\u0026rsquo;t to say that you can\u0026rsquo;t mess up a screen by changing the style sheet, but at least you know that he logic behind the \u0026ldquo;Save\u0026rdquo; Button doesn\u0026rsquo;t need to be retested, because no code in that class has been changed.\n","date":"15 December 2024","externalUrl":null,"permalink":"/javafx/elements/styleable-properties/","section":"JavaFX","summary":"With CSS you can control fonts, sizes, borders, alignment, padding an lots of other standard Properties of your layouts.  This allows you to control the styling from outside your code.  But what if there’s some aspect of your layout that isn’t available through the standard CSS attributes?  This article will show you how to create StyleableProperties and then how to link them back to your Style Sheets via CssMetaData.","title":"Custom Styleable Properties","type":"javafx"},{"content":" Introduction # Once you\u0026rsquo;ve mastered the basic components of TableView layout and you try do something in the real world, you\u0026rsquo;ll inevitably run up against data that doesn\u0026rsquo;t seem to work nicely with TableView. We\u0026rsquo;re going to look at how to wrestle with real world data and how you can work effectively with it in your TableViews. The approach that we\u0026rsquo;re going to take is to try to understand how data moves into your TableView, and that really boils down to understanding how it gets into your TableCells.\nThis article assumes that you\u0026rsquo;ve read, or a least understand the concepts from, the first article in this series: TableView Basics. If you haven\u0026rsquo;t, then you might want to spend a few minutes reading it. The examples in this article are going to carry on using the same sample layout and data from that first article.\nThe first thing that we\u0026rsquo;re going to look at is something that you learn of in TableView 101 but you never seem to learn about; and that\u0026rsquo;s TableCell.updateItem()\u0026hellip;\nObservables and TableCell.updateItem() # When you define a TableColumn you need to define the data source for that column. The method call is defined like this:\npublic final void setCellValueFactory​(Callback\u0026lt;TableColumn.CellDataFeatures\u0026lt;S,​T\u0026gt;,​ObservableValue\u0026lt;T\u0026gt;\u0026gt; value) You\u0026rsquo;re installing a Callback that takes a CellDataFeatures and returns an ObservableValue\u0026lt;T\u0026gt; where T is the data type of the column. You can see that the TableColumn data source needs to be Observable, which usually means a Property or a Binding or something like that. In fact, the JavaDocs even give an example of using a data source which isn\u0026rsquo;t Observable and putting it in an Observable wrapper:\nfirstNameCol.setCellValueFactory(new Callback\u0026lt;CellDataFeatures\u0026lt;Person, String\u0026gt;, ObservableValue\u0026lt;String\u0026gt;\u0026gt;() { public ObservableValue\u0026lt;String\u0026gt; call(CellDataFeatures\u0026lt;Person, String\u0026gt; p) { return new ReadOnlyObjectWrapper(p.getValue().getFirstName()); } }); But here\u0026rsquo;s the enigma: Cell.updateItem() doesn\u0026rsquo;t take an Observable as one of its parameters:\nprotected void updateItem​(T item, boolean empty) What gives? We have to define the data source for the column as an Observable, but the cells for the column are given non-observable data! This has always confused me. Surely, it would make more sense to just pass the Observable in to the TableCell as an Observable so that it can be treated as an Observable.\nPoking around in the source code for TableCell gives the answer. There\u0026rsquo;s a method called TableCell.updateItem(int oldIndex). This is one of the methods that actually calls Cell.updateItem(). There\u0026rsquo;s a comment for this method in the source code:\n/* * This is called when we think that the data within this TableCell may have * changed. You\u0026#39;ll note that this is a private function - it is only called * when one of the triggers above call it. */ It turns out that \u0026ldquo;the triggers above\u0026rdquo; involve changing the row that the cell displays, or when the cell is edited by the user. But there has to be more.\nI took the code from the TableView Basics article and added a button to the main screen with the following code:\nprivate Node addButton(ObservableList\u0026lt;OrderModel_1\u0026gt; tableItems) { Button button = new Button(\u0026#34;Click Me\u0026#34;); button.setOnAction(evt -\u0026gt; { System.out.println(\u0026#34;Button has been clicked\u0026#34;); tableItems.get(0).shippedProperty().set(true); }); return button; } Then I added some code into the custom TableCell to print to the console whenever updateItem() was called. Initially, the screen looked like this:\nand then I clicked the Button:\nAnd the output on the console looked like this:\nCell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 0 has called updateItem() Cell: 1 has called updateItem() Cell: 2 has called updateItem() Cell: 3 has called updateItem() Cell: 4 has called updateItem() Cell: 5 has called updateItem() Cell: 6 has called updateItem() Cell: 7 has called updateItem() Cell: 8 has called updateItem() Cell: 9 has called updateItem() Cell: 10 has called updateItem() Cell: 11 has called updateItem() Cell: 12 has called updateItem() Cell: 13 has called updateItem() Cell: 14 has called updateItem() Cell: 15 has called updateItem() Cell: 16 has called updateItem() Button has been clicked Cell: 1 has called updateItem() What this tells us is that there\u0026rsquo;s something in the code for TableView, TableColumn or TableCell that captures changes to the value contained in our data source Observable and invokes Cell.updateItem().\nA really important take-away from this is that if you aren\u0026rsquo;t composing your TableView data models of fields of Observable values, then you\u0026rsquo;re missing out on an important feature. TableView is designed to automatically update its display whenever the underlying data changes. This means that you can treat your data model as just data, manipulate it how you like, and the TableView will stay updated without any need to reference back to your View to update it.\nIf you use ReadOnlyObjectWrapper to force your non-observable data values into your TableView, then you won\u0026rsquo;t get any of the wonderful goodness, and you\u0026rsquo;ll have to update your TableView in some tedious, manual way.\nThat\u0026rsquo;s all good, but what does Cell.updateItem() actually do?\nWhat Cell.updateItem() Does # The first place to look is in the source code. So, here it is:\nprotected void updateItem(T item, boolean empty) { setItem(item); setEmpty(empty); if (empty \u0026amp;\u0026amp; isSelected()) { updateSelected(false); } } To understand this code, you need this:\npublic final ObjectProperty\u0026lt;T\u0026gt; itemProperty() { return item; } /** * Sets the item to the given value - should not be called directly as the * item is managed by the virtualized control. * @param value the new data value to set in this cell */ public final void setItem(T value) { item.set(value); } Now you can see that there\u0026rsquo;s a field in Cell called item that holds the value in the cell. Cell.updateItem() simply updates the value in item through the delegated method setItem().\nBy the way, this is why strange stuff happens if you forget to call super.updateItem() in your custom TableCell.updateItem().\nEven more, this explains why it\u0026rsquo;s strange stuff, and not just wrong stuff. Because the way that we\u0026rsquo;ve been shown how to write updateItem() is to directly place the value of the parameter item into our Cell layout, not to use the field item in our layout. So, if you don\u0026rsquo;t call super.updateItem() then you won\u0026rsquo;t update the value in the field item, but your layout will change. Anything that uses the field item will be wrong, yet the cell might look correct.\nAnd that, in itself, suggests that maybe we\u0026rsquo;re doing something wrong.\nTableCells Without Overriding updateItem() # Let\u0026rsquo;s go back to the first custom TableCell from the TableView Basics article. We wrote it like this:\npublic class BooleanTableCell1\u0026lt;S\u0026gt; extends TableCell\u0026lt;S, Boolean\u0026gt; { CheckBox checkBox = new CheckBox(); StackPane container; public BooleanTableCell1() { container = new StackPane(checkBox); container.setPadding(new Insets(8)); setText(null); setGraphic(null); } @Override protected void updateItem(Boolean item, boolean empty) { super.updateItem(item, empty); if (!empty) { if (item != null) { checkBox.setSelected(item); } else { checkBox.setSelected(false); } setGraphic(container); } else { setGraphic(null); } } } You can see that we\u0026rsquo;re ignoring the built in ObjectProperty\u0026lt;Boolean\u0026gt; of Cell, and just stuffing the value of the passed parameter into our CheckBox. What if we used the item field instead?\npublic class BooleanTableCell4\u0026lt;S\u0026gt; extends TableCell\u0026lt;S, Boolean\u0026gt; { private final StackPane container; public BooleanTableCell4() { CheckBox checkBox = new CheckBox(); checkBox.selectedProperty().bind(itemProperty()); container = new StackPane(checkBox); container.setPadding(new Insets(8)); setText(null); setGraphic(null); } @Override protected void updateItem(Boolean item, boolean empty) { super.updateItem(item, empty); if (!empty) { setGraphic(container); } else { setGraphic(null); } } } This works just fine and fits in better, in my opinion, with the concept of writing Reactive code for your views. In this case, the layout is defined statically yet behaves dynamically. We\u0026rsquo;ve done away with the CheckBox field, since we\u0026rsquo;re no longer referencing it from updateItem().\nBut we can do better. If you look back at the code for Cell.updateItem() you\u0026rsquo;ll see that it doesn\u0026rsquo;t just update the field item, it also calls setEmpty(), which is a delegate to the setter of a Property field in Cell called empty. This indicates that we can use the empty field in our layout constructor:\npublic class BooleanTableCell4\u0026lt;S\u0026gt; extends TableCell\u0026lt;S, Boolean\u0026gt; { public BooleanTableCell4() { CheckBox checkBox = new CheckBox(); checkBox.selectedProperty().bind(itemProperty()); StackPane container = new StackPane(checkBox); container.setPadding(new Insets(8)); graphicProperty().bind(Bindings.createObjectBinding(() -\u0026gt; !isEmpty() ? container:null, emptyProperty() )); setText(null); } @Override protected void updateItem(Boolean item, boolean empty) { super.updateItem(item, empty); } } Now the Graphic Property is bound to the Empty Property and we can ditch the container field since we\u0026rsquo;re no longer referencing it from updateItem().\nHowever, our updateItem() does nothing except call the super.updateItem() that it was overriding. So we can get rid of it:\npublic class BooleanTableCell4\u0026lt;S\u0026gt; extends TableCell\u0026lt;S, Boolean\u0026gt; { public BooleanTableCell4() { CheckBox checkBox = new CheckBox(); checkBox.selectedProperty().bind(itemProperty()); StackPane container = new StackPane(checkBox); container.setPadding(new Insets(8)); graphicProperty().bind(Bindings.createObjectBinding(() -\u0026gt; !isEmpty() ? container : null, emptyProperty())); setText(null); } } Woa! This is nothing like the examples you\u0026rsquo;ll see on the web, or in the JavaDocs. As a matter of fact, the JavaDocs have this to say:\nThe updateItem method should not be called by developers, but it is the best method for developers to override to allow for them to customise the visuals of the cell.\nI\u0026rsquo;m going to have to say that is rubbish. First of all, everyone knows that doing layout in updateItem() will result in truly bad performance of your TableView as layouts should only be created once per instance of a TableCell. Secondly, updateItem() is clearly all about data management, so you should leave it for those cases where you need to deal with data management.\nBTW: All of the other custom TableCells from TableView Basics can be handled this same way.\nDerived Columns # Simple TableViews will have a data model that has a single field for each TableColumn. This means that the setCellValueFactory​() call for each TableColumn will just need to pull a single Property field from your data model. But what if you need to combine some together?\nBinding in the CellValueFactory Callback # Let\u0026rsquo;s imagine that you have some sort of a customer database and you\u0026rsquo;re displaying data from it. Your database has separate fields for firstName and lastName and you need to put them together in some way.\nThe simplest approach is to create a Binding as the return value for the Callback that you create:\ntableColumn.setCellValueFactory​(cdf -\u0026gt; cdf.value.fNameProperty().concat(\u0026#34; \u0026#34;).concat(cdf.value.lNameProperty()); That will get the job done, but\u0026hellip;\nShould You Do This? # It seems obvious and simple, right? But let\u0026rsquo;s look at it from the other direction.\nSuppose your database just had the names in a single field for the whale name and you wanted your TableView to have two columns; one for first name and one for last name. That\u0026rsquo;s easy enough for \u0026ldquo;John Smith\u0026rdquo;, but what about \u0026ldquo;Billy Bob Van Der Camp Esq\u0026rdquo;? The logic for that is going to be quite a bit more complicated. At some point, the logic required to sort this out goes beyond simply displaying the information on the screen and becomes something that should be considered \u0026ldquo;business logic\u0026rdquo;. When that happens, you need to put it the rest of your business logic - in the Model or the Interactor.\nHow do you do this?\nThe best approach is to set up your Presentation Model to deliver the information to your view. This allows you to still have a single Property to pull for your column, and the TableView doesn\u0026rsquo;t need to know that, behind the scenes, several different elements are being put together.\nThere\u0026rsquo;s two ways that you can do this\u0026hellip;\nBinding the Values in the Interactor # Let\u0026rsquo;s go back to the original case of combining the first and last names into a single value, but this time we\u0026rsquo;ll use the format, \u0026ldquo;{lastName}, {firstName}\u0026rdquo;. The first step is to create a StringProperty field to hold the result:\nprivate final StringProperty name = new SimpleStringProperty(\u0026#34;\u0026#34;); public StringProperty nameProperty() { return name; } public void setNameBinding(StringBinding binding) { name.bind(binding); } We\u0026rsquo;ve provided a method called setNameBinding() so that our Model/Interactor can provide the logic for the Binding. That code will go in the constructor for the Model/Interactor:\npublic class TableBlogInteractor { TableBlogInteractor(OrderModel_1 model) { model.setNameBinding(Bindings.createStringBinding(() -\u0026gt; createFullName(model.fNameProperty().get(), model.lNameProperty().get()), model.fNameProperty(), model.lNameProperty())); } private String createFullName(String fName, String lName) { return lName + \u0026#34;, \u0026#34; + fName; } } And here, createFullName() can be as complicated and baroque as you like, as long as it can still run on the FXAT without taking too long or blocking.\nThe net effect of this is that we\u0026rsquo;ve tailored the Presentation Model to the needs of the View at the cost of creating a bit more coupling in our system. On the upside, this coupling is confined to the Presentation Model, which is where we expect to see virtually all of the coupling.\nThis is the kind of architecture decision that teams come to blows over. On the one hand, we\u0026rsquo;ve moved complexity out of the View and kept business logic in one place. On the other hand, we\u0026rsquo;ve now disclosed some information about how the View is going to use the data - it\u0026rsquo;s going to put the first and last names together into a single display element. Counterbalanced against that, the View doesn\u0026rsquo;t know that nameProperty() is a bound value, it just sees it as another data element. In fact, it might even be a completely different data element from the underlying database. For instance; first name - \u0026ldquo;John\u0026rdquo;, last name = \u0026ldquo;Smith\u0026rdquo; and name = \u0026ldquo;Smitty\u0026rdquo; could be totally valid.\nThere\u0026rsquo;s no absolute right or wrong here. It just has to be an approach that makes sense to the team.\nBinding the Values in the Presentation Model # Finally, there\u0026rsquo;s a \u0026ldquo;middle ground\u0026rdquo; approach where all of the logic for combining the names together resides in the Presentation Model itself:\npublic ObservableStringValue nameProperty() { return Bindings.createStringBinding(() -\u0026gt; createFullName(fName.get(), lName.get()), fName, lName); } private String createFullName(String fName, String lName) { return lName + \u0026#34;, \u0026#34; + fName; } Personally, I\u0026rsquo;d lean towards this more than performing the Binding in the TableColumn creation code, with the same caveats that if the logic gets too complicated then it belongs with the other business logic in the application - in the Model or the Interactor. One of the benefits of this approach (over binding in the TableColumn code) is that nature of how the full name is created remains hidden from the View. This means that it can be changed, with the logic moved into the Interactor, without any changes required to the View.\nGenerally speaking, I think you\u0026rsquo;re better off having a single field (or something that looks like a field) in your Presentation Model for each column in your TableView. This keeps your View from having to understand relationships between the various data elements in the Presentation Model.\nComplex Columns # Sometimes you need two or more distinct data elements to adequately display a column in a TableView. How do you handle that?\nFor instance, let\u0026rsquo;s assume that you want to show the full name in a single TableCell, but you want to display them stacked vertically to save horizontal space. This means that they\u0026rsquo;ll still be two data elements, the first name and the last name, but they\u0026rsquo;ll both be displayed in the same TableCell.\nAs another example, let\u0026rsquo;s say that you want to change the way that a TableCell is formatted based on one data element, but the data displayed in the cell is from another data element. This is the one that we\u0026rsquo;ll look at.\nFirst off, you can\u0026rsquo;t really do what we did when combining the names, where the two data elements are transformed into a single generic element. That\u0026rsquo;s because you can\u0026rsquo;t take them apart again to use the individual parts in your layout design.\nWhatever you do, don\u0026rsquo;t put the diffent data elements into a List\u0026lt;Object\u0026gt;. It\u0026rsquo;s a kludge that you\u0026rsquo;ll see people do all the time and it\u0026rsquo;s simply horrible.\nThe answer is to create a custom object that will hold all of the data components that you need as fields inside of it. The data element in your TableView data model will be an ObjectProperty\u0026lt;CustomObject\u0026gt;.\nHowever, this can become mind-numbingly difficult to deal with if you do it wrong. The temptation is to create this custom object composed of ObservableValues as fields. Then when you put this into ObjectProperty\u0026lt;CustomObject\u0026gt; you\u0026rsquo;ll have an ObservableValue composed of ObservableValues, and you\u0026rsquo;ll have to watch out. This is because if you bind those composed ObservableValues to elements of your GUI, they because orphaned when the enclosing ObjectProperty\u0026lt;CustomObject\u0026gt; changes its value. This means that you\u0026rsquo;ll have to unbind and rebind - well look at this later.\nUsing an Immutable Custom Object # Mutability makes complex stuff way, way more complex. And mutability makes really complicated stuff nearly impossible. ObservableValues, by their very nature, are mutable. You can keep things much simpler by making your custom objects immutable.\nThis means that in order to change one of the values inside them, you\u0026rsquo;ll need to partially clone the object and replace the old value with the new value and return the new object. If you do this, then you won\u0026rsquo;t need to worry about having multiple layers of ObservableValues to keep track of.\nLet\u0026rsquo;s take a this\u0026hellip;\nWe\u0026rsquo;ll use the same example data model that we used in the TableView Basics article, but we\u0026rsquo;ll add in a custom object that looks like this to replace sku:\npublic class ProductId { private final String sku; private final Boolean customPart; public ProductId(String sku, Boolean customPart) { this.sku = sku; this.customPart = customPart; } public String getSku() { return sku; } public Boolean isCustomPart() { return customPart; } public ProductId changeSku(String newSku) { return new ProductId(newSku, customPart); } public ProductId changeCustomPart(Boolean newCustomPart) { return new ProductId(sku, newCustomPart); } @Override public boolean equals(Object other) { if ((other instanceof ProductId)) { return (sku.equals(((ProductId) other).getSku()) \u0026amp;\u0026amp; customPart.equals(((ProductId) other).isCustomPart())); } return false; } } You can see that ProductId is immutable. There\u0026rsquo;s no way to change either of the fields inside of ProductId. Just to make the point, a couple of convenience methods have been added to show how changing either field can be achieved by creating a new ProductId with the new field value and tnen returning that new ProductId. Once you have an instance of ProductId, however, its values for sku and customPart are locked in. We also have an implementation of equals() since the TableView code won\u0026rsquo;t invoke updateItem() if the old and new values are the same.\nNow we can look at our new OrderModel data class:\npublic class OrderModel_2 { private final ObjectProperty\u0026lt;ProductId\u0026gt; sku = new SimpleObjectProperty\u0026lt;\u0026gt;(); private final ObjectProperty\u0026lt;Integer\u0026gt; uid = new SimpleObjectProperty\u0026lt;\u0026gt;(0); private final ObjectProperty\u0026lt;Integer\u0026gt; quantity = new SimpleObjectProperty\u0026lt;\u0026gt;(0); private final ObjectProperty\u0026lt;LocalDate\u0026gt; date = new SimpleObjectProperty\u0026lt;\u0026gt;(LocalDate.now()); private final StringProperty name = new SimpleStringProperty(\u0026#34;\u0026#34;); private final BooleanProperty shipped = new SimpleBooleanProperty(false); public OrderModel_2(int uid, String sku, boolean customPart, int quantity, LocalDate date, Boolean shipped) { this.uid.set(uid); this.sku.set(new ProductId(sku, customPart)); this.quantity.set(quantity); this.date.set(date); this.shipped.set(shipped); } public ObjectProperty\u0026lt;ProductId\u0026gt; skuProperty() { return sku; } public ObjectProperty\u0026lt;Integer\u0026gt; uidProperty() { return uid; } public ObjectProperty\u0026lt;Integer\u0026gt; quantityProperty() { return quantity; } public ObjectProperty\u0026lt;LocalDate\u0026gt; dateProperty() { return date; } public BooleanProperty shippedProperty() { return shipped; } public StringProperty nameProperty() { return name; } public void setNameBinding(StringBinding binding) { name.bind(binding); } public static List\u0026lt;OrderModel_2\u0026gt; generateList() { List\u0026lt;OrderModel_2\u0026gt; results = new ArrayList\u0026lt;\u0026gt;(); int uid = 0; results.add(new OrderModel_2(uid++, \u0026#34;PART 1\u0026#34;,false, 20, LocalDate.of(2022, 1, 20), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART 17\u0026#34;,true, 20, LocalDate.of(2021, 7, 15), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 20\u0026#34;, false,20, LocalDate.of(2020, 5, 5), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART 101\u0026#34;, false,20, LocalDate.of(2021, 4, 17), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART 171\u0026#34;, false,20, LocalDate.of(2022, 12, 27), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 35\u0026#34;,false, 20, LocalDate.of(2021, 11, 4), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART 18\u0026#34;,true, 20, LocalDate.of(2020, 6, 10), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 59\u0026#34;,true, 20, LocalDate.of(2021, 9, 19), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 28837\u0026#34;,false, 20, LocalDate.of(2022, 10, 23), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART A73B\u0026#34;,false, 20, LocalDate.of(2021, 4, 15), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 4J1\u0026#34;, true,20, LocalDate.of(2020, 2, 14), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART FFC32\u0026#34;, false,20, LocalDate.of(2021, 7, 13), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 1832-882B\u0026#34;, false,20, LocalDate.of(2022, 3, 1), false)); results.add(new OrderModel_2(uid++, \u0026#34;SUBPART 25\u0026#34;, false,20, LocalDate.of(2022, 1, 6), false)); results.add(new OrderModel_2(uid++, \u0026#34;ASSEMBLY 7K22\u0026#34;,true, 20, LocalDate.of(2022, 11, 12), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 9983\u0026#34;, false,20, LocalDate.of(2021, 9, 24), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART GH887\u0026#34;, true,20, LocalDate.of(2020, 4, 30), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART 1003\u0026#34;,false, 20, LocalDate.of(2021, 3, 17), false)); results.add(new OrderModel_2(uid++, \u0026#34;PART 31\u0026#34;, true,20, LocalDate.of(2021, 10, 5), true)); results.add(new OrderModel_2(uid++, \u0026#34;PART 98XB\u0026#34;, false,20, LocalDate.of(2022, 3, 22), false)); return results; } } This is almost the same except the sku field has been switched over to our ProductId class and the constructor and the static List creator have been updated to reflect this.\nNow let\u0026rsquo;s look at the TableViewBuilder:\npublic class TableViewBuilder2 implements Builder\u0026lt;Region\u0026gt; { private final ObservableList\u0026lt;OrderModel_2\u0026gt; model; public TableViewBuilder2(ObservableList\u0026lt;OrderModel_2\u0026gt; model) { this.model = model; } @Override public Region build() { TableView\u0026lt;OrderModel_2\u0026gt; results = new TableView\u0026lt;\u0026gt;(); TableColumn\u0026lt;OrderModel_2, Integer\u0026gt; uidCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;UID\u0026#34;); uidCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().uidProperty()); results.getColumns().add(uidCol); TableColumn\u0026lt;OrderModel_2, ProductId\u0026gt; skuCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;SKU\u0026#34;); skuCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().skuProperty()); skuCol.setCellFactory(tableColumn -\u0026gt; new ProductIdTableCell()); results.getColumns().add(skuCol); TableColumn\u0026lt;OrderModel_2, Integer\u0026gt; quantityCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;Quantity\u0026#34;); quantityCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().quantityProperty()); results.getColumns().add(quantityCol); TableColumn\u0026lt;OrderModel_2, LocalDate\u0026gt; dateCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;Date\u0026#34;); dateCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().dateProperty()); results.getColumns().add(dateCol); TableColumn\u0026lt;OrderModel_2, Boolean\u0026gt; shippedCol = new TableColumn\u0026lt;\u0026gt;(\u0026#34;Shipped?\u0026#34;); shippedCol.setCellValueFactory(cdf -\u0026gt; cdf.getValue().shippedProperty()); results.getColumns().add(shippedCol); results.setItems(model); results.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); return results; } } The biggest change here is that we\u0026rsquo;re now creating a ProductIdTableCell for the sku column. Let\u0026rsquo;s look at that:\npublic class ProductIdTableCell1 extends TableCell\u0026lt;OrderModel_2, ProductId\u0026gt; { private static final PseudoClass redDisplay = PseudoClass.getPseudoClass(\u0026#34;custom\u0026#34;); public ProductIdTableCell1() { Text text = new Text(); text.textProperty().bind(Bindings.createStringBinding(this::determineSku, itemProperty())); StackPane container = new StackPane(text); container.getStyleClass().add(\u0026#34;cell-box\u0026#34;); container.setPadding(new Insets(8)); itemProperty().addListener(itemProperty -\u0026gt; container.pseudoClassStateChanged(redDisplay, determineCustomPart())); graphicProperty().bind(Bindings.createObjectBinding(() -\u0026gt; !isEmpty() ? container : null, emptyProperty())); setText(null); } private String determineSku() { return (itemProperty().get() != null) ? itemProperty().get().getSku() : \u0026#34;\u0026#34;; } private boolean determineCustomPart() { return (itemProperty().get() != null) ? itemProperty().get().isCustomPart() : false; } } We\u0026rsquo;re sticking here with same concept of putting everything into the layout constructor and binding to the data value represented by item. This works because we know that there\u0026rsquo;s no way that the sku or customPart field can change without replacing the entire ProuductId contained inside item. The CSS file looks like this:\n.cell-box {} .cell-box:custom { -fx-background-color: lightblue; } And the Application to run it looks like this:\npublic class DataTableViewApplication extends Application { private final ObservableList\u0026lt;OrderModel_2\u0026gt; tableItems = FXCollections.observableArrayList(); public static void main(String[] args) { launch(); } @Override public void start(Stage primaryStage) throws Exception { Scene scene = new Scene(createContent()); primaryStage.setScene(scene); primaryStage.show(); scene.getStylesheets().add(DataTableViewApplication.class.getResource(\u0026#34;css/tableblog.css\u0026#34;).toString()); fetchData(); } private Region createContent() { VBox results = new VBox(new TableViewBuilder2(tableItems).build()); results.setPadding(new Insets(20)); results.setMinWidth(500); return results; } private void fetchData() { Task\u0026lt;List\u0026lt;OrderModel_2\u0026gt;\u0026gt; loadTask = new Task\u0026lt;\u0026gt;() { @Override protected List\u0026lt;OrderModel_2\u0026gt; call() throws InterruptedException { Thread.sleep(2000); return OrderModel_2.generateList(); } }; loadTask.setOnSucceeded(evt -\u0026gt; tableItems.setAll(loadTask.getValue())); Thread loadThread = new Thread(loadTask); loadThread.start(); } } And the result looks like this:\nThe biggest quirk with this approach is that you need to deal with the possibility that the value in item will be null, and that attempts to call item.get().getSku() and item.get().isCustomPart() are going to fail with NPE\u0026rsquo;s. This is always going to be an issue with composed objects as Property values, since the possibility will always exist that the Property container will be empty.\nThe functions determineSku() and determineCustomPart() cope with this, and having them split out from the layout code keeps the layout code clean.\nThere\u0026rsquo;s another way to deal with this, and this involves bringing back the overridden setItem() method. Since we know that the only way to change the value in the item property is through setItem(), we are guarateed that any code we put in there to deal with a new item that is null is going to execute:\npublic class ProductIdTableCell2 extends TableCell\u0026lt;OrderModel_2, ProductId\u0026gt; { private static final PseudoClass redDisplay = PseudoClass.getPseudoClass(\u0026#34;custom\u0026#34;); private final StringProperty sku = new SimpleStringProperty(\u0026#34;\u0026#34;); private final BooleanProperty customPart = new SimpleBooleanProperty(false); public ProductIdTableCell2() { Text text = new Text(); text.textProperty().bind(sku); StackPane container = new StackPane(text); container.getStyleClass().add(\u0026#34;cell-box\u0026#34;); container.setPadding(new Insets(8)); customPart.addListener(itemProperty -\u0026gt; container.pseudoClassStateChanged(redDisplay, customPart.get())); graphicProperty().bind(Bindings.createObjectBinding(() -\u0026gt; !isEmpty() ? container : null, emptyProperty())); setText(null); } @Override public void updateItem(ProductId item, boolean isEmpty) { super.updateItem(item, isEmpty); if (!isEmpty \u0026amp;\u0026amp; (item != null)) { sku.set(item.getSku()); customPart.set(item.isCustomPart()); } else { sku.set(\u0026#34;\u0026#34;); customPart.set(false); } } } Here we\u0026rsquo;ve created two now Property fields in the TableCell. One to hold the sku as a StringProperty and the other to hold customPart as a BooleanProperty. Now we can construct our layout to bind directly to these Properties, essentially treating them in exactly the same way that we would have treated item if it was a simple Property.\nThe code to deal with the empty or null value in the item is placed in the overriden updateItem() method. Note that this is all that this method does. It\u0026rsquo;s simply translating updateItem() into a method that performs the same function on the decomposed contents of ProductId.\nIs this better than the first way?\nI don\u0026rsquo;t think that it\u0026rsquo;s \u0026ldquo;better\u0026rdquo;, but it\u0026rsquo;s a different philosophic approach which is maybe just a little more in alignment with the way that the internal JavaFX code is written. It keeps the layout code a little bit cleaner, and concentrates all of the null value logic into the place where the value in item is updated. Personally, I\u0026rsquo;d probably use this approach.\nUsing Nested Properties # It\u0026rsquo;s possible that you\u0026rsquo;ll have a situation where you really need the components of ProductId to be Properties themselves. Perhaps there is some other part of your GUI that access the same data and you need to bidirectionally bind sku to the text property of a TextField. So how do you go about this?\nYou need to understand that once you start using Properties composed of other Properties your brain is going to start to hurt.\nWant you\u0026rsquo;ll want to do is to Bind one of those composed properties and use the \u0026ldquo;Set it and forget it\u0026rdquo; layout approach. Except that, if you do, it\u0026rsquo;ll break.\nWe\u0026rsquo;ll start with a new version of ProductId, we\u0026rsquo;ll call it PropertyProductId:\npublic class PropertyProductId { private final StringProperty sku = new SimpleStringProperty(\u0026#34;\u0026#34;); private final BooleanProperty customPart = new SimpleBooleanProperty(false); public PropertyProductId(String sku, boolean customPart){ this.sku.set(sku); this.customPart.set(customPart); } public String getSku() { return sku.get(); } public StringProperty skuProperty() { return sku; } public void setSku(String sku) { this.sku.set(sku); } public boolean isCustomPart() { return customPart.get(); } public BooleanProperty customPartProperty() { return customPart; } public void setCustomPart(boolean customPart) { this.customPart.set(customPart); } } This is much more consistent with the typical JavaFX data model we\u0026rsquo;re familiar with. The fields are implemented as JavaFX \u0026ldquo;Beans\u0026rdquo;, with delgated getter and setter methods and property fetchers.\nLet\u0026rsquo;s imagine that you have an instance of this called productId. Now, you could do something like this:\nTextField textField = new TextField(); textField.textProperty().bind(productId.skuProperty()); This is the kind of thing that we do all the time when creating layouts and binding them to elements of a data model. But what happens if we do this:\nproductId.setSku(\u0026#34;FRED999\u0026#34;); TextField textField = new TextField(); textField.textProperty().bind(productId.skuProperty()); . . . productId = new PropertyProductId(); productId.setSku(\u0026#34;ABC123\u0026#34;); What value do you expect to see in textField? \u0026ldquo;ABC123\u0026rdquo;?\nNo, you\u0026rsquo;ll still see \u0026ldquo;FRED999\u0026rdquo; because textField was bound to that particular instance of PropertyProductId that was stored in productId at the time it was bound. Even more, it was bound to the StringProperty in the sku field of that instance of PropertyProductId at the time it was bound. And that\u0026rsquo;s where it will stay bound until you force it to change.\nIt get\u0026rsquo;s worse when productId itself is an ObjectProperty\u0026lt;PropertyProductId\u0026gt;\u0026hellip;.\nproductId.bind(dataModel.productIdProperty()) productId.get().setSku(\u0026#34;FRED999\u0026#34;); TextField textField = new TextField(); textField.textProperty().bind(productId.get().skuProperty()); . . . dataModel.setProductId(new PropertyProductId()); dataModel.getProductId().setSku(\u0026#34;ABC123\u0026#34;); System.out.println(productId.getSku()); Oh no!\nLet\u0026rsquo;s look at this. Our local productId is bound to our data model\u0026rsquo;s productId property. Then we\u0026rsquo;re going to stuff \u0026ldquo;FRED999\u0026rdquo; into the sku Property and bind it to the textProperty() of the TextField. Then, later on, some code somewhere is going to update our data model and change the value in its productId field. At that point, our local productId variable, which is bound to the data model value will change. So when the sku value is changed to \u0026ldquo;ABC123\u0026rdquo; in the data model, that change will be reflected in the local productId and the output from the println will be \u0026ldquo;ABC123\u0026rdquo;.\nBUT the TextField will still display \u0026ldquo;FRED999\u0026rdquo;\nThat\u0026rsquo;s because the TextField is still bound to the to the StringProperty in the sku field of that instance of PropertyProductId at the time it was bound.\nAnd, of course, that\u0026rsquo;s the nature of Properties. We expect their values to change at random times, which is why we use bindings.\nThe only way to deal with this is to use a Listener on the wrapping Property and then recreate the bindings when the outer Property changes. There is a function called Bindings.select() that can do this for you, but we\u0026rsquo;ll leave this for another day.\nLet\u0026rsquo;s look at how we\u0026rsquo;d deal with this in our example TableCell. We\u0026rsquo;ll stick with the second version, since the overridden updateItem() method essentially fills the same role as a Listener:\npublic class ProductIdTableCell3 extends TableCell\u0026lt;OrderModel_3, PropertyProductId\u0026gt; { private static final PseudoClass redDisplay = PseudoClass.getPseudoClass(\u0026#34;custom\u0026#34;); private final StringProperty sku = new SimpleStringProperty(\u0026#34;\u0026#34;); private final BooleanProperty customPart = new SimpleBooleanProperty(false); public ProductIdTableCell3() { Text text = new Text(); text.textProperty().bind(sku); StackPane container = new StackPane(text); container.getStyleClass().add(\u0026#34;cell-box\u0026#34;); container.setPadding(new Insets(8)); customPart.addListener(itemProperty -\u0026gt; container.pseudoClassStateChanged(redDisplay, customPart.get())); graphicProperty().bind(Bindings.createObjectBinding(() -\u0026gt; !isEmpty() ? container : null, emptyProperty())); setText(null); } @Override public void updateItem(PropertyProductId item, boolean isEmpty) { super.updateItem(item, isEmpty); if (!isEmpty \u0026amp;\u0026amp; (item != null)) { sku.unbind(); customPart.unbind(); sku.bind(item.skuProperty()); customPart.bind(item.customPartProperty()); } else { sku.unbind(); customPart.unbind(); } } } This doesn\u0026rsquo;t look that different from the previous version, and that\u0026rsquo;s because updateItem(), as has been pointed out, is functioning as a ChangeListener, since any updates to item have to go through here. So we can adapt to the fact that PropertyProductId is composed of Properties by binding our two property fields to the fields in the new item instead of using Property.set(). We clear out our internal properties by unbinding them.\nConclusion # Hopefully, you can see that the key to creating TableCells is to treat them like any other Reactive layout design, but one where the Data Model is constantly replaced. If the Data Model is a single ObservableValue, this is trivial and you can just treat it as an immutable model.\nWhen the Data Model is more complex, you can handle this best by creating proxy Properties for the elements of your Data Model as fields in the TableCell, binding your layout to these proxies, and then handle the changing Data Models by simply updating the proxies.\nI think that it\u0026rsquo;s a little disappointing that virtually every piece of documentation and tutorial available handles TableCell customization in the same, inelegant way. It\u0026rsquo;s amounts to little more than copypasta from the JavaDocs with the same approach of overriding updateItem() without any insight into what updateItem() actually does or why you should override it. There\u0026rsquo;s no mention anywhere about the item Property or the empty Property or how you might leverage them to come up with a simpler, more logical approach to TableCell design.\nI think it\u0026rsquo;s interesting that when you remove the layout references from updateItem() and bind the layout directly to item and empty it starts to make more sense. In the end, it makes it much easier to conceive of ways to implement TableCells that use more complicated data structures and nested Property constructs.\n","date":"17 November 2023","externalUrl":null,"permalink":"/javafx/elements/nodes/tableview/cell-data/","section":"JavaFX","summary":"In this article, we taka a look at how TableView handles data, the mechanism that move data in and out of TableCells, and how to work with it.","title":"Handling TableView Cell Data","type":"javafx"},{"content":" TextFormatter # TextFormatter is the coolest JavaFX feature that you\u0026rsquo;\u0026rsquo;ve probably never heard of. With TextFormatter you can turn any TextField or TextArea into a customized data entry field that will accept only a specific kind of input and bound directly to any type of Property in your Data Model. You can create TextFields for Numbers, Dates, Phone Numbers or Postal Codes. Really, anything you can think of.\nNote As with almost everything else in JavaFX: Here we are given some really powerful and well designed low-level tools and we\u0026rsquo;re left to figure out how to do something useful with them.\nThere\u0026rsquo;s no standard implementations of TextFormatter in the library to handle even typical situations like integer or decimal number input, and nothing to use a starting point to build your own TextFormatter. This can be daunting.\nThis section should help you.\nHow TextFormatter Works # TextFormatter is a component that you install onto a TextField which then provides two interfaces. One to your Data Model and one for the user interaction.\nThe Converter # The Converter is a bi-directional transform that converts the String data in your TextField to an external format or type. This means that you can have the types and the format of the Properties in your Data Model match their purpose, without having to worry about how they will be represented in the GUI.\nYou could, for instance provide an IntegerProperty in your Model, and have the Converter translate it to the String required by TextField.\nAdditionally, you could have a StringProperty in your Model, but its format is different from the best representation in the GUI. For instance you could have phone numbers stored in your model with just the digits, something like \u0026ldquo;9995551212\u0026rdquo;, but have the TextField display \u0026ldquo;(999)555-1212\u0026rdquo;. Converter will handle that for you, and you can directly bind the Model Property to the Value Property in the TextFormatter.\nThe Filter # The Filter is the interface that sits between the TextField itself and all of the user interactions. This includes keystrokes and mouse actions, including selecting text inside the TextField.\nNo more trying to capture KeyEvents and interpreting them! The Filter does it for you.\nThe Filter is called \u0026ldquo;Filter\u0026rdquo; because each user action is packaged up as a \u0026ldquo;Change\u0026rdquo; and passed through the Filter before it is applied to the TextField. The Change object contains virtually all of the information that you need to evaluate the impact of the change on the TextField. The Filter can either let the change go through as is, halt the change, or let a modified version of the change pass on to the TextField.\nPutting it Together # The basic process is straight forward. Create a Converter that will handle your data type and then a Filter that will ensure that the TextField will behave the way you want. Then you pass them to the constructor of a TextFormatter that install onto a TextField or a TextArea using its setTextFormatter() method.\nThe Articles # This is a big subject, and cannot be adequately handled in a single article. So it\u0026rsquo;s been divided into parts:\n","date":"3 January 2022","externalUrl":null,"permalink":"/javafx/elements/nodes/textformatter/","section":"JavaFX","summary":"TextFormatter is the coolest JavaFX feature that you’’ve probably never heard of. With TextFormatter you can turn any TextField or TextArea into a customized data entry field that will accept only a specific kind of input and bound directly to any type of Property in your Data Model. You can create TextFields for Numbers, Dates, Phone Numbers or Postal Codes. Really, anything you can think of.","title":"All About TextFormatter","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # The structure of cascading style sheet How to implement a cascading style sheet How to define a custom class selector How to define some attributes in a style sheet How to implement a style sheet in an application How to attach styling to a JavaFX Node The importance of DRY (Don\u0026rsquo;t Repeat Yourself) What is a Stylesheet in JavaFX? # Style sheets in JavaFX fill much the same purpose that they do in web design. The allow you to specify the styling of various Nodes of a GUI in a self-contained element outside of your code.\nThe Code So Far # Let\u0026rsquo;s review how far we\u0026rsquo;ve come with our sample application. We have a screen with a TextField for user input and a Button to trigger an action which will update our output.\npublic class Main extends Application { public static void main(String[] args) { launch(args); } private final StringProperty greeting = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty name = new SimpleStringProperty(\u0026#34;\u0026#34;); @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContent(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContent() { VBox results = new VBox(20, createInputRow(), createOutputLabel(), createGreetingButton()); results.setAlignment(Pos.CENTER); return results; } private Node createGreetingButton() { Button results = new Button(\u0026#34;Hello\u0026#34;); results.setOnAction(evt -\u0026gt; setGreeting()); return results; } private Node createInputRow() { TextField textField = new TextField(\u0026#34;\u0026#34;); textField.textProperty().bindBidirectional(name); HBox hBox = new HBox(6, new Label(\u0026#34;Name:\u0026#34;), textField); hBox.setAlignment(Pos.CENTER); return hBox; } private Node createOutputLabel() { Label results = new Label(\u0026#34;\u0026#34;); results.textProperty().bind(greeting); return results; } private void setGreeting() { greeting.set(\u0026#34;Hello \u0026#34; + name.get()); } } Now we are going add some styling to our screen using a cascading stylesheet.\nThe Structure of the Stylesheet # A stylesheet is just a text file with a particular structure:\n.root { colour1 : value; colour2 : value; } .classSelector1 { -fx-attributeA: value; -fx-attributeB: value; } .classSelector1 { -fx-attributeA: value; -fx-attributeC: value; } The lines that start with a \u0026ldquo;.\u0026rdquo;, like \u0026ldquo;.root\u0026rdquo; and \u0026ldquo;.classSelector1\u0026rdquo; are called \u0026ldquo;selectors\u0026rdquo;. More specifically, they are called \u0026ldquo;Class Selectors\u0026rdquo;. There are other kinds of selectors, but for now we are going to stick to just Class Selectors as they do everything we need.\nThe \u0026ldquo;.root\u0026rdquo; class selector is special. It\u0026rsquo;s invoked whenever the stylesheet is applied, and is usually used to define colours that are used throughout the stylesheet, in much the same way that you\u0026rsquo;d assign static values to constants in a Java class.\nFor any given Selector, you can assign values to various JavaFX \u0026ldquo;attributes\u0026rdquo;. These are things like border colours, border widths, opacity, alignments and so on. Specific attributes might not apply to certain Node types. For instance there\u0026rsquo;s no point trying to assign a font to a Circle, since it doesn\u0026rsquo;t have any text.\nAlso, each attribute will have a different kind of data that it can be set to. Some attributes take colour values, some take pixel values, some take decimal values, some take something else. Some attributes can have fairly complicated values assigned to them. We\u0026rsquo;ll look at those much later.\nIf you are curious, there\u0026rsquo;s a web page with a reference guide to JavaFX CSS.\nLoading a Stylesheet # As previously noted, a stylesheet is just a file, and it\u0026rsquo;s treated as a resource in Java. So you put it in the \u0026ldquo;resources\u0026rdquo; folder in the \u0026ldquo;main\u0026rdquo; section of your project and access it through Class.getResource().\nVirtually all Nodes in JavaFX can have a stylesheet added to them. In order to work, though, the Node that you attach your stylesheet to has to be the \u0026ldquo;root\u0026rdquo; of the Scene. We\u0026rsquo;ll see how this works in our example.\nHere\u0026rsquo;s our stylesheet:\n.root { prompt-colour: #a04000; greeting-colour: #6000b0; } .greeting-label { -fx-text-fill: greeting-colour; -fx-font-size: 32px; } .prompt-label { -fx-text-fill: prompt-colour; -fx-font-size: 16px; -fx-font-weight: bold; } We\u0026rsquo;ve defined two custom colours in \u0026ldquo;.root\u0026rdquo;. One called \u0026ldquo;prompt-colour\u0026rdquo;, which is a dark orange, and another called \u0026ldquo;greeting-colour\u0026rdquo; which is purple. Then we\u0026rsquo;ve created two Class Selectors, one called \u0026ldquo;.greeting-label\u0026rdquo; and the other called \u0026ldquo;.prompt-label\u0026rdquo;. Each one assigns a colour to the text, and a size for the text. In addition, \u0026ldquo;.prompt-label\u0026rdquo; also sets the font weight.\nIt\u0026rsquo;s usually best to name your Class Selectors in a functional way. In this case, \u0026ldquo;.greeting-label\u0026rdquo; is very, very specific and probably not a great name. On the other hand, \u0026ldquo;.prompt-label\u0026rdquo; is likely to be used anytime you have a Label + TextField combination, and is a pretty good description of what it does.\nBoth of these Class Selector names end in \u0026ldquo;-label\u0026rdquo; to indicate that they are intended to be used with Labels in the layout. This seems to be a best practice.\nHere\u0026rsquo;s the application, updated to use the stylesheet:\npublic class Main extends Application { public static void main(String[] args) { launch(args); } private final StringProperty greeting = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty name = new SimpleStringProperty(\u0026#34;\u0026#34;); @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContents(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContents() { VBox results = new VBox(20, createInputRow(), createOutputLabel(), createButton()); results.setAlignment(Pos.CENTER); results.getStylesheets().add(this.getClass().getResource(\u0026#34;/css/beginners.css\u0026#34;).toExternalForm()); return results; } private Button createButton() { Button results = new Button(\u0026#34;Hello\u0026#34;); results.setOnAction(evt -\u0026gt; setGreeting()); return results; } private HBox createInputRow() { TextField textField = new TextField(\u0026#34;\u0026#34;); textField.textProperty().bindBidirectional(name); Label namePrompt = new Label(\u0026#34;Name:\u0026#34;); namePrompt.getStyleClass().add(\u0026#34;prompt-label\u0026#34;); HBox hBox = new HBox(6, namePrompt, textField); hBox.setAlignment(Pos.CENTER); return hBox; } private Node createOutputLabel() { Label results = new Label(\u0026#34;\u0026#34;); results.getStyleClass().add(\u0026#34;greeting-label\u0026#34;); results.textProperty().bind(greeting); return results; } private void setGreeting() { greeting.set(\u0026#34;Hello \u0026#34; + name.get()); } } And here\u0026rsquo;s what it looks like when it\u0026rsquo;s running:\nOnly three things have been changed in the code.\nFirst, in createContents() there\u0026rsquo;s a call to results.getStylesheets() which returns an ObservableList of stylesheets attached to the Node. Then we add our stylesheet to it after fetching it as a resource. Now we have an active stylesheet.\nNext, in createInputRow() we\u0026rsquo;ve done two things. First, we\u0026rsquo;ve instantiated our prompt Label as a variable, instead of inside the constructor of the HBox. This lets us call its getStyleClass() method. This also returns an ObservableList, and we can add a Class Selector from our stylesheet to it. The Class Selector is added here without the \u0026ldquo;.\u0026rdquo; prefix - that\u0026rsquo;s just the way you do it. So here we assign it the \u0026ldquo;prompt-label\u0026rdquo; class selector.\nFinally, we add the \u0026ldquo;greeting-label\u0026rdquo; class selector to the label returned from createOutputLabel() method.\nOne Last Thing # Did you notice that in order to add styling to namePrompt we had to instantiate it separately so that we could then call its getStyleClass() method?\nThis is an aspect of JavaFX which can get a bit frustrating. There\u0026rsquo;s a lot of boilerplate code you need to use to do some fairly simple and common things. None of it is very difficult, but it can clutter up your code pretty quickly. In this case, now we have 3 lines of code where we used to have one. It\u0026rsquo;s instantly just that little bit harder to read.\nThe other thing that you might notice is that exactly the same pattern happens in createOutputLabel(). It\u0026rsquo;s a different Class Selector, but the same basic operation is happening. This violates the principle of \u0026ldquo;Don\u0026rsquo;t Repeat Yourself\u0026rdquo; (DRY).\nWith JavaFX, one of the best tactics you can use to keep the boilerplate out of your layout code, and to keep it as simple as possible to read is to follow DRY religiously. This is one of the most important concepts to understand.\nLet\u0026rsquo;s look at how you would do it here:\npublic class Main2 extends Application { public static void main(String[] args) { launch(args); } private final StringProperty greeting = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty name = new SimpleStringProperty(\u0026#34;\u0026#34;); @Override public void start(Stage primaryStage) { Scene scene = new Scene(createContents(), 400, 200); primaryStage.setScene(scene); primaryStage.show(); } private Region createContents() { VBox results = new VBox(20, createInputRow(), createOutputLabel(), createButton()); results.setAlignment(Pos.CENTER); results.getStylesheets().add(this.getClass().getResource(\u0026#34;/css/beginners.css\u0026#34;).toExternalForm()); return results; } private Button createButton() { Button results = new Button(\u0026#34;Hello\u0026#34;); results.setOnAction(evt -\u0026gt; setGreeting()); return results; } private HBox createInputRow() { TextField textField = new TextField(\u0026#34;\u0026#34;); textField.textProperty().bindBidirectional(name); HBox hBox = new HBox(6, styledLabel(\u0026#34;Name:\u0026#34;, \u0026#34;prompt-label\u0026#34;), textField); hBox.setAlignment(Pos.CENTER); return hBox; } private Node createOutputLabel() { Label results = styledLabel(\u0026#34;\u0026#34;, \u0026#34;greeting-label\u0026#34;); results.textProperty().bind(greeting); return results; } private Label styledLabel(String contents, String classSelector) { Label results = new Label(contents); results.getStyleClass().add(classSelector); return results; } private void setGreeting() { greeting.set(\u0026#34;Hello \u0026#34; + name.get()); } } What we\u0026rsquo;ve used here is the \u0026ldquo;Builder Pattern\u0026rdquo;. We\u0026rsquo;ve created a method called styledLabel() that is a \u0026ldquo;builder\u0026rdquo;. It\u0026rsquo;s a utility method that allows us to create a Label with a particular styling in a single call. It removes the repeated pattern, and allows us to avoid instantiating promptLabel as a variable altogether, so that now it can be called directly as a constructor parameter for the HBox.\nThis is a very small application with a simple layout, so the repeated pattern and the extra lines of code are not really a big issue here specifically. But this gets out of hand quickly as the size of the application grows, and the difference in code clarity between following DRY or not becomes enormous when an application is even moderately complicated.\n","externalUrl":null,"permalink":"/beginners/part4/","section":"JavaFX","summary":"JavaFX includes Cascading Style Sheets for styling the elements of your GUI.  This is a quick guide about how to use them.","title":"Part 4 - Styling","type":"javafx"},{"content":" Introduction # Recently, I posted an article titled, Should You Use FXML . In that article, I stated that I felt that well written and organized code would always be easier to maintain than any corresponding FXML/FXML Controller would be. I did not emphasize this, as the article was intended to be a discussion about the merits and costs of using FXML more than as a \u0026ldquo;is this better than this?\u0026rdquo;, exploration.\nIn the article, I included a sample of a large (462 line) FXML file that I grabbed somewhat randomly from GitHub. Some readers expressed the opinion that the FXML was \u0026ldquo;bad\u0026rdquo;, and that they write better FXML by hand. Some expressed concern that I had cherry-picked bad FXML to use as an example, while others seemed upset at the idea of randomly picking FXML as an example.\nI wasn\u0026rsquo;t sure about how to respond to these comments because I realized that the frame of reference between myself and these commentors was just too different. It really didn\u0026rsquo;t matter to me if the FXML example that I picked was good, bad or average.\nIt didn\u0026rsquo;t matter because I understand that the way that I write layout code is astronomically better than FXML can ever be.\nBut I couldn\u0026rsquo;t say that. Who would believe me? What makes me think my coding is so much better?\nThis article is my attempt to explain my position. I\u0026rsquo;m going to take that example FXML and its FXML Controller code (and other stuff it turns out it needs to work), and re-write it as purely coded layout in Kotlin.\nMy hope is that you can look at the original FXML and code, and then look at my version, and you will see the potential for writing your layouts by hand.\nAnd this is not about me or my coding skill. There\u0026rsquo;s nothing in my version that anyone reading this article couldn\u0026rsquo;t do themselves, or learn to do themselves.\nApproach # Generally speaking, the goal is to reproduce the layout as close as possible to the original, so that it becomes clear just how much easier it is to understand and maintain a hand coded layout. However, there are secondary considerations that need to be taken into account:\nImproving the Layout I was torn about this at first. Eventually, I decided that there might be aspects of layout design that are adversely affected by using SceneBuilder to create the FXML file. It might be obvious, when hand-coding, that the same look and feel can be achieved through a better design. I have chosen to implement these improvements as these are generally problems that wouldn\u0026rsquo;t arise coding the layout by hand.\nNote This shows that the impact of hand-coding goes beyond being simply a matter of clarity and maintainability, but also affects the application design.\nImplementing a Framework The orginal design, like most FXML implementations, has too much functionality in the FXML Controller. This is probably through the misplaced belief that the FXML Controller acts as an MVC Controller. In order to understand how a hand-coded layout is better in this respect, a MVCI framework has been applied. This results in the creation of a Presentation Model, and moving all of the application logic found in the FXML Controller into the Interactor.\nChanging to a Reactive Design It\u0026rsquo;s clear to me that JavaFX is intended to be used as a Reactive framework, and that it just works better that way. Systems built with a Reactive approach are simpler and cleaner, and easier to understand.\nThreading and the FXAT The FXML Controller doesn\u0026rsquo;t have any code that attempts to perform potentially blocking (like file access) operations off the FXAT. There\u0026rsquo;s no point in creating bad code for conversion, so I\u0026rsquo;ve organized the code such that thread handling is performed as it should be.\nWidgetsFX Since this is intended as an example of how a real application would be put together, I have chosen to use my own WidgetsFX library to implement many of the builders, extension functions and helper classes that I would ordinarily use to build layouts. I\u0026rsquo;m not including any of that code here, but you can easily tell what it would do.\nNote I also freely admit that I worked with the WidgetsFX project open, and that I added new functionality to it as required. This is the way that I would ordinarily work, and it\u0026rsquo;s also how the WidgetsFX library grows organically over time.\nStyling I\u0026rsquo;ve chosen to move any styling in the FXML or the FXML Controller into an external style sheet. Then I\u0026rsquo;m not going to create that style sheet because\u0026hellip;why bother. The result is that the screens in my project, while having an identical effective layout, don\u0026rsquo;t look like the originals\u0026hellip;but that\u0026rsquo;s not the point.\nKotlin # I\u0026rsquo;m writing this all in Kotlin for two reasons. Firstly, I find Java painful and unsatisfying to code with after several years of writing mostly Kotlin. Secondly, Kotlin just makes it so much easier to write clear, easy to understand layout code.\nI think that, even if you don\u0026rsquo;t fully understand the syntax, the Kotlin code is easy enough to understand for most Java programmers. Take a look at this:\nVBox(10.0).apply { padding = Insets(25.0) . . . children += intSpinnerOf(1, 50, \u0026#34;standard-spinner\u0026#34;, model.connectorThickness.asObject()) .setStep(1) .bindDisable(model.selectedDistance.isNull) .withInitialDelay(Duration.millis(500.0)) .withRepeatDelay(Duration.millis(500.0)) } } You don\u0026rsquo;t really need to know the details of how the extension function .apply{} works to understand that the code inside the {} configures the VBox. You can see that the padding is set to 25px, even though you don\u0026rsquo;t understand that Kotlin allows you to refer directly to values of fields that have getters and setters and that it will still call those getters and setters. Then, children += is clearly using the += operator on a List to do the equivalent of getChildren().add().\nIt\u0026rsquo;s also clear that setStep(), bindDisable(), withInitialDelay() and withRepeatDelay() are all configuration methods for the Spinner. Since they are chained, all of these methods are designed as decorators.\nIt\u0026rsquo;s obviously not clear what the parameters of intSpinnerOf() are, but Intellij displays what the parameter names are. So when you really are working with this code, there is no question what the parameters mean.\nFor purposes of this article, pretty much any Java programmer can look at this snippet of code and understand that it defines a VBox with a particular padding and that it contains a Spinner\u0026lt;Integer\u0026gt; that has been configured in a particular way and bound to some Property in the Model. That\u0026rsquo;s probably enough to get the point.\nThe Original Version # You can find this project on GitHub here.\nI was concerned that the project might be deleted or change beyond recognition over time, so I placed a snapshot of the FXML file, the FXML Controller and a few supporting files in this . I\u0026rsquo;m not going to include all the code in this article itself, because it\u0026rsquo;s just going to be too big.\nWhen the program is running, the screen looks like this, although this version from the read.me page appears to be out of date and doesn\u0026rsquo;t quite match what the code does:\nThe code we are working on is the control panel in the upper left corner, so here it is close-up:\nUnfortunately, we don\u0026rsquo;t have screen shots of all of the Tabs in the control panel, but you can get a sense of the design from this.\nThere\u0026rsquo;s obviously some pretty strong styling going on here, it looks like maybe they used AtlantaFX? I didn\u0026rsquo;t go looking, and I\u0026rsquo;m not going to make any attempt to duplicate it. There is zero code or FXML that applies any of this styling, so I\u0026rsquo;m deeming it \u0026ldquo;out of scope\u0026rdquo;.\nNotes While Performing the Conversion # As I went through the process of performing the conversion, I made a point of taking some notes about issues that I encountered and ideas that occured to me\u0026hellip;\nThis Was Probably Generated From SceneBuilder # I came across this in the FXML file:\n\u0026lt;HBox alignment=\u0026#34;CENTER\u0026#34; spacing=\u0026#34;10.0\u0026#34; GridPane.columnSpan=\u0026#34;2147483647\u0026#34; GridPane.rowIndex=\u0026#34;8\u0026#34;\u0026gt; I cannot see any human entering \u0026ldquo;2147483647\u0026rdquo; as a columnSpan value. So this is either generated from SceneBuilder, or copypasta from some section that was generated by SceneBuilder.\nWhy does this matter? If the contention is that FXML is clear and easy to read, and yet we can blame most of the strange structures that make this file difficult to understand on SceneBuilder - then that\u0026rsquo;s an issue.\nThe Design is Difficult to Understand # It\u0026rsquo;s monolithic. 462 lines of FXML, and you need to scan all of it to understand that it\u0026rsquo;s basically just 4 Tabs in a TabPane. The GridPanes were a particular chore, as the components were not organized in the FXML file at all, just jumbled up willy-nilly and difficult to locate.\nNote All in all, I spent more time trying to understand the FXML and FXML Controller than I did writing my own version.\n\u0026ldquo;Bad\u0026rdquo; FXML Doesn\u0026rsquo;t Appear Be More Verbose # It has become clear that you wouldn\u0026rsquo;t write FXML like this by hand. It\u0026rsquo;s just too jumbled up for that, and probably crosses into the realm of \u0026ldquo;bad\u0026rdquo; FXML. However, there really isn\u0026rsquo;t anything in its jumbled-upness that makes it longer or more verbose. As a matter of fact, there are things about it - like the lack of \u0026ldquo;row\u0026rdquo; or \u0026ldquo;column\u0026rdquo; specifications for GridPane elements in row or column 0 - that make it a bit less verbose.\nThe inescapable truth seems to be that if you are going to create a layout of this complexity, then you are going to need 450+ lines of FXML to do it. No matter how good your FXML writing skills are.\nDealing With FileChooser # In most of my programming, I don\u0026rsquo;t use FileChooser much at all, but this project and this screen do. So I had to grapple with the question of \u0026ldquo;Where does it go in the MVCI structure?\u0026rdquo;. It can be argued that FileChooser is a GUI element, and therefore belongs to the View, and that\u0026rsquo;s were I initially placed it. But then I ended up with references to File objects, and that caused me to look closer at this approach.\nFirst off, File is most distinctly not a front end class. It seemed worrying to have this data type handled by the GUI code.\nSecondly, the File had to be passed back to the Interactor somehow, because it\u0026rsquo;s the Interactor that was going to use it. This made it really clear to me that there was an issue. Why does the View get to know anything about the back-end structure. Furthermore, the View doesn\u0026rsquo;t actually use the File object, it just passes it back to the Controller.\nNow, what happens if the storage is changed from JSON files to a database? Or the application is changed to facilitate sharing designs between different users by email? Would that mean that you would have to change the View in order to make this change?\nFor sure, that FileChooser would need to change. Maybe it becomes a Dialog that allows for load and save to a database, or to select an email with attachments (or to send an email). But there\u0026rsquo;s no reason that you should have to change the View for that.\nIn the end, I moved the FileChooser calls into the Controller, which is where I think they really belong. It\u0026rsquo;s now integrated with the thread handling and invocation of Interactor methods. The File object is no longer tramp data passing from the View through the Controller to the Interactor.\nUnused? Elements # In the Tab labeled \u0026ldquo;PCA\u0026rdquo; there are two RadioButtons, \u0026ldquo;Use Hyperspace\u0026rdquo; and \u0026ldquo;Use Hypersurface\u0026rdquo; that do NOT have their isSelected values used in any code in the FXML Controller.\nHowever, the ToggleGroup to which they belong, pcahyperSourceGroup is NOT private to the FXML Controller. So it is possible that some other class with a reference to this FXML Controller is accessing this ToggleGroup, getting a reference to the RadioButton that is selected, and (Gasp!!!) checking it\u0026rsquo;s text value to find out which RadioButton it is.\nDon\u0026rsquo;t try this at home.\nNeedless to say, I\u0026rsquo;m not sure if these RadioButtons do or do not do anything - and that should be immediately apparent from reading the code. I\u0026rsquo;ve connected these RadioButtons to some elements in the Model that are clearly named as some kind of \u0026ldquo;dummy\u0026rdquo; value.\nIt may be that I grabbed the code in the middle of development and it just wasn\u0026rsquo;t yet complete. There were several new releases of this project between writing and publishing this article. On the other hand, this may just be something that somehow got lost and forgotten in the hundreds of lines of code here.\nConnecting to the Rest of the Application # When you look at the screen snaps on the project Read.me page, it\u0026rsquo;s clear that this screen is supposed to be a control/input screen for a real-time display elsewhere in the application. As such, there needs to be a way to communicate with the rest of the application in real-time.\nWe see that the original code implements this communication through EventHandlers, like this:\nscene.addEventHandler(ManifoldEvent.DISTANCE_CONNECTOR_SELECTED, e -\u0026gt; { } for incoming Events, and this:\nscene.getRoot().fireEvent(new ManifoldEvent(ManifoldEvent.DISTANCE_CONNECTOR_WIDTH, item.getDistance())); for outgoing Events.\nThis is probably one of the most insidious issues that can occur when you forego implementing a proper framework. The code you are writing is Event centric, so then everything is now an Event. Even when it really isn\u0026rsquo;t. I\u0026rsquo;ll discuss this more when we look at the new design.\nChanges to the Design # It was clear when doing the conversion that there were \u0026ldquo;issues\u0026rdquo; with the original design. Furthermore, it was clear that it would not make sense to leave these issues unaddressed when performing the conversion.\nIssues With the Original Design # Most of these issues, as far as I can tell, are a direct result of utilizing FXML in the way that most of the tutorials, including those from Oracle, tell you to. From what I can see, these issues build on each other. Let\u0026rsquo;s take a look at them:\nThere is no Framework # The original implementation assumed that the FXML file was the \u0026ldquo;View\u0026rdquo; and the FXML Controller was the \u0026ldquo;Controller\u0026rdquo; and the domain objects collectively comprised the \u0026ldquo;Model\u0026rdquo;. The results is that virtually all of the code resides in the FXML Controller and it does way, way too much while doing none of it well.\nThis is, in my opinion, a big problem with the design of this application, and this clearly shows how the automatic \u0026ldquo;separation of concerns\u0026rdquo; claimed for FXML is just a myth. There is no separation here, you have application logic, file handling and communication with other parts of the application muddled up with the configuration of individual Nodes. Within the layout itself, you have extensive coupling of Nodes all over the layout.\nThere is no Presentation Model # All of the data is stored in the screen Nodes, and there is no external data storage outside of the value Properties of the Nodes themselves. This means that all of the data must to be scraped out of the Nodes when it\u0026rsquo;s needed, which, in turn, means that all of the Node variables have to be globally scoped so that their data is available when required. Globally scoping those variables is a massive source of coupling within the FXML Controller.\nOf course, the design of FXML requires that all of the Nodes from the layout that are going to be accessed from the FXML Controller are instantiated as fields in the FXML Controller, which means that they are globally scoped. This makes it harder to see the benefits of attempting to limit the scope of these variables by implementing a Presentation Model.\nFinally, without a Presentation Model, it\u0026rsquo;s much more difficult to share the data with the application logic. This is obscured in this application by including the application logic inside the FXML Controller.\nIt is an \u0026ldquo;Action\u0026rdquo; Based Design # Without a Presentation Model to act as a data representation of the \u0026ldquo;State\u0026rdquo; of the GUI, it is very difficult to create a Reactive design. Instead of linking GUI elements together through the Presentation Model, they are directly referenced by other GUI elements.\nThe effect of this is that the application becames Node-centric, instead of data centric, and this shift, in turn, leads to an \u0026ldquo;Action\u0026rdquo; based design.\nUsing Events to Communicate Between Screens # The elements in this screen are used to control the manner in which data in other windows and screens is displayed. This means that this screen needs to communicate somehow with other windows and screens that are external to this layout. The programmers have decided to use JavaFX Events to do this. Consider this snippet of code:\nconnectorThicknessSpinner.valueProperty().addListener(e -\u0026gt; { DistanceListItem item = distancesListView.getSelectionModel().getSelectedItem(); if (null != item) { Integer width = (Integer) connectorThicknessSpinner.getValue(); item.getDistance().setWidth(width); scene.getRoot().fireEvent( new ManifoldEvent(ManifoldEvent.DISTANCE_CONNECTOR_WIDTH, item.getDistance())); } }); Here we have a Spinner configured such that every time its value changes a Listener is triggered and that Listener will eventually fire a ManifoldEvent of a certain type, in this case ManifoldEvent.DISTANCE_CONNECTOR_WIDTH type.\nPresumably, some external EventHandler is configured to respond to this ManifoldEvent and do something.\nOn the surface, this seems like a reasonable approach: The programmers needed a reliable messaging facility to communicate between components, and the JavaFX Event system seems to fit the bill. But there are some problems with this:\nSequencing can be a problem. Events invoke EventHandlers which are submitted to the FXAT. If a system is very active, it\u0026rsquo;s possible that there\u0026rsquo;s a lot of items in the FXAT queue, and they may have changed the environment in which the EventHandler will run. You cannot control this.\nIt deals in domain objects. That call to item.getDistance() returns a Distance which is a domain object.\nRelated to the previous item, the domain objects are stored in singleton Lists in Distance and Manifold. This leads to timing issues where domain objects might exist in the singleton lists but not in the ListViews yet. There is code that defends against this and generates errors when this happens.\nDelivery is not guaranteed. There\u0026rsquo;s nothing to say that some screen element couldn\u0026rsquo;t filter an Event before it gets to its intended target. This might be hard to debug. Essentially, the Event system is global and couples everything that uses it.\nIt\u0026rsquo;s not clear who the recipient is. Events are, by definition, broadcast entities. To find out how this Event is handled, you\u0026rsquo;ll have to search through the entire application to see which classes have an EventHandler for the ManifoldEvent.DISTANCE_CONNECTOR_WIDTH subtype.\nPersonally, I think that Events and EventHandlers are best used for very localized things. This would mean adding an EventHandler onto the Button that generates the Event (like a click ActionEvent). Using the JavaFX Event system as a general communication bus feels like a \u0026ldquo;code smell\u0026rdquo; to me.\nThe ListViews # This is the only part of the design which is objectively \u0026ldquo;wrong\u0026rdquo; from a technical JavaFX respect.\nThere are two classes for each ListView, let\u0026rsquo;s look at the one for manifolds. We have the class ManifoldListItem which extends VBox, and the class Manifold which we should probably consider to be a \u0026ldquo;Domain Object\u0026rdquo;. Every ManifoldListItem contains a reference to a Manifold.\nThe biggest problem is that the ListView is defined as ListView\u0026lt;ManifoldListItem\u0026gt; and then has no ListCell defined anywhere. This violates one of the principal rules of ListView and TableView: Do NOT put Nodes as the items.\nI don\u0026rsquo;t even know how this works. Honestly, this was the one thing that took me the closest to actually downloading the whole project and building it to see if it actually works.\nThe Manifold class has a static HashMap field that stores all of the Manifolds that have been created - essentially a singleton that can be accessed through the whole application. Whenever a new Manifold is created, there should also be an ManifoldEvent fired that should trigger an EventHandler added to the Scene. That EventHandlercreates a new ManifoldListItem and adds it to the ListView.\nIf the manifold HashMap is cleared, there should be an accompanying ManifoldEvent fired which trigger a different EventHandler set on the Scene.\nThere are three ColorPickers outside the ListView that display colours associated with the item selected in the ListView. There is code that updates the value in these ColorPickers when an ManifoldEvent indicating that a new Manifold has been selected. Also, when the value in the Spinners is changed, it fires a ManifoldEvent that presumably gets handled externally and updates the Manifold.\nIf all of this seems very confusing and roundabout, that\u0026rsquo;s because it is. This is one of those cases where doing it wrong is so, so much more complicated than doing it right. We\u0026rsquo;ll see how this works in the next section.\nThe New Design # The new version uses MVCI as a framework to implement a Reactive design. There is a Presentation Model composed of Observable data classes which are then bound (mostly bi-directionally) to the value Properties of the screen Nodes. This means that the layout elements are instantiated, configured, bound to the Presentation Model and then added to the layout and forgotten. There is no need to ever reference them again, as everything important about them has been bound to Properties in the Presentation Model.\nThere is no need to \u0026ldquo;scrape\u0026rdquo; data out of the GUI Nodes.\nAll of the application logic has been moved out of the layout code and into the Interactor. The Interactor is suprisingly small, because much of complexity of communication with the external screen has now been eliminated through shared data.\nThere is a SharedModel class which contains JavaFX Observable objects that are supplied by whatever element of the overall application contains this screen.\nThe Controller, as usual, handles instantiation, threading and connectivity to the rest of the application\u0026rsquo;s GUI.\nThe ListViews # This was the biggest architectural change to the design. The Manifold and Distance domain objects were transformed into Presentation Objects and all of the singleton related stuff was removed. Now they are simple JavaFX Observable POJO`s.\nThe ListViews were changed to have custom ListCells that mirrored the structure in the ManifoldListItem and DistanceListItem classes. These ListCells contain interactive Nodes like TextField and CheckBox who\u0026rsquo;s values are bi-directionally bound to the corresponding Properties in the item currently loaded into the ListCell.\nTo handle the values inside Manifold and Distance that are updated from outside the ListView, the Model has two Properties:\nval selectedDistance: ObjectProperty\u0026lt;Distance\u0026gt; = SimpleObjectProperty() val selectedManifold: ObjectProperty\u0026lt;Manifold\u0026gt; = SimpleObjectProperty() These need to be synchronized with the current selection in the ListViews. Ordinarily you could do this:\ninfix fun \u0026lt;T : Any\u0026gt; ListView\u0026lt;T\u0026gt;.bindSelection(boundProperty: ObjectProperty\u0026lt;T\u0026gt;): ListView\u0026lt;T\u0026gt; = apply { boundProperty.bind(selectionModel.selectedItemProperty()) } But there\u0026rsquo;s a twist here\u0026hellip;\nThe selected item needs to be synchronized with an external selection of those objects from some other part of the application. This needs to be in both directions. That external component needs to be able to select these objects, and react to changes in the selection from these ListViews. Ordinarily, you\u0026rsquo;d just make the bind bi-directional, but there\u0026rsquo;s an issue there:\nfinal ReadOnlyObjectProperty\u0026lt;T\u0026gt; selectedItem Oh no! The selectedItem property in SelectionModel is read-only! The only way to change the value programmatically is to call SelectionModel.selectItem(). This means we\u0026rsquo;ll have to use subscriptions to handle the changes:\ninfix fun \u0026lt;T : Any\u0026gt; ListView\u0026lt;T\u0026gt;.connectSelection(connectedProperty: ObjectProperty\u0026lt;T\u0026gt;): ListView\u0026lt;T\u0026gt; = apply { selectionModel.selectedItemProperty().subscribe { newItem -\u0026gt; connectedProperty.value = newItem } connectedProperty.subscribe { newItem -\u0026gt; selectionModel.select(newItem) } } If you think about it, you\u0026rsquo;ll realize that triggering the subscription on one Property due to a change in the other won\u0026rsquo;t cause an infinite loop because the result of that subscription will bring the two values in sync and won\u0026rsquo;t trigger the second subscription.\nYou can now see that this screen doesn\u0026rsquo;t even need to know about the domain objects Manifold and Distance any more. These elements - if they exist at all - are created somewhere else and then presumably used to create Manifold and Distance presentation data objects.\n\u0026ldquo;Drag \u0026rsquo;n Drop\u0026rdquo; File Import # Right near the top of the FXML Controller, there is some code that initialized the \u0026ldquo;root\u0026rdquo; of the Scene as the destination for a drag \u0026rsquo;n drop operation to load a configuration file. The idea being, I assume, that you can just drag a file from a file manager application into this layout and it will load it in if it is, in fact, a configuration file.\nI wrestled with the idea of putting the EventHandlers for drag and drop into the layout itself, but then realized that I\u0026rsquo;d have to supply an action handler for this to the ViewBuilder from the Controller. The end result would be that I would have a File object floating around in my layout code, even if indirectly. This part bothered me.\nI came to the conclusion that the drag and drop didn\u0026rsquo;t have anything to do with the layout as a layout. This would be different if there was a box in the layout that said \u0026ldquo;Drop Files Here\u0026rdquo; and only that box would respond to the drag and drop operation. In this situation, we don\u0026rsquo;t have anything like this, and the entire layout can just be considered as a Node (in this case a Region, but we only care about it as a subclass of Node) and dealt with from the outside.\nThis means that it makes sense to add the drag and drop handling as a decorator onto the layout directly from the Controller. Now, we don\u0026rsquo;t have to pass any handlers over to the ViewBuilder, since it isn\u0026rsquo;t involved. In fact, neither the ViewBuilder or the layout itself has any knowledge that it is a drag and drop destination.\nIn the Controller, the getView() method now looks like this:\nfun getView(): Region = viewBuilder.build() asFileDrop { loadUmap(it[0]) } It\u0026rsquo;s worth looking at the WidgetsFX implementation of this, since it is a great example of how the boilerplate can be stripped out of your application code:\ninfix fun \u0026lt;T : Node\u0026gt; T.asFileDrop(handler: (List\u0026lt;File\u0026gt;) -\u0026gt; Unit): T = apply { addEventHandler(DragEvent.DRAG_OVER) { event -\u0026gt; event.acceptTransferModes(TransferMode.COPY) } addEventHandler(DragEvent.DRAG_DROPPED) { event -\u0026gt; with (event.dragboard) { if (hasFiles()) { handler.invoke(files) } } } } Everything in here is boilerplate and every time you implement this you would need those 11 lines of code. But now it just becomes asFileDrop {do something}.\nThe Large Number of Action Handlers # There\u0026rsquo;s a lot of Buttons in this screen, and all of them trigger some kind of action within the application logic.\nQuestion How do we provide all these action handlers to the View?\nThe first thing to understand is that the Reactive nature of the new design means that all of the data that might be relevant to any action is already represented in the Model and is always fully up to date.\nNote This means that no action handler ever has to supply data from the GUI. The only information that is required is which Button was clicked.\nThe few actions that need to open FileChoosers need to pass the Window (Stage) that triggered it as FileChooser needs this to work. I chose to implement these such that the Button would grab this element as part of its EventHandler that runs at click-time. These actions do need to pass that element back to the action handler.\nTwo Enums were created, one for the FileChooser operations, called FileOperation and the other for all the other operations, called GeneralOperation. \u0026ldquo;Operation\u0026rdquo; seems less likely to get confused with \u0026ldquo;Action\u0026rdquo;. Naming things is hard! The action handler is a Consumer\u0026lt;GeneralOperation\u0026gt;, or in Kotlin notation (GeneralOperation) -\u0026gt; Unit, and each Button will call the handler\u0026rsquo;s invoke(GeneralOperation) method, passing the appropriate GeneralOperation value to it.\nIt is the Controller\u0026rsquo;s job to define this action handler and provide it to the ViewBuilder. Unless there is some thread handling required in the nature of any of these actions, the Controller has no business getting involved in the execution of these actions, which is the domain of the Interactor. This action handler is essential a \u0026ldquo;dispatch routine\u0026rdquo;, which invokes an appropriate Interactor method for each operation type via the Kotlin version of switch:\nprivate fun generalOperation(operation: GeneralOperation) { when (operation) { GeneralOperation.BUILD_CLUSTER -\u0026gt; interactor.buildCluster() GeneralOperation.GENERATE -\u0026gt; interactor.generate() GeneralOperation.CLEAR_ALL -\u0026gt; interactor.clearAll() GeneralOperation.EXPORT_ALL -\u0026gt; interactor.exportAll() GeneralOperation.CLEAR_DISTANCES -\u0026gt; interactor.clearDistances() GeneralOperation.PROJECT -\u0026gt; interactor.project() GeneralOperation.EXPORT_MATRIX -\u0026gt; interactor.exportMatrix() GeneralOperation.SAVE_PROJECTIONS -\u0026gt; interactor.saveProjections() GeneralOperation.RUN_PCA -\u0026gt; interactor.runPca() } } For all practical purposes, most of these operations do not have any real application logic associated with them that resides inside of this MVCI framework. They instead are triggering actions elsewhere in the application. We\u0026rsquo;ll look at that next\u0026hellip;\nConnecting to the Rest of the Application # There is a SharedElements class which contains JavaFX Observable objects that are supplied by whatever element of the overall application contains this screen. This is the main source of coupling to the rest of the application\u0026rsquo;s GUI. Sharing these data elements has the magic effect of making all the Event firing and EventHandlers in the original version totally redundant.\nLet\u0026rsquo;s look at how this works\u0026hellip;\nIn a situation where this screen is part of a larger application, whatever GUI element of that application that \u0026ldquo;owns\u0026rdquo; this screen would pass a SharedElements object to the Controller via its constructor. This SharedElements object is then passed to the Model via its constructor. At this point, the Controller\u0026rsquo;s involvement with the SharedElements is done.\nSharedElements is a set of Properties which are defined externally. These are used to instantiate the Property fields in the Model. Like this:\nval showWireFrame: BooleanProperty = sharedElements.showWireFrame val showControlPoints: BooleanProperty = sharedElements.showControlPoints It\u0026rsquo;s important to note that since these elements are incorporated into the Model, just like any other elements, the fact that these are Properties defined externally is never exposed to the View or the Interactor. Any changes to the manner in which these are implemented, connected or related to the Properties in the Model are never going to ripple through to be changes in the View or the Interactor.\nIn a similar manner an object of the class SharedFunctions is passed to the constructor of the Controller, which calls it externalFunctions. This is, in turn, passed to the Interactor via its constructor. At this point, the Controller\u0026rsquo;s involvement with SharedFunctions is complete.\nThe Interactor then invokes these external functions as part of the application logic related to performing various actions related to Button clicks in the View.\nOn the surface this seem very round-about but it actually isolates the coupling nicely. The View has a Button that triggers an EventHandler. That EventHandler invokes an action Consumer that is defined by the Controller. That Consumer invokes an corresponding method in the Interactor which, in turn, invokes a Runnable provided to the Interactor from outside the MVCI construct through the Controller.\n{: .notice\u0026ndash;primary :} Information about what these pieces do is available on a strict \u0026ldquo;need to know\u0026rdquo; basis throughout the framework. There is an Enum which is shared between the Controller and the ViewBuilder that defines what operations can be invoked from the View, but the View has no idea what those operaitons do. The Controller knows how to invoke corresponding methods in the Interactor, but has no idea what those methods do. The Interactor knows which external operations to call, but it has no idea what they do. Finally, the Controller passes the list external operation handlers to the Interactor, but doesn\u0026rsquo;t know what they are.\nCounting Lines of Code/FXML # I\u0026rsquo;m not generally a fan of counting code, but it can be an indicator of the complexity, readability and maintainability of a system.\nLet\u0026rsquo;s look at the original:\nElement Lines FXML 462 FXML Controller 752 ManifoldListItem 76 DistanceListItem 55 Manifold 130 Distance 190 Total 1665 Now, let\u0026rsquo;s look at the hand-coded version:\nElement Lines Model 60 Controller 48 Interactor 80 ViewBuilder 230 ManifoldListCell 21 DistanceListCell 24 SharedElements 12 SharedFunctions 12 Total 487 The hand-coded version is less than 1/3 the amount of code in the original version - although I\u0026rsquo;m not sure how compare FXML to lines of code. In any event, the entire hand-coded version is only slightly larger than the FXML file itself.\nIs it Easier to Understand? # I always feel that the most common use case for someone performing maintenance or enhancement to a layout is going to start out by looking at the actual running screen. Then they are going to want to get a feel for how some specific section of the layout is designed.\nIn this case, we have a bunch of Tabs and the programmer is probably going to want to drill down into the code for a specific Tab.\nLet\u0026rsquo;s take a look at the top of the layout code, were the root elements are defined:\noverride fun build(): Region = TabPane( createUmapTab(), createPcaTab(), createDistancesTab(), createHullTab() ) withClosingPolicy TabPane.TabClosingPolicy.UNAVAILABLE private fun createPcaTab() = Tab(\u0026#34;PCA\u0026#34;) withContents (VBox(10.0, pcaGridPane(), pcaButtonBox()) padWith 25.0) private fun createUmapTab() = Tab(\u0026#34;UMAP\u0026#34;) withContents VBox(10.0) .padWith(25.0) .addChild(umapGridPane()) .addChild(umapDistanceThresholdBox()) .addChild(umapControlBox()) private fun createDistancesTab() = Tab(\u0026#34;Distances\u0026#34;) withContents HBox(10.0, distanceLeft(), distanceRight()) private fun createHullTab() = Tab(\u0026#34;Hull Geometry\u0026#34;) withContents BorderPane() .withTop(hullTop()) .withLeft(manifoldPropertyBox()) .withCenter(manifoldBox()) We can see right away that the entirety of the layout is a single TabPane with 4 Tabs that cannot be closed. We have a short builder method for each Tab with an name that mirrors the title of each Tab. No matter which Tab you are interested in you can quickly find it, without even having to scroll down through the code.\nWe can quickly get an idea about the structure of the contents of these Tabs. Two are VBoxes, one is an HBox and the other is a BorderPane. There are builders for every element contained in these Regions in the Tabs, and we can click-through on them to get to them.\nYou can also see here that all of the configuration elements such setPadding() have been implemented as extension decorator functions that have also been implemented as \u0026ldquo;infix\u0026rdquo; functions. This means that they can be used without the . and () and can, in some cases, increase readability.\nIn some cases, it looks cleaner if the dot notation is used instead. This allows the decorators to be stacked vertically when they start to add up. However, when the composition is trivial, then the infix notation keeps the coding trivial. Compare the \u0026ldquo;PCA\u0026rdquo; Tab to the \u0026ldquo;UMAP\u0026rdquo; Tab.\nI\u0026rsquo;ve tried to avoid naming the builders with positional names whenever possible. However, I really don\u0026rsquo;t know what this application does, so it was hard to guess at good names for some builders. I gave up with the builder for the layout in BorderPane.top in the \u0026ldquo;Hull\u0026rdquo; Tab, and I just called it hullTop().\nI find that GridPanes are always clumsy to deal with, no matter what, and the row/column locations never jump out at you when scanning the code. However, you can organize the code to make it easier to find things. I tried this with the \u0026ldquo;UMAP\u0026rdquo; GridPane:\nprivate fun umapGridPane() = GridPane().apply { columnConstraints += ColumnConstraints(10.0, 200.0, 288.0).apply { hgrow = Priority.SOMETIMES } columnConstraints += ColumnConstraints(10.0, 200.0, 380.0).apply { hgrow = Priority.SOMETIMES } addRowConstraints(10, stdRowConst) umapSpinnerColumn() umapSliderColumn() } private fun GridPane.umapSliderColumn() { stackedSlider(\u0026#34;Repulsion Strength\u0026#34;, model.repulsionStr, 0.0, 2.0, 0.1, 1, 0) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Minimum Distance\u0026#34;, model.minimumDistance, 0.0, 0.6, 0.1, 1, 2) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Spread\u0026#34;, model.spread, 0.5, 1.5, 0.1, 1, 4) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Op Mix Ration\u0026#34;, model.opMixRatio, 0.0, 1.0, 0.1, 1, 6) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Target Weight\u0026#34;, model.targetWeight, 0.0, 1.0, 0.1, 1, 8) ticksOn true addStyle \u0026#34;std-slider\u0026#34; } private fun GridPane.umapSpinnerColumn() { stackedIntSpinner(\u0026#34;Number of Components\u0026#34;, model.numberOfComponents, 2, 5, 1, 0, 0) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Number of Epochs\u0026#34;, model.numberOfEpochs, 25, 500, 25, 0, 2) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Nearest Neighbours\u0026#34;, model.nearestNeighbour, 5, 500, 5, 0, 4) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Negative Sample Rate\u0026#34;, model.negativeSampleRate, 1, 250, 1, 0, 6) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Local Connectivity\u0026#34;, model.localConnectivity, 1, 250, 1, 0, 8) addStyle \u0026#34;std-spinner\u0026#34; } This GridPane has two columns, the one on the right has a bunch of Sliders with their Labels, and the one on the left has Spinners and their Labels. In both columns, the Label sits on the row above its corresponding input Control. It\u0026rsquo;s now easy to see the structure of the GridPane at a glance. In contrast, translating these GridPanes from the FXML took more time than any other part because you couldn\u0026rsquo;t just look at it quickly and understand the structure.\nIt\u0026rsquo;s not clear when you see the code in these articles that the IDE that I use (Intellij IDEA) provides a lot of on-screen information that\u0026rsquo;s not seen here. For instance, this is what I see with umapSliderColumn():\nFrom here it is clear what all of those parameters do.\nOne further thing, which I think contributes greatly to the \u0026ldquo;easier to understand\u0026rdquo; aspect of this discussion. You can see that ALL of the parameters related to these input Controls are included here in these 10 method calls. There\u0026rsquo;s no need to go running off somewhere else see how one of the Spinners is configured. Each one is also bi-directionally bound to a Property field in the Model, so there\u0026rsquo;s no need to mess about with initial values either - as that is handled in the Model or the Interactor.\nThe New Code # Oh, wow! That\u0026rsquo;s a lot of discussion and preamble, and not a lot of coding. Let\u0026rsquo;s take a look at the completed redesign.\nThe ViewBuilder # This is the bulk of the code\u0026hellip;\nclass FromFxmlViewBuilder( private val model: FromFxmlModel, private val fileOp: (Window, FileOperation) -\u0026gt; Unit, private val genOp: (GeneralOperation) -\u0026gt; Unit ) : Builder\u0026lt;Region\u0026gt; { private val stdRowConst = RowConstraints().apply { minHeight = 10.0 prefHeight = 30.0 vgrow = Priority.NEVER } override fun build(): Region = TabPane(createUmapTab(), createPcaTab(), createDistancesTab(), createHullTab()) .withClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE) private fun createPcaTab() = Tab(\u0026#34;PCA\u0026#34;) withContents (VBox(10.0, pcaGridPane(), pcaButtonBox()) padWith 25.0) private fun createUmapTab() = Tab(\u0026#34;UMAP\u0026#34;) .withContents(VBox(10.0, umapGridPane(), umapDistanceThresholdBox(), umapControlBox()).padWith(25.0)) private fun createDistancesTab() = Tab(\u0026#34;Distances\u0026#34;) withContents HBox(10.0, distanceLeft(), distanceRight()) private fun createHullTab() = Tab(\u0026#34;Hull Geometry\u0026#34;) withContents BorderPane() .withTop(hullTop()) .withLeft(manifoldPropertyBox()) .withCenter(manifoldBox()) private fun umapGridPane() = GridPane().apply { columnConstraints += ColumnConstraints(10.0, 200.0, 288.0).apply { hgrow = Priority.SOMETIMES } columnConstraints += ColumnConstraints(10.0, 200.0, 380.0).apply { hgrow = Priority.SOMETIMES } addRowConstraints(10, stdRowConst) umapSpinnerColumn() umapSliderColumn() } private fun GridPane.umapSliderColumn() { stackedSlider(\u0026#34;Repulsion Strength\u0026#34;, model.repulsionStr, 0.0, 2.0, 0.1, 1, 0) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Minimum Distance\u0026#34;, model.minimumDistance, 0.0, 0.6, 0.1, 1, 2) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Spread\u0026#34;, model.spread, 0.5, 1.5, 0.1, 1, 4) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Op Mix Ration\u0026#34;, model.opMixRatio, 0.0, 1.0, 0.1, 1, 6) ticksOn true addStyle \u0026#34;std-slider\u0026#34; stackedSlider(\u0026#34;Target Weight\u0026#34;, model.targetWeight, 0.0, 1.0, 0.1, 1, 8) ticksOn true addStyle \u0026#34;std-slider\u0026#34; } private fun GridPane.umapSpinnerColumn() { stackedIntSpinner(\u0026#34;Number of Components\u0026#34;, model.numberOfComponents, 2, 5, 1, 0, 0) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Number of Epochs\u0026#34;, model.numberOfEpochs, 25, 500, 25, 0, 2) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Nearest Neighbours\u0026#34;, model.nearestNeighbour, 5, 500, 5, 0, 4) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Negative Sample Rate\u0026#34;, model.negativeSampleRate, 1, 250, 1, 0, 6) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Local Connectivity\u0026#34;, model.localConnectivity, 1, 250, 1, 0, 8) addStyle \u0026#34;std-spinner\u0026#34; } private fun umapDistanceThresholdBox() = HBox(10.0).apply { alignment = Pos.TOP_CENTER children += VBox(10.0, promptOf(\u0026#34;Distance Metric\u0026#34;), ChoiceBox(generateDefaultMetrics()).firstSelected()) children += VBox( 10.0, promptOf(\u0026#34;Threshold (if applicable)\u0026#34;), doubleSpinnerOf(0.01, 1.0, \u0026#34;standard-spinner\u0026#34;, model.threshold.asObject()).setStep(0.01) ) } private fun umapControlBox() = HBox(15.0, umapConfigButtonBox(), umapHyperBox(), umapRunExportButtonBox()) private fun umapConfigButtonBox() = VBox(10.0).apply { children += buttonOf(\u0026#34;Load New Config\u0026#34;) { println(\u0026#34;Hello\u0026#34;) } addStyle \u0026#34;standard-button\u0026#34; children += buttonOf(\u0026#34;Save Current Config\u0026#34;) { fileOp.invoke(this.scene.window, FileOperation.UMAP_SAVE) } .addStyle(\u0026#34;standard-button\u0026#34;) } private fun umapHyperBox() = VBox(10.0).apply { with(ToggleGroup()) { addChild(RadioButton(\u0026#34;Use Hypersurface\u0026#34;) inToggleGroup this) addChild(RadioButton(\u0026#34;Use Hyperspace\u0026#34;) inToggleGroup this setSelected true) } addChild(CheckBox(\u0026#34;Progress Output\u0026#34;)) } private fun umapRunExportButtonBox() = VBox( 10.0, buttonOf(\u0026#34;Run UMAP\u0026#34;) { genOp.invoke(GeneralOperation.PROJECT) } addStyle \u0026#34;standard-button\u0026#34;, buttonOf(\u0026#34;Export TMatrix\u0026#34;) { genOp.invoke(GeneralOperation.EXPORT_MATRIX) } addStyle \u0026#34;standard-button\u0026#34;, buttonOf(\u0026#34;Export Projections\u0026#34;) { genOp.invoke(GeneralOperation.SAVE_PROJECTIONS) } addStyle \u0026#34;standard-button\u0026#34; ) private fun pcaButtonBox() = HBox( 15.0, buttonOf(\u0026#34;Project Data\u0026#34;) { genOp.invoke(GeneralOperation.RUN_PCA) } addStyle \u0026#34;standard-button\u0026#34;, buttonOf(\u0026#34;Export Projections\u0026#34;) { genOp.invoke(GeneralOperation.SAVE_PROJECTIONS) } addStyle \u0026#34;standard-button\u0026#34; ) alignTo Pos.CENTER private fun pcaGridPane() = GridPane().apply { columnConstraints += ColumnConstraints(10.0, 200.0, 288.0).apply { hgrow = Priority.SOMETIMES } columnConstraints += ColumnConstraints(10.0, 200.0, 380.0).apply { hgrow = Priority.SOMETIMES } addRowConstraints(10, stdRowConst) pcaLeftColumn() pcaRightColumn() } private fun GridPane.pcaRightColumn() { add(promptOf(\u0026#34;Component Analysis Type\u0026#34;), 1, 0) with(ToggleGroup()) { add(radioButtonOf(\u0026#34;PCA (EigenValue)\u0026#34;, model.analysisMethodPca, this, \u0026#34;std-radio\u0026#34;), 1, 1) add( radioButtonOf(\u0026#34;Singular Value Decomposition\u0026#34;, model.analysisMethodSvd, this, \u0026#34;std-radio\u0026#34;), 1, 2 ) } add(promptOf(\u0026#34;Input Data Source\u0026#34;), 1, 3) with(ToggleGroup()) { add(radioButtonOf(\u0026#34;Use Hypersurface\u0026#34;, model.dummyBoolean1, this, \u0026#34;std-radio\u0026#34;), 1, 4) add(radioButtonOf(\u0026#34;Use Hyperspace\u0026#34;, model.dummyBoolean2, this, \u0026#34;std-radio\u0026#34;), 1, 5) } } private fun GridPane.pcaLeftColumn() { stackedIntSpinner(\u0026#34;Number of Components\u0026#34;, model.numberOfPcaComponents, 2, 5, 1, 0, 0) addStyle \u0026#34;std-spinner\u0026#34; add(checkBoxOf(\u0026#34;Enabled Ranged Fitting (Experimental)\u0026#34;, model.rangeFitting, \u0026#34;std-checkbox\u0026#34;), 0, 2) stackedIntSpinner(\u0026#34;Fit Start Index\u0026#34;, model.fitStartIndex, 0, 500, 5, 0, 3) .bindDisable(model.rangeFitting.not()) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Fit End Index\u0026#34;, model.fitEndIndex, 5, 2000, 5, 0, 5) .bindDisable(model.rangeFitting.not()) addStyle \u0026#34;std-spinner\u0026#34; stackedIntSpinner(\u0026#34;Output Scaling Factor\u0026#34;, model.pcaScalingFactor, 1, 1000, 0, 10, 7) addStyle \u0026#34;std-spinner\u0026#34; } private fun distanceLeft() = VBox(10.0).apply { padding = Insets(25.0) children += h3Of(\u0026#34;Distance Metric\u0026#34;) children += dataOf(StringExpression.stringExpression(model.selectedDistance.flatMap { it.metric } .orElse(\u0026#34;Select Distance\u0026#34;))) with(ToggleGroup()) { children += radioButtonOf(\u0026#34;Point to Point\u0026#34;, model.pointToPoint, this, \u0026#34;std-radio\u0026#34;) children += radioButtonOf(\u0026#34;Point to Group\u0026#34;, model.pointToGroup, this, \u0026#34;std-radio\u0026#34;) } children += h3Of(\u0026#34;Connector Thickness\u0026#34;) children += intSpinnerOf(1, 50, \u0026#34;standard-spinner\u0026#34;, model.connectorThickness.asObject()) .setStep(1) .bindDisable(model.selectedDistance.isNull) .withInitialDelay(Duration.millis(500.0)) .withRepeatDelay(Duration.millis(500.0)) children += promptOf(\u0026#34;Connector Colour\u0026#34;) children += ColorPicker().apply { promptText = \u0026#34;Change the colour of the 3D connector\u0026#34; valueProperty().bindBidirectional(model.connectorColour) bindDisable(model.selectedDistance.isNull) } } private fun distanceRight() = VBox(10.0).apply { children += HBox( 10.0, h3Of(\u0026#34;Collected Distances\u0026#34;), buttonOf(\u0026#34;Clear All\u0026#34;) { genOp.invoke(GeneralOperation.CLEAR_DISTANCES) }) alignTo Pos.CENTER children += (ListView\u0026lt;Distance\u0026gt;() withItems model.distanceList withCellFactory Callback { DistanceListCell() } connectSelection model.selectedDistance).apply { model.externallySelectedDistance.subscribe { newValue -\u0026gt; this.selectionModel.select(newValue) } } } private fun manifoldPropertyBox(): Region = VBox(10.0).apply { children += promptOf(\u0026#34;Selected Manifold Properties\u0026#34;) children += titledPaneOf(\u0026#34;Material\u0026#34;) { TwoColumnGridPane().addColorPickerRow(\u0026#34;Diffuse Colour\u0026#34;, model.manifoldDiffuseColour, \u0026#34;std-color-picker\u0026#34;) .addColorPickerRow(\u0026#34;Wire Mesh Colour\u0026#34;, model.manifoldWireMeshColour, \u0026#34;std-color-picker\u0026#34;) .addColorPickerRow(\u0026#34;Specular Colour\u0026#34;, model.manifoldSpecularColour, \u0026#34;std-color-picker\u0026#34;) } withCollapsable false children += meshViewPane() } private fun meshViewPane() = titledPaneOf(\u0026#34;MeshView\u0026#34;) { VBox(5.0).apply { children += radioButtonHBox( \u0026#34;Cull Face\u0026#34;, listOf( Pair(\u0026#34;Front\u0026#34;, model.frontCullFace), Pair(\u0026#34;Back\u0026#34;, model.backCullFace), Pair(\u0026#34;None\u0026#34;, model.noneCullFace) ), 5.0, \u0026#34;std-radio-button\u0026#34; ) children += radioButtonHBox( \u0026#34;Draw Mode\u0026#34;, listOf(Pair(\u0026#34;Fill\u0026#34;, model.fillDrawMode), Pair(\u0026#34;Lines\u0026#34;, model.linesDrawMode)), 5.0, \u0026#34;std-radio-button\u0026#34; ) children += HBox( 10.0, checkBoxOf(\u0026#34;Show Wire Frame\u0026#34;, model.showWireFrame, \u0026#34;std-checkbox\u0026#34;), checkBoxOf(\u0026#34;Show Control Points\u0026#34;, model.showControlPoints, \u0026#34;std-checkbox\u0026#34;) ) } } withCollapsable false private fun hullTop(): Region = HBox(10.0).apply { padWith(10.0) children += hullTopGridPane() children += VBox( 20.0, buttonOf(\u0026#34;Generate\u0026#34;) { genOp.invoke(GeneralOperation.GENERATE) }, buttonOf(\u0026#34;Cluster Tools\u0026#34;) { genOp.invoke(GeneralOperation.BUILD_CLUSTER) } ) } private fun hullTopGridPane() = TwoColumnGridPane() .addRadioButtonHBoxRow( \u0026#34;Point Set\u0026#34;, listOf(Pair(\u0026#34;Visible\u0026#34;, model.useVisible), Pair(\u0026#34;All\u0026#34;, model.useAll)), 5.0, \u0026#34;std-radio-button\u0026#34; ) .addRow(\u0026#34;Distance Tolerance\u0026#34;) { HBox( 10.0, checkBoxOf(\u0026#34;Auto\u0026#34;, model.toleranceAuto, \u0026#34;std-checkbox\u0026#34;), doubleSpinnerOf(0.1, 1.0, \u0026#34;std-spinner\u0026#34;, model.toleranceManual.asObject()).setStep(0.1) .bindDisable(model.toleranceAuto) ) } .addChoiceBoxRow(\u0026#34;Find by Label\u0026#34;, model.factorLabelList, model.selectedFactorLabel, \u0026#34;std-choice-box\u0026#34;, true) private fun manifoldBox(): Region = VBox(5.0).apply { children += h3Of(\u0026#34;Generated Manifolds\u0026#34;) children += HBox(20.0).apply { children += buttonOf(\u0026#34;Clear All\u0026#34;) { genOp.invoke(GeneralOperation.CLEAR_ALL) } children += buttonOf(\u0026#34;Export All\u0026#34;) { genOp.invoke(GeneralOperation.EXPORT_ALL) } } children += ListView\u0026lt;Manifold\u0026gt;() withItems model.manifoldList withCellFactory Callback { ManifoldListCell() } connectSelection model.selectedManifold } } One of the first things you should notice is that except for the container classes, none of the Nodes are instantiated directly using their constructors. All of them are instantiated via builder methods of some sort, and those builders are generic enough that they are included in WidgetsFX.\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content = \u0026ldquo;This points out one of the biggest problems with the standard JavaFX library - a lack of constructors that allow a parameter for value binding.\u0026rdquo; %}\nFor those layout classes, I\u0026rsquo;ve used three standard techniques for populating them:\nProviding the children as constructor parameters. Using getChildren().add() via children += inside .apply{}. Using the extension function Pane.addChild() In practice, I found that Pane.addChild() outside of apply{} was no better than just including the children as constructor parameters. It\u0026rsquo;s also not clear if Pane.addChild() is any clearer than children += inside of an apply{} block.\nThere are a fair number of GridPanes in this layout. GridPane is fine when there is a strict need to keep columns and rows locked together in some fashion, but that is rarely the case in this layout. Particularly in the UMAP GridPane, where the Labels and Controls are stacked in successive rows, with the Spinner inputs in one column and the Slider inputs in another column. Is there really any need to keep the elements aligned by row?\nWhile I don\u0026rsquo;t think I would use a GridPane in this case (two VBoxes in an HBox would be simpler), I did create the extension functions GridPane.stackedSlider, and GridPane.stackedIntSpinner to get the repeated elements out of the GridPane configuration.\nFor the infix decorator functions, I\u0026rsquo;ve used them as infix when only one or two functions were called, and they would fit onto a single line. When more functions were called, it was more clear to use the regular notation and stack them one per line in the code.\nI am aware that the infix notation and the extension functions are difficult to get used to at first. A couple of years ago, I would have shied away from using them and simply put all of this functionality into apply{} blocks. Today, I find the apply{} approach to be overly verbose in many cases.\nThe net result of the extension functions and builder methods is to strip virtually all of the configuration details and boilerplate out of the layout code leaving something where you can understand the effect of that configuration without obscuring the layout itself.\nI do feel that this:\nprivate fun manifoldPropertyBox(): Region = VBox(10.0).apply { children += promptOf(\u0026#34;Selected Manifold Properties\u0026#34;) children += titledPaneOf(\u0026#34;Material\u0026#34;) { TwoColumnGridPane() .addColorPickerRow(\u0026#34;Diffuse Colour\u0026#34;, model.manifoldDiffuseColour, \u0026#34;std-color-picker\u0026#34;) .addColorPickerRow(\u0026#34;Wire Mesh Colour\u0026#34;, model.manifoldWireMeshColour, \u0026#34;std-color-picker\u0026#34;) .addColorPickerRow(\u0026#34;Specular Colour\u0026#34;, model.manifoldSpecularColour, \u0026#34;std-color-picker\u0026#34;) } withCollapsable false children += meshViewPane() } is far easier to understand than:\n\u0026lt;VBox spacing=\u0026#34;10.0\u0026#34; BorderPane.alignment=\u0026#34;CENTER\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label text=\u0026#34;Selected Manifold Properties\u0026#34;/\u0026gt; \u0026lt;TitledPane collapsible=\u0026#34;false\u0026#34; text=\u0026#34;Material\u0026#34; VBox.vgrow=\u0026#34;ALWAYS\u0026#34;\u0026gt; \u0026lt;content\u0026gt; \u0026lt;VBox spacing=\u0026#34;5.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Diffuse Color\u0026#34;/\u0026gt; \u0026lt;ColorPicker fx:id=\u0026#34;manifoldDiffuseColorPicker\u0026#34; editable=\u0026#34;true\u0026#34; prefHeight=\u0026#34;50.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Wire Mesh Color\u0026#34;/\u0026gt; \u0026lt;ColorPicker fx:id=\u0026#34;manifoldWireMeshColorPicker\u0026#34; editable=\u0026#34;true\u0026#34; prefHeight=\u0026#34;50.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;HBox alignment=\u0026#34;CENTER_LEFT\u0026#34; spacing=\u0026#34;10.0\u0026#34;\u0026gt; \u0026lt;children\u0026gt; \u0026lt;Label prefWidth=\u0026#34;125.0\u0026#34; text=\u0026#34;Specular Color\u0026#34;/\u0026gt; \u0026lt;ColorPicker fx:id=\u0026#34;manifoldSpecularColorPicker\u0026#34; editable=\u0026#34;true\u0026#34; prefHeight=\u0026#34;50.0\u0026#34; prefWidth=\u0026#34;150.0\u0026#34;/\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/HBox\u0026gt; \u0026lt;/children\u0026gt; \u0026lt;/VBox\u0026gt; \u0026lt;/content\u0026gt; \u0026lt;/TitledPane\u0026gt; . . . \u0026lt;/VBox\u0026gt; Especially when you take into consideration the ~40 lines of code that configure these ColorPickers in the FXML Controller. In the Kotlin code, these 3 lines completely configue the ColorPickers and they are never referenced again\u0026hellip;anywhere.\nListView Cells # The original design didn\u0026rsquo;t properly handle the two ListViews properly at all. These two classes provide Cell layouts that emulate what the original code did:\nclass DistanceListCell : ListCell\u0026lt;Distance\u0026gt;() { private val label = Label() private val distanceValueLabel = Label() private val visibleCB = CheckBox(\u0026#34;Visible\u0026#34;) private val layout = HBox(5.0, visibleCB, label, distanceValueLabel) override fun updateItem(newItem: Distance?, isEmpty: Boolean) { item?.let { label.textProperty().unbind() distanceValueLabel.textProperty().unbind() visibleCB.selectedProperty().unbindBidirectional(it.visible) } super.updateItem(newItem, isEmpty) graphic = null text = null if (!isEmpty) { newItem?.let { label.textProperty().bind(it.label) visibleCB.selectedProperty().bindBidirectional(it.visible) distanceValueLabel.textProperty().bind(Bindings.concat(it.metric, \u0026#34;: \u0026#34;, it.distance.asString())) graphic = layout } } } } class ManifoldListCell : ListCell\u0026lt;Manifold\u0026gt;() { private val label = TextField().apply { focusedProperty().subscribe { newVal -\u0026gt; if (newVal) listView.selectionModel.select(item) } } private val visibleCB = CheckBox(\u0026#34;Visible\u0026#34;).apply { focusedProperty().subscribe { newVal -\u0026gt; if (newVal) listView.selectionModel.select(item) } } private val layout = HBox(5.0, visibleCB, label) override fun updateItem(newItem: Manifold?, isEmpty: Boolean) { item?.let { label.textProperty().unbindBidirectional(it.label) visibleCB.selectedProperty().unbindBidirectional(it.visible) } super.updateItem(newItem, isEmpty) graphic = null text = null if (!isEmpty) { newItem?.let { label.textProperty().bindBidirectional(it.label) visibleCB.selectedProperty().bindBidirectional(it.visible) graphic = layout } } } } The Controller # Here\u0026rsquo;s the code for the Controller:\nclass FromFxmlController(sharedElements: SharedElements, externalFunctions: SharedFunctions) { private val model = FromFxmlModel(sharedElements) private val viewBuilder = FromFxmlViewBuilder(model, this::dataOperation, this::generalOperation) private val interactor = FromFxmlInteractor(model, externalFunctions) fun getView(): Region = viewBuilder.build() asFileDrop { runStandardVoidTask({ interactor.loadUmap(it[0]) }, { interactor.completeLoadUmap() }) } private fun dataOperation(window: Window, operation: FileOperation) { when (operation) { FileOperation.UMAP_SAVE -\u0026gt; saveUmap(window) FileOperation.UMAP_LOAD -\u0026gt; chooseAndloadUmap(window) } } private fun generalOperation(operation: GeneralOperation) { when (operation) { GeneralOperation.BUILD_CLUSTER -\u0026gt; interactor.buildCluster() GeneralOperation.GENERATE -\u0026gt; interactor.generate() GeneralOperation.CLEAR_ALL -\u0026gt; interactor.clearAll() GeneralOperation.EXPORT_ALL -\u0026gt; interactor.exportAll() GeneralOperation.CLEAR_DISTANCES -\u0026gt; interactor.clearDistances() GeneralOperation.PROJECT -\u0026gt; interactor.project() GeneralOperation.EXPORT_MATRIX -\u0026gt; interactor.exportMatrix() GeneralOperation.SAVE_PROJECTIONS -\u0026gt; interactor.saveProjections() GeneralOperation.RUN_PCA -\u0026gt; interactor.runPca() } } private fun saveUmap(window: Window) { FileChooser().apply { title = \u0026#34;Choose UMAP Config file output..\u0026#34; initialFileName = \u0026#34;UmapConfig.json\u0026#34; initialDirectory = model.latestDir.value ?: File(\u0026#34;.\u0026#34;) }.showSaveDialog(window)?.let { runStandardVoidTask({ interactor.saveUmap(it) }, { interactor.completeSaveUmap() }) } } private fun chooseAndloadUmap(window: Window) { FileChooser().apply { title = \u0026#34;Choose UMAP Config to load...\u0026#34; initialDirectory = model.latestDir.value ?: File(\u0026#34;.\u0026#34;) }.showOpenDialog(window)?.let { runStandardVoidTask({ interactor.loadUmap(it) }, { interactor.completeLoadUmap() }) } } } This is pretty simple. There\u0026rsquo;s the standard instantiation of the other elements, and then two dispacth methods to handle actions triggered by the View. Additionally, we have two methods to invoke FileChooser as part of a workflow to handle the file operations.\nThe Model # Here is the Model code:\nclass FromFxmlModel(sharedElements: SharedElements) { val externallySelectedDistance: ObjectProperty\u0026lt;Distance\u0026gt; = sharedElements.selectedDistance val externallySelectedManifold: ObjectProperty\u0026lt;Manifold\u0026gt; = sharedElements.selectedManifold val toleranceManual: DoubleProperty = sharedElements.toleranceManual val numberOfComponents: IntegerProperty = SimpleIntegerProperty(3) val numberOfEpochs: IntegerProperty = SimpleIntegerProperty(200) val nearestNeighbour: IntegerProperty = SimpleIntegerProperty(15) val negativeSampleRate: IntegerProperty = SimpleIntegerProperty(5) val localConnectivity: IntegerProperty = SimpleIntegerProperty(1) val repulsionStr: DoubleProperty = SimpleDoubleProperty(1.0) val spread: DoubleProperty = SimpleDoubleProperty(1.0) val minimumDistance: DoubleProperty = SimpleDoubleProperty(0.1) val opMixRatio: DoubleProperty = SimpleDoubleProperty(0.5) val targetWeight: DoubleProperty = SimpleDoubleProperty(0.5) val threshold: DoubleProperty = SimpleDoubleProperty(0.1) val numberOfPcaComponents: IntegerProperty = SimpleIntegerProperty() val dummyBoolean1: BooleanProperty = SimpleBooleanProperty(false) val dummyBoolean2: BooleanProperty = SimpleBooleanProperty(false) val pcaScalingFactor: IntegerProperty = SimpleIntegerProperty(100) val fitStartIndex: IntegerProperty = SimpleIntegerProperty(0) val fitEndIndex: IntegerProperty = SimpleIntegerProperty(50) val rangeFitting: BooleanProperty = SimpleBooleanProperty(false) val dummyBoolean4: BooleanProperty = SimpleBooleanProperty(false) val analysisMethodSvd: BooleanProperty = SimpleBooleanProperty(false) val analysisMethodPca: BooleanProperty = SimpleBooleanProperty(false) val connectorThickness: IntegerProperty = SimpleIntegerProperty(17) val connectorColour: ObjectProperty\u0026lt;Color\u0026gt; = SimpleObjectProperty() val pointToPoint: BooleanProperty = SimpleBooleanProperty(false) val pointToGroup: BooleanProperty = SimpleBooleanProperty(false) val latestDir: ObjectProperty\u0026lt;File?\u0026gt; = SimpleObjectProperty() val manifoldDiffuseColour: ObjectProperty\u0026lt;Color\u0026gt; = SimpleObjectProperty(Color.CYAN) val manifoldWireMeshColour: ObjectProperty\u0026lt;Color\u0026gt; = SimpleObjectProperty(Color.BLACK) val manifoldSpecularColour: ObjectProperty\u0026lt;Color\u0026gt; = SimpleObjectProperty(Color.BLACK) val frontCullFace: BooleanProperty = SimpleBooleanProperty(false) val backCullFace: BooleanProperty = SimpleBooleanProperty(false) val noneCullFace: BooleanProperty = SimpleBooleanProperty(false) val fillDrawMode: BooleanProperty = SimpleBooleanProperty(false) val linesDrawMode: BooleanProperty = SimpleBooleanProperty(false) val showWireFrame: BooleanProperty = sharedElements.showWireFrame val showControlPoints: BooleanProperty = sharedElements.showControlPoints val useAll: BooleanProperty = sharedElements.useAll val useVisible: BooleanProperty = sharedElements.useVisible val toleranceAuto: BooleanProperty = SimpleBooleanProperty(false) val distanceList: ObservableList\u0026lt;Distance\u0026gt; = sharedElements.distanceList val manifoldList: ObservableList\u0026lt;Manifold\u0026gt; = sharedElements.manifoldList val factorLabelList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList() val selectedFactorLabel = sharedElements.selectedFactorLabel val selectedDistance: ObjectProperty\u0026lt;Distance\u0026gt; = SimpleObjectProperty() val selectedManifold: ObjectProperty\u0026lt;Manifold\u0026gt; = SimpleObjectProperty() init { sharedElements.manifoldCullFace.bind(Bindings.createObjectBinding({ if (frontCullFace.value) return@createObjectBinding CullFace.FRONT if (backCullFace.value) return@createObjectBinding CullFace.BACK return@createObjectBinding CullFace.NONE }, frontCullFace, backCullFace, noneCullFace)) } } This is just a POJO of JavaFX Observable classes. The fields that are tied to the external application are instantiated as references to the corresponding field in SharedElements. SharedElements.manifoldCullFace corresponds to whichever of three BooleanProperties is true, and is bound that way.\nSharedElements is not exposed to any other component of the MVCI construct, and looks like this:\nclass SharedElements { val distanceList: ObservableList\u0026lt;Distance\u0026gt; = FXCollections.observableArrayList() val manifoldList: ObservableList\u0026lt;Manifold\u0026gt; = FXCollections.observableArrayList() val selectedDistance: ObjectProperty\u0026lt;Distance\u0026gt; = SimpleObjectProperty() val selectedManifold: ObjectProperty\u0026lt;Manifold\u0026gt; = SimpleObjectProperty() val toleranceManual: DoubleProperty = SimpleDoubleProperty() val useAll: BooleanProperty = SimpleBooleanProperty(false) val useVisible: BooleanProperty = SimpleBooleanProperty(false) val manifoldCullFace: ObjectProperty\u0026lt;CullFace\u0026gt; = SimpleObjectProperty() val showWireFrame: BooleanProperty = SimpleBooleanProperty(false) val showControlPoints: BooleanProperty = SimpleBooleanProperty(false) val selectedFactorLabel: StringProperty = SimpleStringProperty() } In truth, I got fed up searching through all of the ManifoldEvents to find out what data was being passed back and forth to other parts of the application. So I\u0026rsquo;m sure that this SharedElements object is missing quite a few elements. There\u0026rsquo;s enough here to make the point, though, and without the rest of the application it doesn\u0026rsquo;t make any difference for this demonstration.\nThe Interactor # The last MVCI component is the Interactor:\nclass FromFxmlInteractor(private val model: FromFxmlModel, private val externalFunctions: SharedFunctions) { private var umapDto: UmapDto? = null init { createDummyData() model.selectedDistance.subscribe { oldValue, newValue -\u0026gt; oldValue?.let { model.connectorThickness.unbindBidirectional(it.width) model.connectorColour.unbindBidirectional(it.colour) } newValue?.let { model.connectorThickness.bindBidirectional(it.width) model.connectorColour.bindBidirectional(it.colour) } model.externallySelectedDistance.value = newValue } model.selectedManifold.subscribe { oldValue, newValue -\u0026gt; oldValue?.let { model.manifoldDiffuseColour.unbindBidirectional(it.diffuseColour) model.manifoldSpecularColour.unbindBidirectional(it.specularColour) model.manifoldWireMeshColour.unbindBidirectional(it.wireframeColour) } newValue?.let { model.manifoldDiffuseColour.bindBidirectional(it.diffuseColour) model.manifoldSpecularColour.bindBidirectional(it.specularColour) model.manifoldWireMeshColour.bindBidirectional(it.wireframeColour) } model.externallySelectedManifold.value = newValue } } private fun createDummyData() { model.distanceList.add(Distance(\u0026#34;Label 1\u0026#34;, \u0026#34;Millimetres\u0026#34;, 17.0, 8, Color.GREEN)) model.distanceList.add(Distance(\u0026#34;Label 2\u0026#34;, \u0026#34;Nanometres\u0026#34;, 22.0, 1, Color.CYAN)) model.distanceList.add(Distance(\u0026#34;Label 3\u0026#34;, \u0026#34;Millimetres\u0026#34;, 8.0, 10, Color.AZURE)) model.distanceList.add(Distance(\u0026#34;Label 4\u0026#34;, \u0026#34;Millimetres\u0026#34;, 45.0, 3, Color.RED)) model.manifoldList.add(Manifold(\u0026#34;Label 1\u0026#34;)) model.manifoldList.add(Manifold(\u0026#34;Label 2\u0026#34;)) model.manifoldList.add(Manifold(\u0026#34;Label 3\u0026#34;)) model.manifoldList.add(Manifold(\u0026#34;Label 4\u0026#34;)) } fun buildCluster() { externalFunctions.buildCluster.invoke() } fun generate() { externalFunctions.generate.invoke() } fun clearAll() { externalFunctions.clearAll.invoke() } fun exportAll() { externalFunctions.exportAll.invoke() } fun clearDistances() { externalFunctions.clearDistances.invoke() } fun project() { externalFunctions.project.invoke() } fun exportMatrix() { externalFunctions.exportMatrix.invoke() } fun saveProjections() { externalFunctions.saveProjections.invoke() } fun runPca() { externalFunctions.runPCA.invoke() } fun saveUmap(file: File) { model.latestDir.value = file } fun completeSaveUmap() {} fun loadUmap(file: File) { println(\u0026#34;Hey! Loading a file\u0026#34;) } fun completeLoadUmap() {} } The init{} section is much like a constructor, and here it creates all of the relationships between elements of the Model that would be considered \u0026ldquo;business/application logic\u0026rdquo;. In this case it is mostly dealing with the relationships between the actively selected Manifold or Distance and some of the other properties.\nThe rest of this feels much more like a skeleton than it really is. The file handling methods are just placeholders, as they would need to connect to a service of some sort which would do the heavy lifting.\nAll of the other methods, however, are pretty much in their final form. They just need to invoke the functional elements provide by ExternalFunctions, which looks like this:\nclass SharedFunctions { val buildCluster: () -\u0026gt; Unit = {} val generate: () -\u0026gt; Unit = {} val clearAll: () -\u0026gt; Unit = {} val exportAll: () -\u0026gt; Unit = {} val clearDistances: () -\u0026gt; Unit = {} val project: () -\u0026gt; Unit = {} val exportMatrix: () -\u0026gt; Unit = {} val saveProjections: () -\u0026gt; Unit = {} val runPCA: () -\u0026gt; Unit = {} } These would ordinarily be defined in some other part of the application which is actually going to do the work. These functions completely replace all of the ManifoldEvent firings in the original FXML Controller.\nConclusion # I\u0026rsquo;m not going to pretend that I understand what this project does, but my impression is that it involves really complicated and sophisticated analysis of some kind of AI processing. But when you look at this screen it\u0026rsquo;s really just a bunch of Controls and Buttons and Lists that manipulate some data and trigger some actions.\nThe original design leaks the complexity of the entire application into what should be a simple screen. You cannot change a data value without knowing how that will impact the rest of the application.\nI need to stress that the code that I\u0026rsquo;ve published here runs as a stand-alone application. It doesn\u0026rsquo;t connect to anything, but it works and can be integrated into the rest of the application simply by providing the shared data and functions in the Controller constructor.\nClearly, a lot more was done here than just replace the FXML with code, although it\u0026rsquo;s fairly clear that the layout code is much simpler than the FXML plus FXML Controller from the original.\nThe Layout # I deliberately put this project aside for a while so that I could come back to get a more objective sense of how easy it is to read and understand the 230 lines of layout code.\nOne thing that was immediately clear to me when I came back to it was that none of these Tabs have anything to do with each other except that they cohabit in the same TabPane. As such, they could all be defined in their own builders, and each one would, therefore, be a little bit easier to understand since they wouldn\u0026rsquo;t be encumbered with the code from the other Tabs.\nFurthermore, each of these Tabs could have their own, independent, MVCI structure associated with them. There could be a \u0026ldquo;master\u0026rdquo; MVCI structure associated with the TabPane itself, and its Controller could handle instantiation of all of the other MVCI structures. This would make each of the 4 separate MVCI constructs extremely simple and easy to understand.\nNote Even if a Button on one Tab required the external function it invoked to use data from another Tab this wouldn\u0026rsquo;t matter because the Button actions aren\u0026rsquo;t transferring any data - that\u0026rsquo;s already handled by the shared data elements.\nUsing a Framework # I simply cannot imagine building anything like this without implementing a framework of some kind.\nOne of the things that became glaringly apparent after the conversion was that this screen, aside from some file handling, doesn\u0026rsquo;t actually do anything itself. You can see this just from looking at the Interactor. It doesn\u0026rsquo;t have much code that actually does anything. It just dispatches actions off to some other part of the application.\nCertainly, if you had written the application, or if you were very familiar with the entire application, you\u0026rsquo;d know that this screen didn\u0026rsquo;t actually do anything. But this is absolutely not clear from a casual glance at the original code.\nReactive vs Imperative Design # A much as I think the coded layout is a win compared to FXML, I think that this exercise really illustrates the wonderful simplicity that comes from implementing a Reactive design. There are literally hundreds of lines of code in the FXML Controller that just vanish away when a Reactive design is implemented.\nUsing a Reactive design also makes it trivial to connect to external elements of the application through a shared data model. This approach also greatly simplifies the understanding of the coupling between this screen and those external elements as it is all defined inside that single object.\nCoupling # Coupling in this new design is extremely controlled, and easy to understand.\nThe Model is the main source of coupling, but it also isolates as well. There\u0026rsquo;s no way for anything outside of the View to know if a Boolean value in the Model is presented to the user via a RadioButton, a ToggleButton, a CheckBox or some custom Control. But, no matter how it\u0026rsquo;s handled in the View, the Interactor can always simply deal with the Boolean value that it is bound to.\nIn a similar manner, the SharedElements is the main source of both coupling and isolation between this screen and the rest of the application.\nImportant Coupling is usually the single biggest source of unecessary complexity in any application. Controlling coupling is the best way to improve code quality.\nI simply cannot stress this too much. Virtually every good (or \u0026ldquo;clean\u0026rdquo;) coding technique, is designed to control and eliminate coupling as much possible. Looking at this \u0026ldquo;Trinity\u0026rdquo; project, excessive coupling is everywhere and it makes everything much more complicated than it needs to be. I\u0026rsquo;ve tried to limit coupling as much as possible in my version, and I think it is reflected in the lack of complexity.\nThe Kotlin # It\u0026rsquo;s really clear from this example just how much Kotlin lets you extract the boilerplate and verbosity out of the layout code. The infix extension decorator functions mean that you never have to instantiate any element of the layout as a variable. In most cases the instantiation, configuration, binding and addition to the layout of the Nodes is done in a single line.\nMaybe (probably?) you don\u0026rsquo;t want to learn Kotlin to do this. In Java, I think you\u0026rsquo;d have to create classic builders with the configuration elements included as decorators. But you could do a lot of this stuff that way.\nWhat I will say is that if you are looking for a tool to make layout creation and maintenance easier, you\u0026rsquo;ll get a lot more mileage out of learning Kotlin than you will by mastering SceneBuilder and FXML.\n","date":"22 July 2025","externalUrl":null,"permalink":"/javafx/techniques/nofxml/fxml-to-code/","section":"JavaFX","summary":"I’ve talked a lot about how coded layouts are better than FXML.  In this article we take a fairly large FXML based layout which is part of a larger project, convert it to code, and implement a framework using a Reactive design.  Is the final result better than FXML?  See for yourself.","title":"Converting FXML to Code","type":"javafx"},{"content":" Introduction # If you rigorously follow DRY (Don\u0026rsquo;t Repeat Yourself), and you should, then you\u0026rsquo;ll eventually have to think about \u0026ldquo;Big Picture DRY\u0026rdquo;. This all about avoiding repeating yourself in different projects, and different parts of projects. Not just within a single class or module of an application.\nIf you are one of those programmers that likes to use TableView - and there are lots of you out there - then one of the ways you might be repeating yourself without thinking about it is your TableColumns. Or, and this is even more likely, you\u0026rsquo;re avoiding properly configuring some kinds of TableColumns because it\u0026rsquo;s just too much tedious work to do every time.\nIn this article, we are going to look at how to create custom TableColumns to handle and display various kinds of data the way that you want to deal with them and that you can re-use over and over. We are also going to look at how custom TableViewCells work hand-in-glove with custom TableColumns and how you can create them to work together seemlessly.\nKotlin The examples in this article are written in Kotlin. If you need some help understanding Kotlin, then refer to this article. Different Data Types Should be Presented Differently # TableColumns are easy to set up when the data in the column is just a simple String. But what about numbers? What about dates and money?\nOnce you get into these questions, then you\u0026rsquo;re suddenly looking at custom TableCells to handle the presentation. The difference between adding a TableColumn with nothing more than a CellValueFactory and doing it right can seem huge. But it doesn\u0026rsquo;t have to be, and if you approach it properly then you can put your work into a personal library that you can use over and over.\nLet\u0026rsquo;s start by looking at a TableView configured with the bare minimum:\nclass Example1 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 350.0).apply { Example1::class.java.getResource(\u0026#34;example.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = TableView\u0026lt;TableData1\u0026gt;().apply { columns += TableColumn\u0026lt;TableData1, String\u0026gt;(\u0026#34;Column A\u0026#34;).apply { setCellValueFactory { it.value.value1 } styleClass += \u0026#34;a-column\u0026#34; } columns += TableColumn\u0026lt;TableData1, Double\u0026gt;(\u0026#34;Temperature\u0026#34;).apply { setCellValueFactory { it.value.value4.asObject() } } columns += TableColumn\u0026lt;TableData1, String\u0026gt;(\u0026#34;Column C\u0026#34;).apply { setCellValueFactory { it.value.value2 } styleClass += \u0026#34;c-column\u0026#34; } for (x in 1..30) items += TableData1( \u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, Random.nextDouble(45.0) ) } padding = Insets(10.0) } } Which looks like this:\nI think we can all agree that this is very, very ugly. We can clean it up a bit by rounding it off to one decimal place by using ObservableValue.map{}:\ncolumns += TableColumn\u0026lt;TableData1, Double\u0026gt;(\u0026#34;Temperature\u0026#34;).apply { setCellValueFactory { it.value.value4.asObject().map { number -\u0026gt; Math.round(number * 10.0) / 10.0 } } } Which is a little bit less ugly, but still not very good. The worst thing is that the numbers are all left justified, so let\u0026rsquo;s fix that by adding a style class selector temperature-column to the TableColumn and then fixing the alignment in the style sheet:\n.table-row-cell .temperature-column { -fx-alignment: center-right; } One last problem is that the column is way too wide for the data because the heading, \u0026ldquo;Temperature\u0026rdquo; is a long word. \u0026ldquo;Temp\u0026rdquo; would work but it\u0026rsquo;s also commonly used for \u0026ldquo;temporary\u0026rdquo; (at least to programmers) and doesn\u0026rsquo;t seem right. Let\u0026rsquo;s just use a graphic instead:\ncolumns += TableColumn\u0026lt;TableData1, String\u0026gt;(\u0026#34;\u0026#34;).apply { styleClass += \u0026#34;temperature-column\u0026#34; setCellValueFactory { it.value.value4.asObject().map { number -\u0026gt; Math.round(number * 10.0) / 10.0 } .map { number -\u0026gt; \u0026#34;%6.1f\u0026#34;.format(number) } } Example1::class.java.getResource(\u0026#34;thermometer24.png\u0026#34;)?.toString()?.let { graphic = ImageView(it) } } Which looks like this:\nAt this point, I\u0026rsquo;m not pretending that this is a great UI design. This article is about programming techniques, so we\u0026rsquo;re looking for interesting things to do here.\nCreating a Custom Column # For me, this small amount of code would be enough to trigger DRY the next time I created a \u0026ldquo;Temperature\u0026rdquo; column. We can do it by creating a builder function or by creating a custom class extending TableColumn. If we were going to stop here, then a builder would be the best option. But we\u0026rsquo;re not going to stop here, so we\u0026rsquo;ll create a custom class, which will look like this to start:\nclass TemperatureColumn\u0026lt;T\u0026gt;(extractor: (data: T) -\u0026gt; DoubleProperty) : TableColumn\u0026lt;T, Double\u0026gt;() { init { styleClass += \u0026#34;temperature-column\u0026#34; setCellValueFactory { extractor.invoke(it.value).asObject().map { number -\u0026gt; Math.round(number * 10.0) / 10.0 } } TemperatureColumn::class.java.getResource(\u0026#34;thermometer24.png\u0026#34;)?.toString()?.let { graphic = ImageView(it) } } } All of the apply{} code from before has been moved into init{} in our custom class. We don\u0026rsquo;t need a column heading parameter in the constructor, because we are just going to use our graphic. But we are now taking a constructor parameter that specifies how to extract our column data from the table data.\nIn the TableView setup code we invoke our TemperatureColumn like this:\ncolumns += TemperatureColumn\u0026lt;TableData1\u0026gt; { it.value4 } Once we\u0026rsquo;ve done this, our attitude to how we approach this new TableColumn changes in two ways:\nIt\u0026rsquo;s now worth doing things that we wouldn\u0026rsquo;t have put in the effort for with a \u0026ldquo;one-off\u0026rdquo; implementation. It\u0026rsquo;s now worth doing things really, really technically correct and configurable. We\u0026rsquo;ll see this in the first enhancement that we make\u0026hellip;\nStyling a New Colour for Temperatures Below Freezing # Let\u0026rsquo;s look at how to change the colour of the text to indicate temperatures at or below freezing. This is best done through a Pseudo-class so that we can set the colour via the style sheet.\nLet\u0026rsquo;s start with the style sheet:\n.table-row-cell .temperature-column:freezing { -fx-text-fill: cornflowerblue; -fx-font-weight: bold; } To do this, we\u0026rsquo;ll need to create the PseudoClass which will mean creating a custom TableCell:\nclass TemperatureTableCell\u0026lt;T\u0026gt; : TableCell\u0026lt;T, Double\u0026gt;() { companion object PseudoClasses { val freezingPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;freezing\u0026#34;) } init { itemProperty().subscribe { newValue -\u0026gt; pseudoClassStateChanged(freezingPseudoClass, (newValue?.let { (it \u0026lt;= 0.0) } ?: false)) } textProperty().bind(itemProperty().asString()) } } Which looks like this:\nClearly, it is possible to just code in the colour change for freezing temperatures as part of the TemperatureTableCell, and that approach might be more expeditious. But when you are building a custom TableColumn for re-use, you want to get it as close to \u0026ldquo;right\u0026rdquo; as you can. And that means that you treat the colour as styling and you implement it so that it can be styled through the style sheets.\nYou may have guessed, given the formula, that this column is intended to display °C. What if you need °F?\nStyling the Units # The first thing we need to do is to move the rounding feature out of the TemperatureColumn itself, and into the TemperatureTableCell because we don\u0026rsquo;t want to be doing any conversions on previously rounded numbers. Then we can introduce an Enum called TemperatureUnit and an ObjectBinding\u0026lt;Double\u0026gt; to convert between Celsius input and Fahrenheit, Celsius and Kelvin output.\nclass TemperatureColumn\u0026lt;T\u0026gt;(extractor: (data: T) -\u0026gt; DoubleProperty) : TableColumn\u0026lt;T, Double\u0026gt;() { val outputUnits: ObjectProperty\u0026lt;TemperatureUnit\u0026gt; = SimpleObjectProperty(TemperatureUnit.C) infix fun outputAs(newOutput: TemperatureUnit) = this.apply { outputUnits.value = newOutput } companion object PseudoClasses { val freezingPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;freezing\u0026#34;) } init { styleClass += \u0026#34;temperature-column\u0026#34; setCellValueFactory { extractor.invoke(it.value).asObject() } TemperatureColumn::class.java.getResource(\u0026#34;thermometer24.png\u0026#34;)?.toString()?.let { graphic = ImageView(it) } setCellFactory { TemperatureTableCell() } } enum class TemperatureUnit { C, F, K } inner class TemperatureTableCell\u0026lt;T\u0026gt; : TableCell\u0026lt;T, Double\u0026gt;() { init { itemProperty().subscribe { newValue -\u0026gt; pseudoClassStateChanged(freezingPseudoClass, (newValue?.let { (it \u0026lt;= 0.0) } ?: false)) } textProperty().bind( ConversionBinding(itemProperty(), outputUnits) .map { rawValue -\u0026gt; rawValue?.let { Math.round(rawValue * 10.0) / 10.0 }?.toString() }) } } class ConversionBinding( val celciusValue: ObjectProperty\u0026lt;Double\u0026gt;, val outputUnits: ObjectProperty\u0026lt;TemperatureUnit\u0026gt; ) : ObjectBinding\u0026lt;Double?\u0026gt;() { init { super.bind(celciusValue, outputUnits) } override fun computeValue(): Double? = when (outputUnits.value) { TemperatureUnit.K -\u0026gt; celciusValue.value?.let { it + 270.0 } TemperatureUnit.F -\u0026gt; celciusValue.value?.let { (it * 9 / 5) + 32 } else -\u0026gt; celciusValue.value } } } First off, everything has been moved inside the TemperatureColumn class, and TemperatureTableCell has been labled an \u0026ldquo;inner\u0026rdquo; class so that it can access fields from the outer TemperatureColumn class. There is a ConversionBinding that is dependent on Cell's item and TableColumn's outputUnits Properties and which handles the conversion from Celsius to all three output formats. The text Property of TemperatureTableCell is now bound to the item Property through this custom Binding and then further processed to limit it to one decimal place.\nThe outputAs() method of TemperatureColumn has been written as an infix decorator to make it easier to configure from the layout code.\nIn the layout, I\u0026rsquo;ve added a second TemperatureColumn so that we can have one in Celcius and one in Farenheit:\ncolumns += TemperatureColumn\u0026lt;TableData1\u0026gt; { it.value4 } outputAs TemperatureColumn.TemperatureUnit.F columns += TemperatureColumn\u0026lt;TableData1\u0026gt; { it.value4 } And it looks like this:\nYou can see here that the freezing Pseudo-class is still calculated properly as it is done on the Celsius value. The conversions happens as textProperty() is updated, so the internal value of item always remains in Celsius.\nStyling the Units Via CSS # Ideally, all the aspects of how the output is displayed should be configurable via Style Sheets. Let\u0026rsquo;s look at how to set up the StyleableProperties in our TableColumn so that we can configure the output units via CSS:\nclass TemperatureColumn\u0026lt;T\u0026gt;(extractor: (data: T) -\u0026gt; DoubleProperty) : TableColumn\u0026lt;T, Double\u0026gt;() { private val outputUnits: StyleableObjectProperty\u0026lt;TemperatureUnit\u0026gt; = SimpleStyleableObjectProperty(OUTPUT_UNITS_META_DATA, TemperatureUnit.C) infix fun outputAs(newOutput: TemperatureUnit) = this.apply { outputUnits.value = newOutput } companion object StylingStuff { val freezingPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;freezing\u0026#34;) val OUTPUT_UNITS_META_DATA: CssMetaData\u0026lt;TemperatureTableCell\u0026lt;*\u0026gt;, TemperatureUnit\u0026gt; = object : CssMetaData\u0026lt;TemperatureTableCell\u0026lt;*\u0026gt;, TemperatureUnit\u0026gt;( \u0026#34;-wfx-temperature-unit\u0026#34;, StyleConverter.getEnumConverter(TemperatureUnit::class.java) ) { override fun isSettable(styleable: TemperatureTableCell\u0026lt;*\u0026gt;) = !(styleable.outputUnits.isBound) override fun getStyleableProperty(styleable: TemperatureTableCell\u0026lt;*\u0026gt;) = styleable.outputUnits } private val cssMetaDataList = (TableColumn.getClassCssMetaData() + OUTPUT_UNITS_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } init { styleClass += \u0026#34;temperature-column\u0026#34; setCellValueFactory { extractor.invoke(it.value).asObject() } TemperatureColumn::class.java.getResource(\u0026#34;thermometer24.png\u0026#34;)?.toString()?.let { graphic = ImageView(it) } setCellFactory { TemperatureTableCell(outputUnits) } } } class TemperatureTableCell\u0026lt;T\u0026gt;(val outputUnits: StyleableObjectProperty\u0026lt;TemperatureUnit\u0026gt;) : TableCell\u0026lt;T, Double\u0026gt;() { override fun getControlCssMetaData() = (super.getControlCssMetaData() + TemperatureColumn.getClassCssMetaData()) as MutableList init { itemProperty().subscribe { newValue -\u0026gt; pseudoClassStateChanged(freezingPseudoClass, (newValue?.let { (it \u0026lt;= 0.0) } ?: false)) } textProperty().bind( ConversionBinding(itemProperty(), outputUnits) .map { rawValue -\u0026gt; rawValue?.let { Math.round(rawValue * 10.0) / 10.0 }?.toString() }) } } Now, this is a little bit complicated if you are new to creating custom StyleableProperties. If you want to learn all about this you can read my article about it here .\nWe\u0026rsquo;ve turned outputUnits into a StyleableObjectProperty which will enable us to connect it to a style sheet. This is done via something called CssMetaData, which really should be defined statically. The CssMetaData is in the companion object - which is pretty much the Kotlin version of static - and is called OUTPUT_UNITS_META_DATA. This is how you create an instance of a static anonymous inner class in Kotlin, and the constructor defines the style class selector and the data converter that will be used. Then we override a couple of methods to tell it how to find the data in the TableCell and how to determine if it is settable by the style sheet.\nSince it\u0026rsquo;s static, the CssMetaData doesn\u0026rsquo;t need to be defined in the class that uses it. In this case, we are going to be using the same Property for each TableCell in the TableColumn, so we can define it in the custom TableColumn and then include it in the CssMetaDataList for the TemperatureTableCell.\nOne more twist on this is that getCssMetaData() is final in TableCell, but we have getControlCssMetaData() that we can use instead.\nAnd now we can do this in the style sheet:\n.temperature-column { -wfx-temperature-unit: \u0026#34;k\u0026#34;; } Adding More Styleable Properties # What if, instead of \u0026ldquo;freezing\u0026rdquo;, you wanted to define \u0026ldquo;cold\u0026rdquo; as a styleable element? Even freezing is contextual, because the use case assumes the freezing point of water, but \u0026ldquo;cold\u0026rdquo; isn\u0026rsquo;t a generally understood value. This means we\u0026rsquo;ll have to have some way to define \u0026ldquo;cold\u0026rdquo; in the style sheet, as well as provide all of the same elements that we did for freezing.\nWe\u0026rsquo;ve already performed most of the types of setup that we will need for the previous styling, so adding \u0026ldquo;cold\u0026rdquo; is just applying what we\u0026rsquo;ve already seen:\nclass Example2 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 350.0).apply { Example2::class.java.getResource(\u0026#34;Example0.css\u0026#34;)?.toString()?.let { stylesheets += it } } Menu() stage.show() } private fun createContent(): Region = BorderPane().apply { center = TableView\u0026lt;TableData1\u0026gt;().apply { columns += TableColumn\u0026lt;TableData1, String\u0026gt;(\u0026#34;Column A\u0026#34;).apply { setCellValueFactory { it.value.value1 } styleClass += \u0026#34;a-column\u0026#34; } columns += TemperatureColumn2\u0026lt;TableData1\u0026gt; { it.value4 } outputAs TemperatureUnit.F columns += TemperatureColumn2\u0026lt;TableData1\u0026gt; { it.value4 } columns += TableColumn\u0026lt;TableData1, String\u0026gt;(\u0026#34;Column C\u0026#34;).apply { setCellValueFactory { it.value.value3 } styleClass += \u0026#34;c-column\u0026#34; } repeat(30) { items += TableData1( \u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, Random.nextDouble(-30.0, 55.0) ) } } padding = Insets(10.0) } } class TemperatureColumn2\u0026lt;T\u0026gt;(extractor: (data: T) -\u0026gt; DoubleProperty) : TableColumn\u0026lt;T, Double\u0026gt;() { private val outputUnits: StyleableObjectProperty\u0026lt;TemperatureUnit\u0026gt; = SimpleStyleableObjectProperty(OUTPUT_UNITS_META_DATA, TemperatureUnit.C) private val numDecimals: StyleableObjectProperty\u0026lt;Number\u0026gt; = SimpleStyleableObjectProperty\u0026lt;Number\u0026gt;(NUM_DECIMALS_META_DATA, 1) private val coldPoint: StyleableObjectProperty\u0026lt;Number\u0026gt; = SimpleStyleableObjectProperty\u0026lt;Number\u0026gt;(COLD_POINT_META_DATA, 1000) infix fun outputAs(newOutput: TemperatureUnit) = this.apply { outputUnits.value = newOutput } companion object StylingStuff { val freezingPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;freezing\u0026#34;) val coldPseudoClass: PseudoClass = PseudoClass.getPseudoClass(\u0026#34;cold\u0026#34;) val OUTPUT_UNITS_META_DATA: CssMetaData\u0026lt;TemperatureTableCell2\u0026lt;*\u0026gt;, TemperatureUnit\u0026gt; = object : CssMetaData\u0026lt;TemperatureTableCell2\u0026lt;*\u0026gt;, TemperatureUnit\u0026gt;( \u0026#34;-wfx-temperature-unit\u0026#34;, StyleConverter.getEnumConverter(TemperatureUnit::class.java) ) { override fun isSettable(styleable: TemperatureTableCell2\u0026lt;*\u0026gt;) = !(styleable.outputUnits.isBound) override fun getStyleableProperty(styleable: TemperatureTableCell2\u0026lt;*\u0026gt;) = styleable.outputUnits } val NUM_DECIMALS_META_DATA: CssMetaData\u0026lt;TemperatureTableCell2\u0026lt;*\u0026gt;, Number\u0026gt; = object : CssMetaData\u0026lt;TemperatureTableCell2\u0026lt;*\u0026gt;, Number\u0026gt;( \u0026#34;-wfx-num-decimal\u0026#34;, StyleConverter.getSizeConverter() ) { override fun isSettable(styleable: TemperatureTableCell2\u0026lt;*\u0026gt;) = !(styleable.numDecimals.isBound) override fun getStyleableProperty(styleable: TemperatureTableCell2\u0026lt;*\u0026gt;) = styleable.numDecimals } val COLD_POINT_META_DATA: CssMetaData\u0026lt;TemperatureTableCell2\u0026lt;*\u0026gt;, Number\u0026gt; = object : CssMetaData\u0026lt;TemperatureTableCell2\u0026lt;*\u0026gt;, Number\u0026gt;( \u0026#34;-wfx-cold-point\u0026#34;, StyleConverter.getSizeConverter() ) { override fun isSettable(styleable: TemperatureTableCell2\u0026lt;*\u0026gt;) = !(styleable.coldPoint.isBound) override fun getStyleableProperty(styleable: TemperatureTableCell2\u0026lt;*\u0026gt;) = styleable.coldPoint } private val cssMetaDataList = (TableColumn.getClassCssMetaData() + OUTPUT_UNITS_META_DATA + NUM_DECIMALS_META_DATA + COLD_POINT_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } init { styleClass += \u0026#34;temperature-column\u0026#34; setCellValueFactory { extractor.invoke(it.value).asObject() } TemperatureColumn2::class.java.getResource(\u0026#34;thermometer24.png\u0026#34;)?.toString()?.let { graphic = ImageView(it) } setCellFactory { TemperatureTableCell2(outputUnits, numDecimals, coldPoint) } } } class TemperatureTableCell2\u0026lt;T\u0026gt;( val outputUnits: StyleableObjectProperty\u0026lt;TemperatureUnit\u0026gt;, val numDecimals: StyleableObjectProperty\u0026lt;Number\u0026gt;, val coldPoint: StyleableObjectProperty\u0026lt;Number\u0026gt;, ) : TableCell\u0026lt;T, Double\u0026gt;() { override fun getControlCssMetaData() = (super.getControlCssMetaData() + TemperatureColumn2.getClassCssMetaData()) as MutableList init { itemProperty().subscribe { newValue -\u0026gt; pseudoClassStateChanged(TemperatureColumn2.freezingPseudoClass, (newValue?.let { (it \u0026lt;= 0.0) } ?: false)) pseudoClassStateChanged( TemperatureColumn2.coldPseudoClass, (newValue?.let { (it \u0026lt;= coldPoint.value.toDouble()) } ?: false)) } textProperty().bind( ConversionBinding(itemProperty(), outputUnits) .map { rawValue -\u0026gt; rawValue?.let { (rawValue * 10.0).roundToInt() / 10.0 }?.toString() }) } } fun main() = Application.launch(Example2::class.java) With the following added to the CSS file:\n.table-row-cell .temperature-column:cold { -fx-text-fill: hotpink; -fx-font-weight: bold; } .table-row-cell .temperature-column:freezing { -fx-text-fill: cornflowerblue; -fx-font-weight: bold; } Note that the freezing element goes after the cold element, because the freezing threshold (in this case) is below cold and will need to override cold.\nThere is more that we could do, but I\u0026rsquo;m sure that you get the idea at this point.\nMore Utilitarian TableColumn Types # In my own projects I rarely use TableColumn directly. I have custom columns for Double, Integer and even String. One of the primitive types that really doesn\u0026rsquo;t work for me out-of-the-box is Boolean. The default is a CheckBox, and it just doesn\u0026rsquo;t look right. I much prefer to use an image of some sort.\nSomething like this:\nThis can look a little busy, so it can be useful to only show the true images:\nIt can be cleaner to just use red and green dots:\nThis can be especially effective if you only show the dots for true values:\nLet\u0026rsquo;s look at the code for this:\nclass Example3 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 350.0).apply { Example3::class.java.getResource(\u0026#34;Example0.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = TableView\u0026lt;TableData3\u0026gt;().apply { columns += TableColumn\u0026lt;TableData3, String\u0026gt;(\u0026#34;Column A\u0026#34;).apply { setCellValueFactory { it.value.value1 } styleClass += \u0026#34;a-column\u0026#34; } columns += BooleanColumn\u0026lt;TableData3\u0026gt;(\u0026#34;Column C\u0026#34;) { it.value4 }.apply { styleClass += \u0026#34;c-column\u0026#34; } withFalseValues false withCircles true withCircleSize 18.0 repeat(30) { items += TableData3( \u0026#34;A\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;C\u0026#34;, Random.nextBoolean() ) } } padding = Insets(10.0) } } class BooleanColumn\u0026lt;T\u0026gt;(title: String, extractor: (data: T) -\u0026gt; BooleanProperty) : TableColumn\u0026lt;T, Boolean\u0026gt;(title) { private val showFalse: StyleableBooleanProperty = SimpleStyleableBooleanProperty(SHOW_FALSE_META_DATA, true) private val useCircles: StyleableBooleanProperty = SimpleStyleableBooleanProperty(USE_CIRCLES_META_DATA, false) private val circleSize: StyleableDoubleProperty = SimpleStyleableDoubleProperty(CIRCLE_SIZE_META_DATA, 7.0) infix fun withCircles(withCircles: Boolean) = apply { useCircles.value = withCircles } infix fun withCircleSize(circleRadius: Double) = apply { circleSize.value = circleRadius } infix fun withFalseValues(falseValues: Boolean) = apply { showFalse.value = falseValues } companion object StylingStuff { val SHOW_FALSE_META_DATA: CssMetaData\u0026lt;BooleanTableCell\u0026lt;*\u0026gt;, Boolean\u0026gt; = object : CssMetaData\u0026lt;BooleanTableCell\u0026lt;*\u0026gt;, Boolean\u0026gt;( \u0026#34;-wfx-show-false\u0026#34;, StyleConverter.getBooleanConverter() ) { override fun isSettable(styleable: BooleanTableCell\u0026lt;*\u0026gt;) = !(styleable.showFalse.isBound) override fun getStyleableProperty(styleable: BooleanTableCell\u0026lt;*\u0026gt;) = styleable.showFalse } val USE_CIRCLES_META_DATA: CssMetaData\u0026lt;BooleanTableCell\u0026lt;*\u0026gt;, Boolean\u0026gt; = object : CssMetaData\u0026lt;BooleanTableCell\u0026lt;*\u0026gt;, Boolean\u0026gt;( \u0026#34;-wfx-use-circles\u0026#34;, StyleConverter.getBooleanConverter() ) { override fun isSettable(styleable: BooleanTableCell\u0026lt;*\u0026gt;) = !(styleable.useCircles.isBound) override fun getStyleableProperty(styleable: BooleanTableCell\u0026lt;*\u0026gt;) = styleable.useCircles } val CIRCLE_SIZE_META_DATA: CssMetaData\u0026lt;BooleanTableCell\u0026lt;*\u0026gt;, Number\u0026gt; = object : CssMetaData\u0026lt;BooleanTableCell\u0026lt;*\u0026gt;, Number\u0026gt;( \u0026#34;-wfx-circle-size\u0026#34;, StyleConverter.getSizeConverter() ) { override fun isSettable(styleable: BooleanTableCell\u0026lt;*\u0026gt;) = !(styleable.circleSize.isBound) override fun getStyleableProperty(styleable: BooleanTableCell\u0026lt;*\u0026gt;) = styleable.circleSize } private val cssMetaDataList = (TableColumn.getClassCssMetaData() + SHOW_FALSE_META_DATA + USE_CIRCLES_META_DATA + CIRCLE_SIZE_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } init { styleClass += \u0026#34;boolean-column\u0026#34; setCellValueFactory { extractor.invoke(it.value).asObject() } setCellFactory { BooleanTableCell(showFalse, useCircles, circleSize) } } } class BooleanTableCell\u0026lt;T\u0026gt;( val showFalse: StyleableBooleanProperty, val useCircles: StyleableBooleanProperty, val circleSize: StyleableDoubleProperty ) : TableCell\u0026lt;T, Boolean\u0026gt;() { override fun getControlCssMetaData() = (super.getControlCssMetaData() + BooleanColumn.getClassCssMetaData()) as MutableList init { val booleanItem = booleanProperty(itemProperty()) styleClass += \u0026#34;boolean-table-cell\u0026#34; graphic = StackPane( ImageView(BooleanColumn::class.java.getResource(\u0026#34;checkmark.png\u0026#34;)?.toString()).apply { fitWidth = 14.0 fitHeight = 14.0 visibleProperty().bind(booleanItem.and(useCircles.not())) this.styleClass += \u0026#34;true-image\u0026#34; }, ImageView(BooleanColumn::class.java.getResource(\u0026#34;crossmark.png\u0026#34;)?.toString()).apply { fitWidth = 14.0 fitHeight = 14.0 visibleProperty().bind(booleanItem.not().and(showFalse).and(useCircles.not())) this.styleClass += \u0026#34;false-image\u0026#34; }, Circle().apply { visibleProperty().bind(booleanItem.and(useCircles)) radiusProperty().bind(circleSize) fill = Color.MEDIUMSEAGREEN styleClass += \u0026#34;true-circle\u0026#34; }, Circle().apply { visibleProperty().bind(booleanItem.not().and(useCircles).and(showFalse)) radiusProperty().bind(circleSize) fill = Color.FIREBRICK styleClass += \u0026#34;false-circle\u0026#34; } ) } } class TableData3(initVal1: String, initVal2: String, initVal3: String, initVal4: Boolean) { val value1: StringProperty = SimpleStringProperty(initVal1) val value2: StringProperty = SimpleStringProperty(initVal2) val value3: StringProperty = SimpleStringProperty(initVal3) val value4: BooleanProperty = SimpleBooleanProperty(initVal4) } fun main() = Application.launch(Example3::class.java) and the CSS file:\n.boolean-column { -wfx-show-false: false; -wfx-use-circles: true; -wfx-circle-size: 5.0; } .boolean-column .true-image { -fx-fit-width: 14px; -fx-fit-height: 14px; -fx-image: url(checkmark.png); } .boolean-column .false-image { -fx-fit-width: 14px; -fx-fit-height: 14px; -fx-image: url(crossmark.png); } .boolean-column .true-circle { -fx-fill: green; } .boolean-column .false-circle { -fx-fill: red; } We have 3 StyleableProperties here. One to control the image/circle display, one to control the size of the circles (if used), and one to determine if anything will be displayed when the value is false. These StyleableProperties are all passed to the BooleanTableCell as constructor parameters.\nThe BooleanTableCell has a graphic that is just a StackPane with 2 ImageViews and 2 Circles in it. The visibility of each of those elements is bound to dependencies on showFalse, useCircles and the item value, such that only one of them (or maybe none of them) will ever be visible. Note that the item Property is of type ObjectProperty\u0026lt;Boolean\u0026gt;, so you don\u0026rsquo;t have the Fluent API for BooleanProperty. To get around this, the variable booleanItem is instantiated using BooleanProperty.booleanProperty() which is automatically bound to item but is of type BooleanProperty. Then the Fluent API can be used in the visibility bindings.\nThere are default values for the ImageView images and the Circle colours. However, no provision has been made to allow them to be modified from the layout. This means that the BooleanTableCell will work without any Style Sheet configuration at all, but you cannot change the colours without using a style sheet.\nAccessing the BooleanTableCell Programmatically # OK. You can\u0026rsquo;t do this. Even if you could find a way to do so, you\u0026rsquo;d be breaking the underlying paradigm for TableView and it would be a bad idea.\nThe whole idea behind TableView is that the TableCells at runtime are kept at \u0026ldquo;arms length\u0026rdquo; from your code. You don\u0026rsquo;t know how many there are, you don\u0026rsquo;t have references to them, you don\u0026rsquo;t know which ones are currently in use and you don\u0026rsquo;t access them directly. What you can do is define them, and tell the system how to create one.\nThis means that you cannot programmatically call a method on them to configure it after it has been instantiated, and they can only be instantiated through the Callback specified in the TableColumn. What you can interact directly with, however, is the TableColumn. That\u0026rsquo;s why the StyleableProperties are defined in the BooleanColumn and then passed to the BooleanTableCell in its constructor. That\u0026rsquo;s also why the three infix decorator methods are defined in the BooleanColumn.\nThis means you can do this:\ncolumns += BooleanColumn\u0026lt;TableData3\u0026gt;(\u0026#34;Column C\u0026#34;) { it.value4 }.apply { styleClass += \u0026#34;c-column\u0026#34; } withFalseValues false withCircles true withCircleSize 18.0 Accessing the ImageViews and Circles # Because the ImageViews and the Circles are defined in the BooleanTableCell, you cannot access them programatically from your layout code at all.\nThis is why all four of these elements have unique styleclass selectors. This allows you to style just about everything about them from the style sheet.\nOnce again, if you do want to programatically configure them, you\u0026rsquo;ll have to do it through the BooleanColumn and then pass the values to the BooleanTableCell in it\u0026rsquo;s constructor.\nJavaDocs # My experience has been that most people find that understanding layout code is the most difficult aspect of enhancing and doing maintenance of a JavaFX application. Because of this, anything that you can do to reduce and simplify your layout code is going to be a big win.\nIf you can define a TableView with 10 columns with 10-15 lines of code, and if 10 of those lines are just simple constructor calls for custom TableColumns you can go a long way towards simplifying to layout code. This is especially true if you use good, descriptive names for your custom TableColumns.\nLook at our BooleanColumn, it can be instantiated like this:\ncolumns += BooleanColumn(\u0026#34;Some Heading\u0026#34;){item -\u0026gt; item.someBooleanProperty()} But, if the maintenance programmer has to click through into BooleanColumn to see how it works and how to configure some aspect of its display, then it gets harder, not easier, for them.\nIt\u0026rsquo;s really important to make that information readily available without forcing maintenance programmers to read the source code. JavaDocs can be crucial here. Let\u0026rsquo;s take a look at some JavaDocs (KDocs actually, because it\u0026rsquo;s Kotlin) for BooleanColumn\n/** * TableColumn for the display of Boolean values. * * ## Infix Decorator Methods * Values can be displayed as either ImageViewss or Circles, with ImageViews as the default behaviour. Use * `BooleanColumn.withCircles(true)` to change to Circles. * * Graphics may be shown for both true and false values, or for only true values. The default is to * display graphics for both. Use `BooleanColumn.withFalseValues(false)` to suppress graphics for * false values. * * If Circles are used, the radius of the Circles may be specified with through `BooleanColurm.withCircleSize(Double)`. * The default Circle radius is 7.0px. * * ## StyleSheet Selectors * - Column: `boolean-column` * - TableCell: `boolean-table-cell` * - ImageViews: `true-image` and `false-image` * - Circles: `true-circle` and `false-circle` * * ## Styleable Properties * - Show false values: `-wfx-show-false` * - Use Circles: `-wfx-use-circles` * - Circle size: `-wfx-circle-size` * * @param title Column heading title * @param extractor Function that will return a Boolean property give an item of the TableView type. */ We have the standard stuff that explains the constructor parameters, of course. But equally important is all the information that they\u0026rsquo;d need to do configuration in the layout, or to tailor the StyleSheet.\nIt looks like this in my IDE:\nThere\u0026rsquo;s probably more that I should add in, but you get the idea.\nConclusion # This is another one of the cases where JavaFX gives you all the raw tools that you need to build things exactly the way that you want without cluttering up the library with classes that do things the way that they want. Even more, TableColumns and TableCells are extremely generic and almost impossible to use out-of-the-box without doing some kind of customization. So why not do that customization \u0026ldquo;right\u0026rdquo;, do it completely, and then just re-use it over and over again?\nA key concept here is recognizing that TableCells that go with a certain kind of data go with TableColumns of the same kind of data. The two are a matched pair. Once you think of it this way, it\u0026rsquo;s fairly clear how to design a TableColumn such that it supports a matched TableCell. And then, in your layout code, you simply don\u0026rsquo;t have to think about the TableCells at all because they just work.\nThe difference in your layout code between using a pre-built, custom, TableColumn and doing the configuration on the fly is huge. I don\u0026rsquo;t think you can overestimate how much baggage that in-layout configuration brings with it. Using a custom TableColumn eliminates all of that baggage and can make your TableView configuration absolutely trivial.\n","date":"10 June 2025","externalUrl":null,"permalink":"/javafx/elements/custom-table-columns/","section":"JavaFX","summary":"Generally, certain kinds of data need to be displayed in TableViews in certain, specific ways.  This article will show you how to create custom TableColumns, along with matching custom TableCells, that you can use over and over in every application that you build.","title":"Type Specific TableColumns","type":"javafx"},{"content":" Introduction # An interesting question come up on the Reddit JavaFX subreddit recently. Someone was having issues with capturing the value from a ComboBox once the user had \u0026ldquo;locked in\u0026rdquo; their selection by hitting \u0026lt;Enter\u0026gt; or tabbing away from the ComboBox.\nThe key difficulty is that ComboBox doesn\u0026rsquo;t have any explicit signal for a \u0026ldquo;locked-in\u0026rdquo; selection. There\u0026rsquo;s no ActionEvent fired at lock-in, and no separate Property to hold a locked-in value.\nSo, how do you tell when a value has been locked-in?\nIt turns out that the visibility of the pop-up ListView of the ComboBox is the determiner. If the pop-up ListView (let\u0026rsquo;s just call it the \u0026ldquo;pop-up\u0026rdquo; from now on) is showing, then the value hasn\u0026rsquo;t been locked in. If they tab away or hit \u0026lt;Enter\u0026gt; then the pop-up disappears and the value stays the same and can be considered \u0026ldquo;locked-in\u0026rdquo;.\nThe question is, how do you differentiate in your application between a transient editing value and the locked in value when you are binding this value to the rest of your GUI or to your Model?\nThis is problematic because, if you have your GUI dynamically responding to changes in the selected value - perhaps making different parts of your GUI visible or invisible, or changing the content of Labels - you might not want these changes to happen due to transient, un-locked-in, values in the ComboBox.\nActions and Values # In general, you have three mechanisms to make your JavaFX GUI respond dynamcially:\nActions These are usually invoked through Events which are \u0026ldquo;fired\u0026rdquo; in certain conditions. Actions are good when you want to capture, \u0026ldquo;something happened\u0026rdquo;, and are implemented via EventHandlers. Values JavaFX values are usually enclosed in Observable wrapper classes that allow other elements of the GUI to respond to changes in their value. The main mechanism to implement this approach is Bindings. Listeners It is also possible to turn a change in the value contained in an Observable object into an action. This is done via a Listener or a Subscription. I think it\u0026rsquo;s important to understand that there are some things that are fundamentally \u0026ldquo;actions\u0026rdquo; just by their nature. When a user clicks on a Button, that\u0026rsquo;s definitely an action and is probably best treated in your application as an action.\nBut there are lots of other things that might be actions but also might be just a data change. A user clicking on a CheckBox is an example of this. Yes, there\u0026rsquo;s clearly a user action taking place, but the main result might simply be that the selectedProperty() of the CheckBox has changed.\nThe point is that you need to be intentional in your programming. If you need to run some business logic when the user clicks on a CheckBox then you can put an EventHandler on the Checkbox's onAction Event. However, if you take this approach, then your business logic won\u0026rsquo;t run if you change the value in the CheckBox prgrammatically, or through a bidirectional Binding. That may be okay if your intention is to respond to a user interaction with the GUI. If you need to run your business logic whenever the value in the CheckBox changes, then maybe a Subscription is a better idea.\nHowever, capturing an Event to trigger an action that simply copies data from one ObservableValue to another is probably going to be the wrong approach. In those cases, you\u0026rsquo;re better off using a Binding to link the two ObservableValues together.\nBut sometimes, the connection between the two ObservableValues seems to be too complicated to use a Binding. And that\u0026rsquo;s what we\u0026rsquo;re going to talk about here\u0026hellip;\nThe ComboBox Scenario # Before we talk about solutions, we need to see the problem in action and understand what causes it. So here\u0026rsquo;s a simple application that demonstrates the problem:\nclass ComboBoxTest1 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 400.0) stage.show() } private fun createContent(): Region = VBox(20.0).apply { val model = Model() children += Label().apply { textProperty().bind(model.testValue.map { \u0026#34;The current value in the ComboBox: $it\u0026#34; }) } children += ComboBox(model.valueList).apply { model.testValue.bind(valueProperty()) } padding = Insets(40.0) } } class Model { val testValue = SimpleObjectProperty\u0026lt;String\u0026gt;(\u0026#34;\u0026#34;) val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;abj\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;hij\u0026#34;, \u0026#34;mno\u0026#34;, \u0026#34;xyz\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;) } fun main() = Application.launch(ComboBoxTest1::class.java) The textProperty() of the Label is bound to the testValue Property of the Model which is bound to the valueProperty() of the ComboBox.\nThe result looks like this:\n{% include video id=\u0026ldquo;vj96zSHzCiE\u0026rdquo; provider=\u0026ldquo;youtube\u0026rdquo; %}\nYou can see how the value in the Label changes as the user scrolls through the values using the up and down keys. But, as we\u0026rsquo;ve already discussed, these \u0026ldquo;values\u0026rdquo; while the pop-up is expanded are transient and you might not want them to cause dynamic changes to your GUI.\nDetecting the \u0026ldquo;Final\u0026rdquo; Value # There are two ways to make a selection in a ComboBox. When you use the keyboard and hit \u0026lt;Enter\u0026gt; or click on a selection with the mouse.\nUnfortunately, there is no Property in ComboBox that says, \u0026ldquo;The selection has been confirmed by the user\u0026rdquo;. Nor is there an Event that is fired that says, \u0026ldquo;The selection has been confirmed by the user\u0026rdquo;.\nWhat does happen, however, is that the pop-up ListView disappears. And there are two Events for that; onHiding and onHidden. The first fires just before it hides the pop-up, and the second fires just after it hides the pop-up.\nThis means that we can capture something related to the user confirming their selection. So let\u0026rsquo;s try that:\nclass ComboBoxTest2 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 400.0) stage.show() } private fun createContent(): Region = VBox(20.0).apply { val model = Model2() children += Label().apply { textProperty().bind(model.testValue.map { \u0026#34;The current value in the ComboBox: $it\u0026#34; }) } children += ComboBox(model.valueList).apply { setOnHidden { model.testValue.value = value } } padding = Insets(40.0) } } class Model2 { val testValue = SimpleObjectProperty\u0026lt;String\u0026gt;(\u0026#34;\u0026#34;) val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;abj\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;hij\u0026#34;, \u0026#34;mno\u0026#34;, \u0026#34;xyz\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;) } fun main() = Application.launch(ComboBoxTest2::class.java) This looks like it works:\n{% include video id=\u0026ldquo;A55mPcAlbR8\u0026rdquo; provider=\u0026ldquo;youtube\u0026rdquo; %}\nThere is a Problem With This Approach # The origin of the problem is that ComboBox doesn\u0026rsquo;t really support the idea of a \u0026ldquo;locked-in\u0026rdquo; selection. The idea that if the pop-up is expanded, then user is still in the process of making a selection is a bit of a kludge.\nThis approach seems to work. However, look at this:\n{% include video id=\u0026ldquo;49sKadCt9T0\u0026rdquo; provider=\u0026ldquo;youtube\u0026rdquo; %}\nOh no!\nIt turns out that the user can use the up and down arrow keys to change the selection without expanding the pop-up. In this case, we have no onHidden Event to capture the lock-in, and we never update our Model with the selected value. Additionally, if we change the value programmatically while the pop-up is closed, then it won\u0026rsquo;t update the Model either.\nThis points out a fundamental difference between programming to events vs programming to state. Events can be missed- or never happen - and then your data can get out of sync. Programming to state doesn\u0026rsquo;t have this problem.\nSo how can we turn this into a \u0026ldquo;State\u0026rdquo; approach?\nComboBox gives us two Properties that we can use: showing and value. We only want to update our Model when either one of the two situations occurs:\nWhen the value changes while showing is false. When showing changes from true to false. We can do this with two Subscriptions, one on each Property:\nMultiple Subscriptions # Here\u0026rsquo;s the code:\nclass ComboBoxTest3 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 400.0) stage.show() } private fun createContent(): Region = VBox(20.0).apply { val model = Model3() children += Label().apply { textProperty().bind(model.testValue.map { \u0026#34;The current value in the ComboBox: $it\u0026#34; }) } children += ComboBox(model.valueList).apply { showingProperty().subscribe { newVal -\u0026gt; if (!newVal) model.testValue.value = value } valueProperty().subscribe { newVal -\u0026gt; if (!isShowing) model.testValue.value = newVal } } padding = Insets(40.0) } } class Model3 { val testValue = SimpleObjectProperty\u0026lt;String\u0026gt;(\u0026#34;\u0026#34;) val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;abj\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;hij\u0026#34;, \u0026#34;mno\u0026#34;, \u0026#34;xyz\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;) } fun main() = Application.launch(ComboBoxTest3::class.java) And this works as you would expect:\n{% include video id=\u0026ldquo;lXiFAlgl2aI\u0026rdquo; provider=\u0026ldquo;youtube\u0026rdquo; %}\nThe biggest problem that I have with this approach is that it overtly takes state changes and turns them into actions in order to update the Model. Surely this is something better done with a Binding.\nThe next issue that I have with this approach is that it needs to be defined in a scope that has access to both the Model and the properties of the ComboBox. You could, however, put it in a method and pass the Model field, and the two ComboBox Properties to it.\nFinally, it\u0026rsquo;s really hard to generalize this, and it virtually needs to be defined as part of the layout. It\u0026rsquo;s also hard to create the relationship between the value and showing without having a Model to connect them to.\nMaybe there\u0026rsquo;s a better way to do this\u0026hellip;\nCreating a Conditional Binding # It is possile to create a Binding that has dependencies on two different ObservableValues. As a matter of fact, we do this all the time, and it\u0026rsquo;s easy to do. The problem is that Bindings don\u0026rsquo;t have any way to say \u0026ldquo;Don\u0026rsquo;t change anything\u0026rdquo; in their output. So, if the value property changes while showing is true then how do we say, \u0026ldquo;Do nothing\u0026rdquo;?\nHere\u0026rsquo;s how:\nclass ConditionalBinding\u0026lt;T\u0026gt;( private val dependency: ObservableValue\u0026lt;T?\u0026gt;, private val condition: ObservableBooleanValue ) : ObjectBinding\u0026lt;T?\u0026gt;() { init { super.bind(dependency, condition) } private var oldValue: T? = null override fun computeValue(): T? { if (condition.value) { oldValue = dependency.value } return oldValue } } Let\u0026rsquo;s look at how this works\u0026hellip;\nFirst, we\u0026rsquo;ve introduced an internal field, oldValue of the same type as the generic type of the ConditionalBinding.\nSecondly, in the computeValue() method, which is the method that we\u0026rsquo;re used to using to create custom Binding classes, we have some logic that updates oldValue based on the two conditions described above.\nFinally, computeValue() will always return whatever is in oldValue, regardless of whether or not it has been updated.\nAnd we can use it like this:\nclass ComboBoxTest4 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 400.0) stage.show() } private fun createContent(): Region = VBox(20.0).apply { val model = Model4() children += Label().apply { textProperty().bind(model.testValue.map { \u0026#34;The current value in the ComboBox: $it\u0026#34; }) } children += ComboBox(model.valueList).apply { model.testValue.bind(ConditionalBinding(valueProperty(), showingProperty().not())) } padding = Insets(40.0) } } class Model4 { val testValue = SimpleObjectProperty\u0026lt;String\u0026gt;(\u0026#34;\u0026#34;) val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;abj\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;hij\u0026#34;, \u0026#34;mno\u0026#34;, \u0026#34;xyz\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;) } fun main() = Application.launch(ComboBoxTest4::class.java) This functions exactly the same way that the two Subscription version does.\nHowever, you can see that the mechanics of it are completely hidden from the layout, and that we can instantiate the ConditionalBinding without any reference to the Observable that it will be bound to - just like any other Binding. In fact, we could instantiate to a variable without binding it to anything at all - from that point on we could treat it like any other ObservableValue.\nFurthermore, now that we\u0026rsquo;ve decided that this is how we want to handle ComboBox, we can put this stuff into a library and forget all about it, like this:\nfun \u0026lt;T : Any\u0026gt; ComboBox\u0026lt;T\u0026gt;.getConditionBinding() = ConditionalBinding(valueProperty(), showingProperty().not()) infix fun \u0026lt;T : Any\u0026gt; ComboBox\u0026lt;T\u0026gt;.conditionallyBind(boundProperty: ObjectProperty\u0026lt;T\u0026gt;) = apply { boundProperty.bind(getConditionBinding()) } These are extension functions, which in Kotlin allow us to add new methods to an existing class without formally extending it. In this case, we\u0026rsquo;re adding two new functions to ComboBox, but you could do the same thing in Java by creating static methods that accept a ComboBox as a parameter. Kotlin just makes it easier and cleaner.\nThe first extension function just creates a ConditionalBinding based on the ComboBox, and the other is a decorator that creates a ConditionalBinding and binds it to a Property. The infix notation allows us to use it without enclosing the parameter in brackets and without prefixing it with a ..\nNow our code looks like this:\nclass ComboBoxTest5 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 400.0) stage.show() } private fun createContent(): Region = VBox(20.0).apply { val model = Model5() children += Label().apply { textProperty().bind(model.testValue.map { \u0026#34;The current value in the ComboBox: $it\u0026#34; }) } children += ComboBox(model.valueList) conditionallyBind model.testValue padding = Insets(40.0) } } class Model5 { val testValue = SimpleObjectProperty\u0026lt;String\u0026gt;(\u0026#34;\u0026#34;) val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;abj\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;hij\u0026#34;, \u0026#34;mno\u0026#34;, \u0026#34;xyz\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;) } fun main() = Application.launch(ComboBoxTest5::class.java) You can see here that we\u0026rsquo;ve completely removed all of the \u0026ldquo;plumbing\u0026rdquo; related to the Binding out of the layout code. We have a library that now includes a custom Binding class, and two extension functions that make any ComboBox that we use work as close as we can to the way that we expect it to work without customizing ComboBox itself. And it doesn\u0026rsquo;t clutter up our layout at all.\nBindings With State # If you are like me, you\u0026rsquo;ve never thought of Bindings as anything more than connective tissue to join together various ObservableValues in your GUI and your Model.\nBut this ConditionalBinding is different. It doesn\u0026rsquo;t just say, \u0026ldquo;Manipulate these values as they change to create a new changing value\u0026rdquo;.\nIt has it\u0026rsquo;s own, hidden, \u0026ldquo;State\u0026rdquo;. That\u0026rsquo;s the oldValue field. And this changes everything.\nLet\u0026rsquo;s be clear, this is still a Binding and, as such, it\u0026rsquo;s job remains to respond to changes in its dependencies and potentially calculate a new value in response to changes in those dependencies. But now you can do different things.\nA Change Counter # Let\u0026rsquo;s try adding an extra Label that shows how many times the value in the ComboBox has had locked-in changes. We can create a custom Binding like this:\nclass CountingBinding\u0026lt;T\u0026gt;(private val dependency: ObservableObjectValue\u0026lt;T?\u0026gt;) : IntegerBinding() { init { super.bind(dependency) } private var oldValue: T? = dependency.value private var counter = 0 override fun computeValue(): Int { if (oldValue != dependency.value) { oldValue = dependency.value counter++ } return counter } } We have to be careful here. The Binding will potentially recompute every time that the dependencies are invalidated - not every time that it changes. So if we want to track changes, then we need to weed out the invalidations that don\u0026rsquo;t represent changes to the value. So to do this, we introduce an oldValue field, and compare the current value to it (and potentially update it) in computeValue(). The counter field keeps track of how many times the value has changed.\nWe can use it like this:\nclass ComboBoxTest6 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 400.0) stage.show() } private fun createContent(): Region = VBox(20.0).apply { val model = Model6() children += Label().apply { textProperty().bind(model.testValue.map { \u0026#34;The current value in the ComboBox: $it\u0026#34; }) } children += Label().apply { textProperty().bind(CountingBinding(model.testValue).map { \u0026#34;Has changed $it times\u0026#34; }) } children += ComboBox(model.valueList) conditionallyBind model.testValue padding = Insets(40.0) } } class Model6 { val testValue = SimpleObjectProperty\u0026lt;String\u0026gt;() val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;abj\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;hij\u0026#34;, \u0026#34;mno\u0026#34;, \u0026#34;xyz\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;) } fun main() = Application.launch(ComboBoxTest6::class.java) And it does what you would expect it to. The counter increments every time that model.testValue changes.\nOne thing to note is that Model6.testValue is now initialized with a null value. That\u0026rsquo;s because, when it\u0026rsquo;s bound to the ComboBox its initial value is set to null and if we want to avoid starting off with a non-zero counter we need to avoid the first change from \u0026quot;\u0026quot; to null.\nThe really interesting thing about this Binding is that it doesn\u0026rsquo;t return anything that you could consider a manipulation of the values passed to it via its dependencies. It\u0026rsquo;s something completely unique.\nAnother thing you could do is to expand our class to be more than just a Binding. Let\u0026rsquo;s add a reset() function to it:\nclass CountingBinding2\u0026lt;T\u0026gt;(private val dependency: ObservableObjectValue\u0026lt;T?\u0026gt;) : IntegerBinding() { init { super.bind(dependency) } fun reset() { counter = 0 invalidate() } private var oldValue: T? = dependency.value private var counter = 0 override fun computeValue(): Int { if (oldValue != dependency.value) { oldValue = dependency.value counter++ } return counter } } The important thing here is to remember that this is still essentially a Binding and we want it to behave properly. So the reset() function needs to call invalidate() which will force any bound elements to call computeValue().\nNow we can do this:\nclass ComboBoxTest7 : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent(), 400.0, 400.0) stage.show() } private fun createContent(): Region = VBox(20.0).apply { val model = Model7() val countingBinding = CountingBinding2(model.testValue) children += Label().apply { textProperty().bind(model.testValue.map { \u0026#34;The current value in the ComboBox: $it\u0026#34; }) } children += Label().apply { textProperty().bind(countingBinding.map { \u0026#34;Has changed $it times\u0026#34; }) } children += Button(\u0026#34;Reset\u0026#34;).apply { setOnAction { countingBinding.reset() } } children += ComboBox(model.valueList) conditionallyBind model.testValue padding = Insets(40.0) } } class Model7 { val testValue = SimpleObjectProperty\u0026lt;String\u0026gt;() val valueList: ObservableList\u0026lt;String\u0026gt; = FXCollections.observableArrayList(\u0026#34;abc\u0026#34;, \u0026#34;abj\u0026#34;, \u0026#34;def\u0026#34;, \u0026#34;hij\u0026#34;, \u0026#34;mno\u0026#34;, \u0026#34;xyz\u0026#34;, \u0026#34;123\u0026#34;, \u0026#34;456\u0026#34;) } fun main() = Application.launch(ComboBoxTest7::class.java) Here we\u0026rsquo;ve instantiated countingBinding as a CountingBinding2 so that we hava a reference to it that includes access to the reset() function. Then we\u0026rsquo;ve added a Button that will call countingBinding.reset() when it\u0026rsquo;s clicked. The result looks like this:\n{% include video id=\u0026ldquo;EMMHsce1LGA\u0026rdquo; provider=\u0026ldquo;youtube\u0026rdquo; %}\nThis is cool, and probably not something you\u0026rsquo;ve thought to do via a Binding before. I know that I hadn\u0026rsquo;t.\nBut is this something that you want or should do?\nThat\u0026rsquo;s for you to decide. The key question to ask is whether or not using a technique like this makes your code easier to understand and maintain or not, and there\u0026rsquo;s a lot of other considerations that come into that. It seems to me that if you\u0026rsquo;re doing this as a \u0026ldquo;one off\u0026rdquo;, then the uniqueness of this approach is going to require research for a maintenance programmer to understand. You\u0026rsquo;d need to offset that against the simplification of the layout code that results from the encapsulation of the functionality.\nOn the other hand, if you find yourself using this technique so much that you put the custom Binding class in a library and use it over and over, then it\u0026rsquo;s probably worthwhile.\nConclusion # I think that the idea of a ConditionalBinding keeps to the idea of a Binding as the connective tissue that holds a Reactive JavaFX design together. Used as described here, it provides a missing element that makes ComboBox work much closer to the way that you (well, at least that \u0026ldquo;I\u0026rdquo;) would expect it to work. At the same time, it sticks to idea of a Binding as an extension of ObservableValue and you can treate ConditionalBinding just like any other ObservableValue and it will work just fine.\nThe addition of internal state to a Binding is a powerful idea that can greatly expand the utility of Bindings, primarily by allowing you to encapsulate and isolate more complicated relationships between data elements.\nIf you look at the example code that utilizes ConditionalBinding, especially the version that uses the Kotlin extension functions on ComboBox, you\u0026rsquo;ll see that the mechanics of dealing with whether or not the pop-up is hidden or visible, and how that impacts updates to the Model, are completely absent from the layout code. That\u0026rsquo;s the power of encapsulation.\n","date":"20 July 2024","externalUrl":null,"permalink":"/javafx/elements/conditional-binding/","section":"JavaFX","summary":"A look at how to extend the idea of Bindings to include internal state to encapsulate the mechanics of more complicated relationships between ObservableValues in your GUI and your Model.","title":"Conditional Bindings","type":"javafx"},{"content":" Image and ImageView # ImageView is the basic JavaFX Node to display images in your GUI. Image is the class used to hold the data contained within an ImageView. The relationship between the two is very similar to the relationship between Label and String.\nMastering ImageView is fairly simple and allows you to add a lot of extra flair to your layouts. From putting images on Buttons, to adding logos and image backgrounds, ImageView is the essential class to know.\nThe Articles: # ","date":"10 April 2022","externalUrl":null,"permalink":"/javafx/elements/nodes/images/","section":"JavaFX","summary":"Images are an important part of many layouts.","title":"Handling Images In JavaFX","type":"javafx"},{"content":"One of the biggest issues that JavaFX beginners (and a lot of non-beginners, too) face is how to create a busy GUI that does not get bogged down and laggy. Animations that should be smooth can stutter and pause, and the GUI can even become completely unresponsive from time to time. Things that should never happen to a UI.\nHow does this happen? This often gets blamed on the \u0026ldquo;Lengthy GUI Task\u0026rdquo;. The idea that the GUI gets caught up doing something long and complex and cannot service the other tasks and requests that are constantly coming in.\nI believe this to be largely a myth. Most of the time when the FXAT is occupied performing a long task, most of that task has nothing to do with the GUI and should be executed on a background thread. That background thread should then submit the actual GUI updates to the FXAT, as they are required, using one of the standard JavaFX techniques.\nAnother StackOverflow Question # I see a lot of StackOverflow questions that get closed without an answer because they don\u0026rsquo;t fit the narrow range of criteria that StackOverflow mandates for question quality. Often this is because the questions are too broad and don\u0026rsquo;t lend themselves to a specific answer. A lot of times, however, these overly broad questions raise great issues which lend themselves to a longer discussion.\nThis recent question, brought up a topic which plagues a lot of developers, and doesn\u0026rsquo;t seem to be going away any time soon:\nI\u0026rsquo;m frustrated in not being able to have multiple independently operating UI elements in FX. As a simple example, I can put a counter (textbox with an AnimationTimer that updates it counting as fast as itToning down admonition backgrounds. can), and it works fine just sitting there. But as soon as I start to do other things in the UI, it interrupts my counter causing it stall/pause momentarily.\nHow can I have a timer (let alone sophisticated animated charts or something) that continue to update seamlessly and independently of anything else going on in the UI?\nIn response to one of the comments, the OP elaborates a bit more:\nWhat I\u0026rsquo;m asking for is a way to have multiple UI threads - so that something going on in UI thread A does not impact what is happening in UI Thread B. If that is simply not possible in FX, then it \u0026ldquo;is what it is\u0026rdquo;, but I wonder how one would write something like a stock trading App, where where there are multiple windows with continuously updating charts, graphs, etc. that are not paused when a lengthy UI task is going on in another window.\nSo there you have it in a nutshell. The OP is having difficulty with a GUI that\u0026rsquo;s suffering from performance issues and suggests (not unreasonably) that perhaps having more than one FXAT would solve the issue as the GUI could then multitask. The assumption is that this is necessary because of \u0026ldquo;lengthy UI tasks\u0026rdquo;.\nWhat is a Long GUI Task? # I have come to believe that there is no such thing. The question seems to imply that updating charts and graphs would meet the definition. But what is \u0026ldquo;updating charts and graphs\u0026rdquo;? To me, that would mean that my code would update some kind of ObservableList, since those are the foundations for TableView, ListView and Charts. The code that runs on the FXAT shouldn\u0026rsquo;t do anything else.\nBut wouldn\u0026rsquo;t that trigger a lengthy GUI task inside the JavaFX library? That seems like a reasonable question to me. I strongly suspect, however, that the developers of the JavaFX library would have thought of that and avoided that problem. But it\u0026rsquo;s worth looking to see.\nSimulating a Busy, Busy Screen # I\u0026rsquo;ve never written a stock trading application, but I think it\u0026rsquo;s clear what the question is asking about. Imagine a screen with a number of graphs and charts that are constantly being updated with data from external API\u0026rsquo;s. Possibly multiple sources, and many updates each second. It\u0026rsquo;s vital that the screen is constantly refreshed with the latest data so that the user can make split second decisions.\nThe implication here is that the sheer quantity of updates is somehow causing so much work for the FXAT that it can\u0026rsquo;t keep up with it with just a single thread, and that having the ability to have multiple threads would make the screen more fluid.\nNow that is something that we can simulate fairly easily:\npublic class LongUiTask extends Application { public static void main(String[] args) { launch(args); } private static int buttonNumber = 0; @Override public void start(Stage primaryStage) { FlowPane pane = new FlowPane(); pane.getChildren().addAll(IntStream.range(0, 80).mapToObj(idx -\u0026gt; createButton()).collect(Collectors.toList())); pane.getChildren().addAll(IntStream.range(0, 20).mapToObj(idx -\u0026gt; createTimerButton()).collect(Collectors.toList())); primaryStage.setScene(new Scene(pane)); primaryStage.show(); } @NotNull private Button createTimerButton() { Button timerButton = new Button(\u0026#34;Timer\u0026#34;); timerButton.setOnAction(startEvt -\u0026gt; { IntegerProperty counter = new SimpleIntegerProperty(0); Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(10000), new KeyValue(counter, 10000))); timerButton.textProperty().bind(Bindings.createStringBinding(() -\u0026gt; Integer.toString(counter.get()), counter)); timeline.play(); }); return timerButton; } private Node createButton() { Button button = new Button(\u0026#34;Start - \u0026#34; + buttonNumber++); button.setOnAction(evt -\u0026gt; countFast(button)); return button; } private void countFast(Button button) { Thread thread = new Thread(() -\u0026gt; { int counter = 0; do { try { sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } String theCount = Integer.toString(counter++); Platform.runLater(() -\u0026gt; button.setText(theCount)); } while (true); }); thread.setDaemon(true); thread.start(); } } What This Application Does # This is a window with a single container in it; a FlowPane. Inside the FlowPane are 100 Buttons. The first 80 Buttons all launch background tasks that \u0026ldquo;count as fast as they can\u0026rdquo;, which is the situation that the StackOverflow question specifically posed. These background tasks are all written so that they update the label on their associated Button, converting the current count number to a String. Essentially, once each of these buttons are clicked, their labels will change to a rapidly incrementing counter.\nThe next 20 buttons each launch a JavaFX TimeLine, which will start a counter which will increment an IntegerProperty once each second. The IntegerProperty is then bound to the Text property of the Button so that it will display the current value of the IntegerProperty as a String. These \u0026ldquo;Timer\u0026rdquo; buttons are just that; timers which increment by one each second displaying how many seconds since they were clicked.\nUp and running, it looks like this (with the numbers in the Buttons whizzing away like crazy):\nWhat Does This Prove? # I chose FlowPane for the main container because it\u0026rsquo;s seems to be a fairly complicated layout for the GUI. All of the elements are arranged horizontally until the screen width is full, then it starts a new row below it and continues to place elements on that row until the screen width is full, and so on. When the buttons start to change their labels, their size changes and the layout has to be recalculated. Also, if you change the size of the Window, the layout will have to be re-evaluated as you drag the border in and out.\nIs this as complicated as a dozen charts and graphs constantly updating? Maybe not, but JavaFX is really efficient at handling the display so it\u0026rsquo;s probably easy to overestimate the load that an updating chart actually puts on the FXAT.\nThe Timer Buttons are important because they are 100% FXAT, and they simulate how multiple simultaneous animations would run smoothly under a heavy load.\nIs this a heavy load? # From a GUI perspective, I think it qualifies as a heavy load. Each of the background threads is generating just under 500 events on the FXAT each second, and there\u0026rsquo;s 80 of them, so that\u0026rsquo;s 40,000 events pushed into the queue each second. Not to mention 20 simultaneous animations happening on the Timer Buttons. (Note: I did some checking on the counters against real time, and it\u0026rsquo;s slower than 500 per second, so I\u0026rsquo;m assuming that there\u0026rsquo;s enough overhead in the sleep() itself that it puts in more than a 2 millisecond pause. So the number of events would be somewhat less than 40,000.)\nRelating back to the original question, what if this was a stock trading application? Isn\u0026rsquo;t it possible that there could be 500 buttons with the current price of 500 different stocks? How would it handle that?\nAssuming that you have some kind of normal access to stock market information, it\u0026rsquo;s highly unlikely that you are going to get API response times in the order of a few milliseconds for each request. Even if you could get 5 updates for each stock each second, that\u0026rsquo;s still only 2,500 updates to the GUI each second, less that 10% of the number of updates that this application posts to the FXAT.\nAnd the Results? # For me, everything went smoothly until I clicked on the last couple of Start buttons. I already had all of the Timers running. Up to around Start button number 75 all of the counter numbers were spinning away faster than the eye could see and everything was fluid. When I had all of the Start Buttons clicked, the numbers started to update about once every second, so the GUI was definitely affected.\nWith a lot, but not all of the Buttons clicked, things moved smoothly until I moved the cursor around the window. Mouse moves generate a LOT of events on the FXAT - thousands of events in a short period of time, and I suspect that these were pushing the GUI into overload.\nAnother quirk about this is that you can click on a button a second time and it will launch another background thread which will start updating the button number. So the number will flicker back and forth between two counters as each thread\u0026rsquo;s events update it. I started up about 30 of the Buttons and waited until the first few had hit 100,000. Then I clicked the first one again. The result was that the Button was now alternating between a 6 digit number and a much shorter number, causing the Button to resize and the FlowPane to re-evaluate the layout. I had the window sized up so that 6 Buttons with 6 digit numbers would fit across a row, but 7 would fit if the first Button had less digits. The result was that the screen was largely unreadable, but because the layout was reworked causing the buttons to flicker around from place to place on the screen. This was exactly as you\u0026rsquo;d expect if everything was working properly.\nWhen I first wrote this test application, there was no sleep() in the countFast() loop. I think this was pretty close to having an infinite NOP loop, and it caused grief with the whole JVM. Even my IDE started to hang. So it\u0026rsquo;s not totally clear whether having nearly 80 background threads is causing some resource issues within the JVM and is affecting the GUI, or whether it\u0026rsquo;s the sheer number of events generated by those threads.\nI tried using a 1 millisecond sleep time, and it started to have trouble when about 45 counter Buttons had been clicked.\nBut What About Charts and Graphs? # I did some further testing. I created a screen with a LineChart and one of those counter Buttons from my test program above.\nThen I loaded up 30 XYChart.Series into the LineChart, and launched a background task for each one that would create an (x,y) pair for each chart with x incrementing by one each time, and the y randomized a bit, but trending up. Each series started x =1 and y as a random number between 0 and 300. The background task looped around a 200 millisecond sleep, and then added a new (x,y) pair to its series. To keep the data series from becoming ridiculously huge, when x got to 1000, it reset it to 1 and any previous (x,y) with the same x was removed from the series. The logic looked like this:\nOptional\u0026lt;XYChart.Data\u0026lt;Number, Number\u0026gt;\u0026gt; dataToRemove = seriesData.stream().filter(data -\u0026gt; data.getXValue().equals(theCount)).findFirst(); Platform.runLater(() -\u0026gt; { dataToRemove.ifPresent(seriesData::remove); seriesData.add(new XYChart.Data\u0026lt;\u0026gt;(theCount, theValue)); }); How did this run?\nPerfectly. The LineChart used animation to restructure itself as the data sets grew to 1000 points, and then as the y values trended upwards. There should have been about 5 data points added each second for each of the 30 data sets. The counter Button was running and the numbers whizzed by faster than I could follow. At no point did anything on the LineChart or the Button stutter or pause.\nYes, But Those GUI Tasks are Small # That\u0026rsquo;s the point.\nThe user code in the FXAT should be small if you are taking the correct approach. When I design a JavaFX application I try to have all of the layout done in the setup at the beginning. I create a model that holds the data as properties, and bind those properties to the screen elements when creating the layout. Then very little of the code that runs after the setup will touch any of the screen at all, it just updates the properties in the model. A couple of exceptions to this would be:\nChanges to the screen display to disable elements when a background task is running. For instance, if I want to disable a Button as soon as it\u0026rsquo;s clicked that could happen in the Event Handler for the onAction() method of the Button. If the application needs to open up a DialogBox or some other on-the-fly screen element. Of course, these kinds of layout changes aren\u0026rsquo;t going to be happening 200 times a second either.\nKeep the Back End in the Background # Look at the code snippet above for the XYChart.Data pruning. Do you see how the stream() functionality is run in the background thread? I assumed that streaming through 1000 items looking for a specific x value could be a little time intensive, so I put that part in the background and stored the result in the Optional. Then the actual update to the ObservableList, to remove that item, was done on the FXAT.\nWhen I first wrote this test application, my countFast() method looked like this:\nprivate void countFast(Button button) { Thread thread = new Thread(() -\u0026gt; { AtomicInteger counter = new AtomicInteger(); do { try { sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } Platform.runLater(() -\u0026gt; button.setText(Integer.toString(counter.getAndIncrement()))); } while (true); }); thread.setDaemon(true); thread.start(); } I used AtomicInteger as a container for the counter because with the Platform.runLater() parameter being a lambda expression, it needed to be \u0026ldquo;final or effectively final\u0026rdquo;. The problem is that the counter.getAndIncrement() call is happening on the FXAT. It\u0026rsquo;s a small thing here, but that background thread has one job only - increment the counter every 2 milliseconds - and it was passing it off to the FXAT!\nThis is extremely easy mistake to make, and most of the time when users have issues with performance in JavaFX it\u0026rsquo;s because this is what they\u0026rsquo;ve done. They\u0026rsquo;ve put back end work into the FXAT.\nIt\u0026rsquo;s tempting to use the background task for nothing but the sleep(), and then put everything in the Platform.runLater(), but don\u0026rsquo;t do it.\nSummary # When I first started looking at this my thought was, \u0026ldquo;Who needs a counter that updates 500 times a second?\u0026rdquo; When I saw it running, though, I realized that the fluidity of the counter changing, and it\u0026rsquo;s perceived speed (mostly by watching the digits at the beginning of the number), can be valuable feedback to the user. I wasn\u0026rsquo;t expecting that.\nIt seems from what we\u0026rsquo;ve seen here that internal GUI updates and layout changes are optimized nicely by the JavaFX library. In that case, we don\u0026rsquo;t need to worry about those routines causing issues with the GUI response.\nIt is possible to overload the GUI by submitting massive numbers of jobs to the FXAT from background tasks. But it really takes a lot.\nThe key to success is to keep your GUI tasks as small as possible. Only do the things that need to be on the FXAT on the FXAT. Avoid running hundreds of lines of layout code in response to the results of a background process. Build the layout first, and then control its display through data bindings to your model.\nAnd no, you don\u0026rsquo;t need a second FXAT to multi-task the GUI.\n","date":"15 August 2021","externalUrl":null,"permalink":"/javafx/elements/fxat/lengthy-task/","section":"JavaFX","summary":"Is the Single-Threaded JavaFX GUI a Limitation?","title":"The Myth of the Lengthy GUI Task","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # The importance of having an application structure. How to build a bare-bones structure that runs. Creating a Framework # In order to organize our application so that the GUI will be separated from the business logic, and to facilitate the creation of a Reactive application, we\u0026rsquo;ll need to adopt a framework. There are lots to choose from, but I\u0026rsquo;m going to use one that I\u0026rsquo;ve developed myself that I find works very well.\nThis framework is called Model-View-Controller-Interactor (MVCI). Rather than spend a lot of time talking about how it works, we\u0026rsquo;ll just start by cooking it up as simply as possible - and it should be clear how things go as we build it out in our application.\nBut, to start with, we\u0026rsquo;ll need the skeleton to build everything on. So here we go.\nThe Application Itself # Since this is JavaFX, we still need to have a class that extends Application and one that implements main(). So, we\u0026rsquo;ll call it Main and add what we need to start using our framework.\npublic class Main extends Application { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(new CustomerController().getView())); primaryStage.show(); } } This is about the simplest Application implementation that you can have. We instantiate a Controller and then call its getView() method to get the \u0026ldquo;root\u0026rdquo; for our Scene, which we put into the Stage. Then we call Stage.show().\nThe Controller # public class CustomerController { private Builder\u0026lt;Region\u0026gt; viewBuilder; private CustomerInteractor interactor; public CustomerController() { CustomerModel model = new CustomerModel(); viewBuilder = new CustomerViewBuilder(model); interactor = new CustomerInteractor(model); } public Region getView() { return viewBuilder.build(); } } From this you can see that the Controller is responsible for instantiating all of the other parts of the framework. The Model is passed to the constructors of the ViewBuilder and the Interactor. This way all of the elements have a reference to the Model. You can see that getView() just delegates to the build() method of the ViewBuilder.\nThe View Builder # public class CustomerViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final CustomerModel model; public CustomerViewBuilder(CustomerModel model) { this.model = model; } @Override public Region build() { return new VBox(); } } There\u0026rsquo;s a key point hidden away here. Since we are only going to use our View as standard descendent of Region, we don\u0026rsquo;t need, nor do we want, a custom class extending some container Node, like VBox, HBox or BorderPane. It will be customized, but we\u0026rsquo;re not going to add any new features to it (which really means adding public methods), so just returning Region is all we want.\nTo do this, we use a Builder. So here you have a Builder. Right now, it just builds an empty VBox.\nThe Interactor # public class CustomerInteractor { private CustomerModel model; public CustomerInteractor(CustomerModel model) { this.model = model; } } Just the bare-bones here. A class with the appropriate constructor, but nothing else.\nThe Model # public class CustomerModel { } Even more bare-bones. We need this class, but it hasn\u0026rsquo;t come into play yet.\nThis Will Run # Yep. It\u0026rsquo;s complete, and you get an empty window on the screen. Woo-hoo!\nOkay, so there\u0026rsquo;s not much here, but\u0026hellip;\nYou should be able to see how all three of the other components share a reference to the Model so it\u0026rsquo;s going to be a way for them to communicate with each other without actually knowing about each other. That\u0026rsquo;s a crucial concept.\nYou should also be able to see that the Controller knows about both the ViewBuilder and the Interactor, yet neither of those classes knows about the Controller.\n","externalUrl":null,"permalink":"/beginners/part5/","section":"JavaFX","summary":"Building an application that “does something” means adopting a framework that works well with JavaFX.  Here we build the skeleton of the Model-View-Controller-Interactor framework that we’re going to use for our CRUD application.","title":"Part 5 - Application Framework","type":"javafx"},{"content":"","date":"5 August 2025","externalUrl":null,"permalink":"/javafx/elements/observables/properties/observable-lists/","section":"JavaFX","summary":"Let’s look at ObservableLists","title":"Observable Lists","type":"javafx"},{"content":" Introduction # TableView is an extremely popular control, but it\u0026rsquo;s also probably the most complex and least understood. This article is about how to manage the styling aspects of TableView, but it would help if you understood the basics. This page has several articles that explain how to create and customize TableViews in your layouts. If you are not comfortable with CSS in JavaFX and the Modena stylesheet, this page has articles that can help you.\nTableView is composed of a large number of components nested into several layers. Most of these components have their own styling elements, so understanding how to style TableView really means understanding how to style all of these components, and how they interact. The approach in this article is to look at virtually all of the style class selectors of all of the components and to examine how they are used.\nThis article is broken down into two main sections. The first is a tutorial section that explains some techniques that you can use to exploit the structure of TableView and the style class selectors that are available. The second is a reference section that lists every style class selector in every component element that I could track down. This should give you all the information you need to customize TableView.\nThere\u0026rsquo;s not much code in the article, but what code there is, is written in Kotlin.\nA Note on Selectors in the Article # In order to keep all of the examples as simple and as generic as possible, any references to style class selectors that might be ambiguous with other classes supported by Modena have been prefixed with .table-view.\nIn practice, it is probably better to add a custom style class selector to your TableView and then use that as the root of your selectors. For instance, the column-header selector is used with both TreeView and TableView. Let\u0026rsquo;s say that you want to style the TableColumnHeaders for a TableView in your layout\u0026hellip;\nStart by assigning a selector to the TableView, let\u0026rsquo;s say my-table. Then you would use:\n.my-table .column-header {} instead of:\n.table-view .column-header {} However, in this article you will only see the second form used.\nTutorials # One of the biggest challenges with styling TableView, apart from the fact that it\u0026rsquo;s quite complicated, is that many of the internal components are not exposed to the programming API. This means that you cannot extend them, modify them or change their implementation. All you can do is add styling for them via CSS.\nThe overall structure of TableView is that it is composed of two main parts: The header row, and the VirtualFlow which contains everything else. Inside the VirtualFlow there are the ScrollBars if required, and a clipping rectangle holding a Group called sheet. Inside sheet are the TableRows which in turn hold the TableCells.\nEvery component, from TableView, through VirtualFlow down to TableCell has style class selectors. Often they have several selectors, and they may be shared with other components.\nTableRow and TableCell Styling # We\u0026rsquo;ll start by looking at the body of TableView, which are the elements contained inside sheet.\nStandard Styling # The structure of the body of a TableView is a set of TableRows, each of which contains a number of TableCells. This means that the TableRow is the enclosing layout component for all of the TableCells. In standard Modena, the TableCells are normally transparent with a thin border on the right, resulting in much of the colour of the TableCells coming from the underlying TableRow:\n.table-cell { -fx-padding: 0.166667em; /* 2px, plus border adds 1px */ -fx-background-color: null; -fx-border-color: transparent -fx-table-cell-border-color transparent transparent; -fx-cell-size: 2.0em; /* 24 */ -fx-text-fill: -fx-text-background-color; } The main selector for TableRow is .table-row-cell and supports the odd and even Pseudo-classes from IndexedCell:\n.table-row-cell { -fx-background: -fx-control-inner-background; -fx-background-color: -fx-table-cell-border-color, -fx-background; -fx-background-insets: 0, 0 0 1 0; -fx-padding: 0; -fx-text-fill: -fx-text-background-color; } .table-row-cell:odd { -fx-background: -fx-control-inner-background-alt; } Where:\n-fx-control-inner-background-alt: derive(-fx-control-inner-background,-2%) And this is what you see in an otherwise unstyled TableView, the cell text sitting on alternating rows of grey and slightly-darker-grey.\nPrimarily, in Modena you\u0026rsquo;ll see styling for TableRows that have the Pseudo-class selected (often combined with filled):\n..table-view:focused \u0026gt; .virtual-flow \u0026gt; .clipped-container \u0026gt; .sheet \u0026gt; .table-row-cell:filled:selected { -fx-background: -fx-selection-bar; -fx-table-cell-border-color: derive(-fx-selection-bar, 20%); } Which is the bluish colour you\u0026rsquo;re familiar with:\nWith the focus somewhere else, it looks like this:\nCustom Cell Styling # For sure, you can create a CellFactory for any column, and in your custom code you can assign whatever selectors that you want and do all kinds of things. But there are good reasons to style the TableCells in a particular column without doing all that. Primarily, that you don\u0026rsquo;t have to create a custom CellFactory to do it.\nAlso, it\u0026rsquo;s probably a good practice to set up a bunch of re-usable custom TableColumns that deal with particular data types that you commonly encounter. For instance, you might want to create a DateTableColumn that handles date display in a standardized way, or a MoneyTableColumn or IntegerTableColumn.\nIn these cases, assign a style class selector to the cells in a TableColumn and all of the cells in that column will get that selector. This includes the TabelColumnHeader as well. You can exclude the TableColumnHeader in your style sheet by specifying your selector as a sub-component of .table-row-cell. This would typically be one of these forms:\n.table-row-cell .my-selector {} .table-view .table-row-cell .my-selector {} .table-view \u0026gt; .virtual-flow \u0026gt; .clipped-container \u0026gt; .sheet \u0026gt; .table-row-cell .my-selector {} .my-table-selector .table-row-cell .my-selector {} You can use it like this:\ncolumns += TableColumn\u0026lt;TableData1, String\u0026gt;(\u0026#34;Column A\u0026#34;).apply { setCellValueFactory { it.value.value1 } styleClass += \u0026#34;a-column\u0026#34; } and\n.table-row-cell \u0026gt; .a-column { -fx-background-color: red; -fx-background-insets: 0 0 1 0px; } Which looks like this:\nIf you don\u0026rsquo;t specify .table-row-cell as the parent, you\u0026rsquo;ll get this:\nWhich is probably not what you want.\nOnce you apply this kind of styling to a TableCell, you\u0026rsquo;ll have to contend with the fact that its non-transparency will mess with the TableRow styling underneath, particularly with selected TableRows:\nOne way to deal with this is to add a custom styling for a-column as a child of a TableRow with the selected Pseudo-class applied:\n.table-row-cell:selected \u0026gt; .a-column { -fx-background-color: #ee000070; } Here, the background colour has had its opacity reduced:\nRow Styling Based on Data Values # TableRow inherits from Cell through IndexedCell, which means that it supports customization just like TableCell. However, while you are not meant to mess about with the layout of TableRow, you can add other custom features like Pseudo-classes and behaviours based on data in the TableRow.\nJust like TableCell, enough TableRows are created to fill the viewport of the TableView and then they are recycled and reloaded with data as the user scrolls through the TableView. The key method here is Cell.updateItem() which you can customize as required. Let\u0026rsquo;s look at how to add a PseudoClass to the TableRow and then link it to an element in the TableRow data:\nclass CustomTableRow : TableRow\u0026lt;TableData1\u0026gt;() { companion object PseudoClasses { val specialPC = PseudoClass.getPseudoClass(\u0026#34;special\u0026#34;) } override fun updateItem(newValue: TableData1?, empty: Boolean) { super.updateItem(newValue, empty) if (empty || newValue == null) { pseudoClassStateChanged(specialPC, false) } else { pseudoClassStateChanged(specialPC, newValue.value4.value) } } } The only customization that we\u0026rsquo;ve done here is to create a PseudoClass as a static member of the CustomTableRow and then used CustomTableRow.updateItem() to call pseudoClassStateChanged() according to the new data being loaded.\nIf we apply the following styling:\n.table-row-cell:special { -fx-background-color: red; } We get the following result:\nWe don\u0026rsquo;t just have to style the entire row. We can apply styling to a specific cell if we\u0026rsquo;ve added a style class selector to its TableColumn by doing this:\n.table-row-cell:special \u0026gt; .a-column { -fx-background-color: red; -fx-background-insets: 0 0 1 0px; } Note that this is exactly the same as the example in the previous section except that we\u0026rsquo;ve applied the special Pseudo-class to .table-row-cell instead of selected:\nHowever, note that we have the same problem as before:\nBut we can fix it by adding a new selector for TableRow with both Pseudo-classes applied and reducing the opacity again:\n.table-row-cell:selected:special \u0026gt; .a-column { -fx-background-color: #ee000070; } And you can see that the rows without the special Pseudo-class continue to work as normal:\nAccessing Specific Column Headers # Instances of TableColumnHeader are not going to be programmatically accessible to you, since they are purely internal to the TableView. The only elements that you can add style class selectors to are the one that you normally access when defining the table. This is mostly TableColumn, TableRow and TableCell.\nFortunately, a TableColumnHeader will automatically be assigned any style class selectors added to their associated TableColumn. So the following code will add the selector, \u0026ldquo;a-column\u0026rdquo; to the TabelColumnHeader associated with this column.\ncolumns += TableColumn\u0026lt;TableData, String\u0026gt;(\u0026#34;Column A\u0026#34;).apply { setCellFactory { CustomTableCell() } setCellValueFactory { it.value.value1 } styleClass += \u0026#34;a-column\u0026#34; } If we try to style this column directly using something like this:\n.a-column .text { -fx-fill: red; } We get this:\nYou can see that it\u0026rsquo;s styled all the Text in the column, and not just the column header. However, if we use the fact that the column header is enclosed in the TableHeaderRow, which has the selector, \u0026ldquo;.column-header-background\u0026rdquo;, then we can isolate the column header:\n.table-view .column-header-background .a-column .text { -fx-fill: red; } Which looks like this:\nNested Column Headers # In TableView all TableColumns are potentially parents of nested columns. The entire header row is actually styled as a single nested column, and then using the TableColumn.getColumns().add() method on any TableColumn will generate another nested column inside the main nested column. Rinse and repeat if you want nested nested columns.\nThe JavaFX terminology is that the parent column gets the term \u0026ldquo;Nested\u0026rdquo; even though the it\u0026rsquo;s the child columns that are nested. The class for the parent column header is NestedTableColumnHeader, and it has a style sheet selector of nested-column-header. Unfortunately, you can\u0026rsquo;t really get to any of the header row components directly through programming without cheating and you have to do your customization through the TableColumn itself.\nThis presents a problem if you want to style the header for different nested columns differently, especially since JavaFX CSS doesn\u0026rsquo;t support sibling selectors. Let\u0026rsquo;s look at this code:\nTableView\u0026lt;TableData\u0026gt;().apply { columns += TableColumn\u0026lt;TableData, String\u0026gt;(\u0026#34;Column A\u0026#34;).apply { setCellFactory { CustomTableCell() } setCellValueFactory { it.value.value1 } } columns += TableColumn\u0026lt;TableData, String\u0026gt;(\u0026#34;B and C\u0026#34;).apply { styleClass += \u0026#34;bc-nested-column\u0026#34; columns += TableColumn\u0026lt;TableData, String\u0026gt;(\u0026#34;Column B\u0026#34;).apply { setCellFactory { CustomTableCell() } setCellValueFactory { it.value.value2 } styleClass += \u0026#34;bc-column\u0026#34; } columns += TableColumn\u0026lt;TableData, String\u0026gt;(\u0026#34;Column C\u0026#34;).apply { setCellFactory { CustomTableCell() } setCellValueFactory { it.value.value3 } styleClass += \u0026#34;bc-column\u0026#34; } } } Columns \u0026ldquo;B\u0026rdquo; and \u0026ldquo;C\u0026rdquo; are inside a nested column with the title \u0026ldquo;B and C\u0026rdquo;. The nested column gets the selector bc-nested-column and the two inner columns get the selector bc-column. Since we don\u0026rsquo;t have sibling combinators to allow us to use .nested-column-header ~ bc-nested-column as a selector, our options for styling are limited. However, we can do this:\n.root { -wfx-header-background: linear-gradient(to right, #1dbbdd, #9310b9) } .table-view .column-header-background { -fx-background-color: -wfx-header-background; } .table-view .column-header { -fx-background-color: transparent; } .filler { -fx-background-color: transparent; } .nested-column-header { -fx-background-color: -wfx-header-background; -wfx-header-background: transparent; } .bc-nested-column { -wfx-header-background: linear-gradient(to bottom, #0dffee, #9310b9); } Here, we have a custom colour defined as a blue to purple gradient to the right in the .root section. The gradients are ugly, but they make it easy to see the continuity of the background behind other elements. Then we set the global header background to this colour. Then we set all of the column headers to transparent, including the filler column at the far right. Next, we set -fx-background-color for the nested column headers to our custom colour and redefine it to be transparent in this context. Finally, for our nested \u0026ldquo;B and C\u0026rdquo; column, we redefine our custom colour to be the same gradient, but going down instead of to the right.\nIt looks like this:\nThe biggest issue here is the filler column at the right. This is defined outside the nested column header that covers all of the other parts of the header row. This means that we have to apply the custom colour to the header background, and then make the main nested column header transparent. If we get rid of the filler by setting a constrained ColumnResizePolicy, and then adding a fourth column just to have something at the right, then we can simplify the style sheet:\n.root { -wfx-header-background: linear-gradient(to right, #1dbbdd, #9310b9) } .table-view .column-header { -fx-background-color: transparent; } .nested-column-header { -fx-background-color: -wfx-header-background; } .bc-nested-column { -wfx-header-background: linear-gradient(to bottom, #0dffee, #9310b9); } Which looks like this:\nNow we don\u0026rsquo;t need to worry about the header background or the filler, and we can set the initial gradient on the main nested column header.\nUnfortunately, JavaFX only supports name elements for colours, so we cannot use this technique for other attributes, like background radii or insets.\nAlternate Approaches # We can use the fact that the main wrapper class for the header row is a NestedTableColumnHeader and specify further children of that selector:\n.nested-column-header { -fx-background-color: linear-gradient(to right, red, blue); } .nested-column-header \u0026gt; .nested-column-header { -fx-background-color: linear-gradient(to bottom, lightgreen, pink); -fx-background-radius: 10 10 3 3px; } .column-header { -fx-background-color: transparent; } Which looks like this:\nAs you can see, this would allow you to specify more attributes than just colour - like background radius in this case. However, you still cannot style siblings differently. It is good if you want to style the wrapper columns for all nested columns the same.\nFinally, you can also assign a style class to the TableColumn that corresponds to the NestedTableColumnHeader and use the technique outlined in the \u0026ldquo;Accessing Specific Column Headers\u0026rdquo; section above. That technique works just as well for NestedTableColumnHeaders. The following style sheet:\n.nested-column-header { -fx-background-color: linear-gradient(to right, red, blue); } .bc-nested-column { -fx-background-color: linear-gradient(to bottom, lightgreen, pink); -fx-background-radius: 10 10 3 3px; } .column-header { -fx-background-color: transparent; } Also yields:\nStyled Components of TableView # This section contains as complete a listing as I could compose of the styleable elements of TableView. It\u0026rsquo;s organized top-down, with subsections for components that are contained within other components. This means that the table of contents for this section should let you see at a hierarchy of the various styled components.\nFor each entry, I\u0026rsquo;ve given the individual style class selector, the actual class of the element, a description of what it is and some hints about how to use it, and then a complete listing of every entry in Modena that has that selector as the final child element. When it seemed appropriate, I\u0026rsquo;ve also included images that highlight the component in action.\nAdditionally, I\u0026rsquo;ve included Pseudo-classes that are supported by each component that aren\u0026rsquo;t documented somewhere else.\nAt the top of the hierarchy is TableView itself, which has a single selector:\nCSS Selector: table-view Type: StackPane Description: This is the TableView itself. It supports a number of Pseudo-classes that are also used as selectors for child elements. Modena: .table-view\n.table-view:disabled\n.table-view:focused Pseudo-classes constrained-resize\nunconstrained-resize\ncell-selection\nrow-selection Note that there are two entries for .table-view in Modena. One, in the section called \u0026ldquo;Box-like Things\u0026rdquo; sets styling like background colours and insets. The other, in the section called \u0026ldquo;TableView\u0026rdquo; defines some colours.\nTableHeaderRow # This is the wrapper class for the entire header row.\nCSS Selector: column-header-background Type: StackPane Description: This is the area at the top of the TableView that holds the column headings and the related controls. Don\u0026rsquo;t let \u0026ldquo;background\u0026rdquo; confuse you, this is the entire header row. Modena: .table-view .column-header-background Root NestedTableColumnHeader # Every TableColumnHeader can act as a NestedTableColumnHeader by adding TableColumns to its associated TableColumn. Technically, every TableColumnHeader is the child of a NestedTableColumnHeader because the TableHeaderRow contains a single, main, NestedTableColumnHeader to which all of the other TableColumnHeaders (or NestedTableColumnHeaders) are added.\nIt\u0026rsquo;s impossible to add style class selectors to the top-level NestedTableColumnHeader because it\u0026rsquo;s not exposed programmatically, and all NestedTableColumnHeaders have the some selector. You can access this specific, top-level, NestedTableColumnHeader via: .table-view .column-header-background \u0026gt; .nested-column-header.\nOther NestedTableColumnHeaders can be given custom style class selectors by adding one to the corresponding TableColumn.\nCSS Selector: nested-column-header Type: StackPane Description: This is the master NestedTableColumnHeader that serves as the container for all of the other TableColumnHeaders and NestedTableColumnHeaders that are defined in the TableView. Modena: None You can access the other NestedTableColumnHeaders via: .table-view .column-header-background \u0026gt; .nested-column-header .nested-column-header.\nTableColumnHeader # For each NestedTableColumnHeader in the TableView there will be some number of TableColumnHeaders that have the following CSS structure:\nCSS Selector: column-header Type: TableColumnHeader (extends Region) Description: The header for a TableColumn. This inherits any style classes added to its associated TableColumn Modena: .table-view .column-header This also supports the last-visible Pseudo-class.\nAdditionally, the column header inherits any style classes applied to its associated TableColumn. Initially, this is just table-column:\nCSS Selector: table-column Type: TableColumnHeader (extends Region) Description: This is the standard inherited styleclass from the TableColumn. This is also shared with all of the TableCells in the column. Modena: None This also supports the last-visible Pseudo-class.\nColumn Label # TableColumnHeader holds a Label to hold the column title:\nCSS Selector: label Type: Label Description: This is just a standard Label Modena: .table-view .column-header .label CSS Selector: text Type: LabeledText Description: This is the Text element inside the Label in the column header. Modena: None Column Context Menu # CSS Selector: context-menu Type: ContextMenu Description: If a ContextMenu has been established for the related TableColumn then a right click in the TableColumnHeader will invoke the context menu. This is the default selector for this. However, since the ContextMenu is established in the application code, it is possible to style it directly from the application code. This will also have all of the styling elements of a ContextMenu. Modena: None Column Sort Indicators # TableColumns can be sorted by clicking on the column headers. Secondary and tertiary sort columns can be specified. The TableColumns contain a GridPane holding an arrow, sort number dots and pontentially a Label if more than 3 columns are selected for sorting. The elements in the GridPane are moved around programmatically depending on a number of factors and this connot be controlled via CSS.\nCSS Selector: arrow Type: Region Description: In Modena, this is an SVG shape of a triangle pointed down. If the column sort is ascending, then it is rotated 180 degrees. Modena: table-view .arrow CSS Selector: sort-order Type: Label Description: If there are more than 3 columns with sorting applied, then the fourth and later columns will show a number beside the arrow instead of a series of dots above/below the arrow. Otherwise this element is not visible. Modena: .table-view .column-header .sort-order CSS Selector: sort-order-dots-container Type: HBox Description: This is just an HBox that holds the sort order dots. Modena: .table-view .column-header .sort-order-dots-container The next two selectors are applied to the actual \u0026ldquo;dots\u0026rdquo;:\nCSS Selector: sort-order-dot Type: Region Description: This is just a small Region with a background colour and curved corners that looks like a \u0026ldquo;dot\u0026rdquo;. Modena: .table-view .column-header .sort-order-dot CSS Selector: ascending or descending Type: Region Description: You can style the dots differently for ascending vs decsending sort orders. These selectors are not used in Modena itself. Modena: None Column Drag # In case you didn\u0026rsquo;t know, you can drag the column headings around to change their order in the TableView. These elements style the drag action:\nCSS Selector: column-drag-header Type: StackPane Description: When a column is being \u0026lsquo;dragged\u0026rsquo; to be placed in a different position, this is a region that follows along the column header area to indicate where the column will be dropped. Modena: .table-view .column-drag-header CSS Selector: column-overlay Type: StackPane Description: This semi-transparent overlay to indicate the column that is currently being moved. Modena: .table-view .column-overlay CSS Selector: column-resize-line Type: Region Description: This is the line that runs down between the columns to indicate where a column will be placed when it is dropped. Oddly, resizing a column does not make this line appear. Modena: .table-view .column-resize-line Filler Column # CSS Selector: filler Type: Region Description: If you haven\u0026rsquo;t set the TableView.columnResizePolicyProperty() to one of the \u0026ldquo;constrained\u0026rdquo; policies, then there might be a phantom column at the far rght of your table. This element is the column header for that last, phantom column. Modena: .table-view .filler Show/Hide Column Button # CSS Selector: show-hide-columns-button Type: Pane Description: The space at the top right corner, above the vertical scrollbar is a \u0026ldquo;Button\u0026rdquo; (but not a Button) that opens a menu allowing you to show or hide various columns. By default this is not visible, but can be turned on. This is the styling for this \u0026ldquo;Button\u0026rdquo;. Modena: .table-view \u0026gt; .column-header-background \u0026gt; .show-hide-columns-button CSS Selector: show-hide-column-image Type: StackPane Description: This is the \u0026ldquo;+\u0026rdquo; that appears in the column show/hide Button. It is a StackPane with an SVG shape applied. Modena: .table-view .show-hide-column-image CSS Selector: context-menu Type: ContextMenu Description: When you click on the \u0026ldquo;button\u0026rdquo; this is the context menu appears showing all of the table columns and allowing you to pick which ones to show or hide. This will also have all of the styling elements of a ContextMenu. Modena: .table-view \u0026gt; .column-header-background \u0026gt; .show-hide-columns-button .context-menu VirtualFlow # TableView is one of the controls that uses VirtualFlow which enables it to have a descrete set of screen Nodes to support a limitless number of elements in a List. This set of Nodes is recycled as they scroll on/off the viewport of the TableView.\nCSS Selector: virtual-flow Type: VirtualFlow (extends Region) Description: VirtualFlow is the container for all of the TableView elements except those in the TableHeaderRow. This includes the rows and columns and the scrollbars. Modena: No direct uses, but it is specified as the parent of other elements. ClippedContainer # CSS Selector: clipped-container Type: VirtualFlow.ClippedContainer (extends Region) Description: This is simple just a Region with a clipping rectangle specified. It acts as a wrapper around sheet to limit the visibility of the contents of the TableView so that it will fit within the space allocated. Modena: No direct uses, but it is specified as the parent of other elements. ClippedContainer -\u0026gt; Sheet # CSS Selector: sheet Type: Group Description: This is the element that contains all of the individual TableCells and TableRows. Modena: No direct uses, but it is specified as the parent of other elements. TableRow # TableRow is itself a Cell and it has three selectors. Since you can specify a RowFactory for a TableView it is possible to apply your own selectors to a customized TableRow. However, if you just want to change the styling of the TableRow without creating a RowFactory, you can use these selectors.\nCSS Selector: cell Type: TableRow Description: This selector is applied to all of the Cell subclass elements in the TableView including the individual TableCells, so any styling here will also apply to all of the TableCells. This is inherited from the standard implementation of Cell Modena: No direct uses, but it is specified as the parent of other elements. CSS Selector: indexed-cell Type: TableRow Description: This selector is applied to all of the Cell subclass elements in the TableView including the individual TableCells, so any styling here will also apply to all of the TableCells. This is inherited from the standard implementation of IndexedCell. Modena: None CSS Selector: table-row-cell Type: TableRow Description: This is the only selector which is specific to TableRow. Use this for styling that you don\u0026rsquo;t want applied to individual TableCells. Modena: .table-row-cell\n.table-row-cell:odd\n.table-view:focused \u0026gt; .virtual-flow \u0026gt; .clipped-container \u0026gt; .sheet \u0026gt; .table-row-cell:filled:selected\n.table-row-cell:filled:selected\n.table-view:focused:row-selection \u0026gt; .virtual-flow \u0026gt; .clipped-container \u0026gt; .sheet \u0026gt; .table-row-cell:focused TableColumn # While you do create TableColumns and add them to the TableView, TableColumn doesn\u0026rsquo;t appear as an actual element in the skin of TableView. Instead it is used to create TableCells and the TableColumnHeaders, both of which will inherit any style sheet selectors applied to the related TableView. As a default, all TableColumns have the following selector applied:\nCSS Selector: table-column Type: Various Description: The default style sheet selector added to all elements that derive from TableColumn. Modena: None TableCell # Since you can specify a CellFactory for a TableColumn it is possible to add a custom style class selector to your custom TableCells. However, if you wish to style all of the TableCells in a TableView, then you an use one of the selectors listed here.\nCSS Selector: cell Type: TableCell Description: This selector is applied to all of the Cell subclass elements in the TableView including the individual TableRows, so any styling here will also apply to all of the TableRows. This is inherited from the standard implementation of Cell. Modena: No direct uses, but it is specified as the parent of other elements. CSS Selector: indexed-cell Type: TableCell Description: This selector is applied to all of the IndexedCell subclass elements in the TableView including the individual TableRows, so any styling here will also apply to all of the TableRows. This is inherited from the standard implementation of IndexedCell. Modena: None CSS Selector: table-cell Type: TableCell Description: This is the sole standard style class selector related only to TableCell and comes from the standard implementation of TableCell. Modena: .table-cell\n.table-cell:selected:disabled\n.table-row-cell:filled \u0026gt; .table-cell:selected\n.table-view:focused:cell-selection \u0026gt; .virtual-flow \u0026gt; .clipped-container \u0026gt; .sheet \u0026gt; .table-row-cell \u0026gt; .table-cell:focused\n.table-view \u0026gt; .virtual-flow \u0026gt; .clipped-container \u0026gt; .sheet \u0026gt; .table-row-cell .table-cell:selected\n.table-view:constrained-resize \u0026gt; .virtual-flow \u0026gt; .clipped-container \u0026gt; .sheet \u0026gt; .table-row-cell \u0026gt; .table-cell:last-visible CSS Selector: table-column Type: TableCell Description: This is the standard inherited styleclass from the TableColumn. This is also shared with the TableColumnHeader for the column. Modena: None CSS Selector: text Type: LabeledText Description: Since TableCell inherits from Labeled, it also inherits this standard style class selector from Labeled that is associated with the LabeledText element in Labeled. Modena: .text ScrollBars # The scrollbars in TableView are an instance of VirtualScrollBar which extends ScrollBar and is intended to work with VirtualFlow. ScrollBar itself is skinned, also. All of the selectors come from the ScrollBar class itself, and there\u0026rsquo;s nothing added from the TableView.\nThe standard ScrollBar has and arrow \u0026ldquo;Button\u0026rdquo; at each end of a track that has an indicator called a \u0026ldquo;thumb\u0026rdquo; that runs up and down it.\nThe orientation of a ScrollBar is indicated by a Pseudo-classes applied to the ScrollBar itself and will be either horizontal or vertical.\nCSS Selector: scroll-bar Type: VirtualScrollBar Description: This is the selector for the entire ScrollBar. The horizontal and vertical Pseudo-classes are applied to this selector. Modena: .table-view \u0026gt; .virtual-flow \u0026gt; .scroll-bar:vertical\n.table-view \u0026gt; .virtual-flow \u0026gt; .scroll-bar:horizontal CSS Selector: track-background Type: StackPane Description: The track background covers the entire space of the ScrollBar minus any padding set on the ScrollBar itself. All of the other components sit on top of the track background. Modena: None CSS Selector: track Type: StackPane Description: The track is the space between the end buttons that the thumb runs up and down. Modena: None CSS Selector: increment-button Type: Region Description: This is one of the two end \u0026ldquo;Buttons\u0026rdquo; in the ScrollBar, and is at the bottom of a vertical ScrollBar and the right of a horizontal ScrollBar. Not this this is not an actual Button but just a Region. Modena: .scroll-bar \u0026gt; .increment-button\n.scroll-bar:horizontal \u0026gt; .increment-button\n.scroll-bar:vertical \u0026gt; .increment-button CSS Selector: increment-arrow Type: Region Description: This is the actual \u0026ldquo;arrow\u0026rdquo; that is drawn in the increment \u0026ldquo;Button\u0026rdquo;. In Modena, it is styled by appling an SVG shape to the Region. Modena: .scroll-bar \u0026gt; .increment-button \u0026gt; .increment-arrow\n.scroll-bar \u0026gt; .increment-button:hover \u0026gt; .increment-arrow\n.scroll-bar \u0026gt; .increment-button:pressed \u0026gt; .increment-arrow\n.scroll-bar:horizontal \u0026gt; .increment-button \u0026gt; .increment-arrow .scroll-bar:vertical \u0026gt; .increment-button \u0026gt; .increment-arrow CSS Selector: decrement-button Type: Region Description: This is one of the two end \u0026ldquo;Buttons\u0026rdquo; in the ScrollBar, and is at the top of a vertical ScrollBar and the left of a horizontal ScrollBar. Not this this is not an actual Button but just a Region. Modena: .scroll-bar \u0026gt; .decrement-button\n.scroll-bar:horizontal \u0026gt; .decrement-button\n.scroll-bar:vertical \u0026gt; .decrement-button CSS Selector: decrement-arrow Type: Region Description: This is the actual \u0026ldquo;arrow\u0026rdquo; that is drawn in the decrement \u0026ldquo;Button\u0026rdquo;. In Modena, it is styled by appling an SVG shape to the Region. Modena: .scroll-bar \u0026gt; .decrement-button \u0026gt; .decrement-arrow .scroll-bar \u0026gt; .decrement-button:hover \u0026gt; .decrement-arrow\n.scroll-bar \u0026gt; .decrement-button:pressed \u0026gt; .decrement-arrow .scroll-bar:horizontal \u0026gt; .decrement-button \u0026gt; .decrement-arrow\n.scroll-bar:vertical \u0026gt; .decrement-button \u0026gt; .decrement-arrow CSS Selector: thumb Type: StackPane Description: This is the indicator/control that sits in the track and slides along the track as the ScrollBar scrolls. Modena: .scroll-bar \u0026gt; .thumb .scroll-bar:vertical \u0026gt; .thumb\n.scroll-bar \u0026gt; .thumb:hover\n.scroll-bar \u0026gt; .thumb:pressed Corner # CSS Selector: corner Type: StackPane Description: This is the little square that appears at the bottom right corner where the two scrollbars meet, if they are both visible. Modena: .table-view \u0026gt; .virtual-flow \u0026gt; .corner PlaceHolder # There is a Node that will be displayed if the TableView is empty which is called \u0026ldquo;placeholder\u0026rdquo;. TableView has a Property called placeholder that you can use to set your own, custom, Node and set your own style class selectors. In the event that you use the default placeholder, which is a StackPane holding a Label, these are the selectors:\nCSS Selector: placeholder Type: StackPane Description: When the TableView is empty, or if it contains no column, then a \u0026ldquo;placeholder\u0026rdquo; StackPane containing a Label will be shown instead of the usual rows and columns. Modena: None CSS Selector: empty-table Type: Unknown Description: This selector appears in Modena, but there is no source code which assigns it to any components. It is possible that it is left over from an earlier version and has no current usages. Modena: .table-view .empty-table CSS Selector: label Type: Label Description: This is the selector inherited from the standard implementation of Label. Modena: None Conclusion # If you are interested in doing this kind of investigation into other JavaFX controls, these are the techniques that I used:\nNode.lookupAll(\u0026quot;*\u0026quot;) This command will give you a list of all of the elements with style class selectors in the Node to which it is applied.\nHowever, it will not show items that are not on the SceneGraph, and will not show parent -\u0026gt; child relationships. So further investigation will almost always be required. It is a good start to get a handle on the scope of the elements involved.\nSearching the Source Code At some point you\u0026rsquo;ll need to look at the source code to discover how the elements are related to each other and whether there are additional style class selectors that you didn\u0026rsquo;t see with Node.lookupAll(\u0026quot;*\u0026quot;) - and there will be some of those.\nA good starting point is to search through the source code for \u0026ldquo;getStyleClass\u0026rdquo;, or just \u0026ldquo;StyleClass\u0026rdquo; because that is the only way to add style class selectors.\nRemember that some selectors are created at the \u0026ldquo;control\u0026rdquo; level, and more might be added in the skin. Many components of TableView are themselves skinned.\nFinally, you need to find the components, and the components of the components. Look for Node subclasses that are declared as fields. Search for \u0026ldquo;getChildren\u0026rdquo; and \u0026ldquo;layoutChildren\u0026rdquo; in the source code.\nSearching Through Modena After all of that, you still might/probably have missed something. Search through Modena for the style class selectors that you have found and pay attention to any parent or child elements listed in the selectors. Also, look for Pseudo-classes that are used and then go back to the source code to find out how they are used.\nI remain convinced that all of this sleuthing is something that should be completely unnecessary, and it should all be readily available in the documentation for the JavaFX library. After all, all of this part of the public API and, as such, should be documented.\nFor instance the TableView Pseudo-class unconstrained-resize is defined in TableView but isn\u0026rsquo;t used anywhere in Modena and won\u0026rsquo;t appear in Node.lookupAll(\u0026quot;*\u0026quot;) since it is a Pseudo-class. The only way to find it is to infer its existence from Modena\u0026rsquo;s use of constrained-resize and then go looking for it in the source code. Even then it\u0026rsquo;s not directly used, but comes from the toString() of the UNCONSTRAINED_RESIZE_POLICY Callback. And if you are looking to see if constrained-resize is a special case of one of the other policies (it isn\u0026rsquo;t) you need to drill down into ConstrainedColumnResizeBase to find its toString() method.\nWhat can you use these Pseudo-class for? In Modena, constrained-resize is used turn the right-hand side border of the last visible column\u0026rsquo;s TableCells transparent. With an unconstrained policy, you would have a filler column to the right of the last visible column, and you would need to see that border.\nRegardless of that, hopefully you will find this tutorial and reference helpful in styling your own TableViews.\n","date":"8 December 2024","externalUrl":null,"permalink":"/javafx/elements/nodes/tableview/styling-guide/","section":"JavaFX","summary":"A comprehensive guide to styling TableView with tutorials and a reference of all of the styling selectors available.","title":"JavaFX Styling Guide: TableView","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # How to pick a suitable scope Structure of a State object - the \u0026ldquo;Model\u0026rdquo; Using builder methods for clarity The Single Responsibility Principle Returning \u0026ldquo;generic\u0026rdquo; objects Avoid passing Events between classes Implementing Create - Selecting Scope # The first feature that we\u0026rsquo;re going to implement is a minimal \u0026ldquo;Create\u0026rdquo; function. While I would really, really like to build a single field version, I can\u0026rsquo;t think of any way that it would be enough to actually test. So we\u0026rsquo;re going to use two fields: Account Number and Customer Name.\nIn this part, we\u0026rsquo;re going to concentrate on the GUI side of the feature then, in part 7, we\u0026rsquo;ll look at the business logic and persistence side of the application.\nThe next question is how \u0026ldquo;wide\u0026rdquo; do we make our feature scope? There\u0026rsquo;s no \u0026ldquo;right\u0026rdquo; answer here, but my preference is to make every feature as small as possible. In fact, there\u0026rsquo;s no such thing as \u0026ldquo;too small\u0026rdquo; when it comes to features. But just remember, every feature has to cut through the application from GUI Node right down to the database. So it\u0026rsquo;s really, really easy to end up with features that are too big.\nI like to start with bare-bones functionality. Something that works, but isn\u0026rsquo;t polished. Then add the error checking, GUI flash and convenience, and things like that as additional features in the next steps. So that\u0026rsquo;s what we\u0026rsquo;ll do here.\nOur scope is going to be a simple screen, and we\u0026rsquo;re not going to worry too much about formatting and appearance just yet. For a first cut, this is probably best. Then when we\u0026rsquo;re happy that the plumbing is good, we can go back and sort things out so that it looks nice, handles errors and checks to make sure that things are good before saving.\nThe best way to understand this is to look at how it\u0026rsquo;s done\u0026hellip;\nThe Model # The first step is to build out our Model with the Properties for the data that we\u0026rsquo;re adding:\npublic class CustomerModel { private final StringProperty accountNumber = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty customerName = new SimpleStringProperty(\u0026#34;\u0026#34;); public String getAccountNumber() { return accountNumber.get(); } public StringProperty accountNumberProperty() { return accountNumber; } public void setAccountNumber(String accountNumber) { this.accountNumber.set(accountNumber); } public String getCustomerName() { return customerName.get(); } public StringProperty customerNameProperty() { return customerName; } public void setCustomerName(String customerName) { this.customerName.set(customerName); } } This is just a POJO (Plain Old Java Object) with Properties as fields. Each field is configured as a JavaFX \u0026ldquo;Bean\u0026rdquo;, with a getter that delegates to Property.get(), a setter that delegates to Property.set(), and a \u0026ldquo;Property Getter\u0026rdquo; that retrieves the actual Property.\nIn this case, both the account number and customer name fields are StringProperty. Note that the fields are final, which means that their references can never be changed. The contents of the Properties can change - that\u0026rsquo;s the point of Properties - but once some other class has obtained a reference to one of these Properties it will always be valid.\nThe ViewBuider # Now, let\u0026rsquo;s take a look at the ViewBuider, which is, naturally, the most complicated part of this section since we\u0026rsquo;re concentrating on the GUI:\npublic class CustomerViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final CustomerModel model; private final Runnable saveHandler; public CustomerViewBuilder(CustomerModel model, Runnable saveHandler) { this.model = model; this.saveHandler = saveHandler; } @Override public Region build() { BorderPane results = new BorderPane(); results.getStylesheets().add(Objects.requireNonNull(this.getClass().getResource(\u0026#34;/css/customer.css\u0026#34;)).toExternalForm()); results.setTop(headingLabel(\u0026#34;Customer Information\u0026#34;)); results.setCenter(createCentre()); results.setBottom(createButtons()); return results; } private Node createCentre() { VBox results = new VBox(6, accountBox(), nameBox()); results.setPadding(new Insets(20)); return results; } private Node accountBox() { return new HBox(6,promptLabel(\u0026#34;Account #:\u0026#34;), boundTextField(model.accountNumberProperty())); } private Node nameBox() { return new HBox(6, promptLabel(\u0026#34;Name:\u0026#34;), boundTextField(model.customerNameProperty())); } private Node createButtons() { Button saveButton = new Button(\u0026#34;Save\u0026#34;); saveButton.setOnAction(evt -\u0026gt; saveHandler.run()); HBox results = new HBox(10, saveButton); results.setAlignment(Pos.CENTER_RIGHT); return results; } private Node boundTextField(StringProperty boundProperty) { TextField textField = new TextField(); textField.textProperty().bindBidirectional(boundProperty); return textField; } private Node promptLabel(String contents) { return styledLabel(contents, \u0026#34;prompt-label\u0026#34;); } private Node headingLabel(String contents) { return styledLabel(contents, \u0026#34;heading-label\u0026#34;); } private Node styledLabel(String contents, String styleClass) { Label label = new Label(contents); label.getStyleClass().add(styleClass); return label; } } In the constructor, we\u0026rsquo;ve added a new parameter, a Runnable called saveHandler.\nNext, we\u0026rsquo;ve changed our layout class from VBox to BorderPane. BorderPane is a great general-purpose layout class for screens like this. It\u0026rsquo;s divided up like so:\nYou can put a single Node into any of these areas. If you want to put more than one thing into an area, then you have to put it into a wrapper layout class, which can be anything you like, including another BorderPane. In our case, for Center and Bottom, we\u0026rsquo;ve used VBox and HBox respectively.\nThe build() method code is very simple. Instantiate a BorderPane then call some builder methods to populate Top, Bottom and Center.\nTop # In Top we\u0026rsquo;ve put a heading, which is just a styled Label. Let\u0026rsquo;s take a look at how this is done:\nThere are two private methods that return Labels that are called from the layout code. These are named promptLabel() and headingLabel(). Both of these methods delegate to a single method called styledLabel().\nWhat\u0026rsquo;s with this?\nAs we\u0026rsquo;ve seen before, we\u0026rsquo;re following DRY by putting the styling code for Label into a builder method so that we\u0026rsquo;re not repeating a pattern in our layout code. In my opinion, making a call to promptLabel() is a little bit easier to read in the layout code than calling styledLabel(\u0026quot;some text\u0026quot;, \u0026quot;prompt-label\u0026quot;). There also the argument that repeatedly calling styledLabel() with the same styleClass value is a violation of DRY. Think of how much refactoring you would need if you decided to change the css selector from \u0026ldquo;prompt-label\u0026rdquo; to something else.\nBottom # The Bottom is populated with an HBox containing a single Button, and the HBox is aligned CENTER_RIGHT. This tucks the Button over to the very right side of the screen, which is where people generally expect to find such buttons.\nLet\u0026rsquo;s look at this Button. It\u0026rsquo;s treated as a simple trigger, and it just invokes the Runnable called saveHandler.\nWhy do it this way?\nIt\u0026rsquo;s best to think of EventHandlers as local values. As local as possible. In this case, we\u0026rsquo;ve made it extremely local, as a lambda defined in the Button.setOnAction() method call. The reason for this is that EventHandlers are all context defined. There are different classes of EventHandlers for Button actions, for mouse clicks and movement and for keyboard actions, just to name a few. As soon as you start passing EventHandlers around, you start to restrict the context in which those handlers can be used.\nIn this case, our Button needs to eventually trigger some code in our Interactor (without knowing about the Interactor), so the actual handler needs to be defined by the Controller. If we passed in, say, a Mouse Click EventHandler, then we\u0026rsquo;d have to monkey around in our ViewBuilder code to deal with mouse clicks, which is totally inappropriate for a Button. So the best way to pass the handler to the ViewBuilder is as some kind of functional interface, like a Runnable or a Consumer. This way, the Controller doesn\u0026rsquo;t need to know anything about how the handler is actually invoked, and the ViewBuilder doesn\u0026rsquo;t need to know anything about what the handler does.\nCenter # Here we just have a VBox containing two HBoxes. Each HBox contains a prompt Label, and a TextField. The TextFields are each bound bidirectionally to the appropriate Property in the Model. Bidirectional binding means that either value can be changed, and the the other value will automatically follow along with it. So if we programmatically change the value in model.customerNameProperty(), then we\u0026rsquo;ll see the value in the TextField change instantly. And if we type in the TextField, then the Property in the Model will instantly change to the same value.\nReturn Values of Builder Methods # Notice that all of the private builder methods in this class return Node. This is deliberate. The idea is that all of the code which needs to deal with the inner workings of the Node that it is building - in other words, the code that needs to know specifically what class is being built - belongs in the builder method itself. Once it\u0026rsquo;s done, then there\u0026rsquo;s no need for the calling program to know exactly what kind of Node was returned.\nIt\u0026rsquo;s possible that the calling method may need to understand that the returned Node has height and width that might need to be manipulated to fit into the layout property. In this case, return a Region instead of a Node.\nThe other benefit to this is that if the code needs to be refactored later on, and the class of the return Node changes, it won\u0026rsquo;t require any refactoring of the calling method. This is all about reducing coupling.\nSingle Responsibility Principle # One of the other key principles that\u0026rsquo;s been followed with this code is the \u0026ldquo;Single Responsibility Principle\u0026rdquo;. It states that any method should only be directly responsible for a single task.\nThe benefits of this are seen with createCentre(). It\u0026rsquo;s directly responsible for creating the VBox which holds the content of the centre. Then it delegates the details of the two rows inside it to builder methods. Because the builder methods have clear names, it\u0026rsquo;s now trivial to look at createCentre() and see exactly what it does, and where to look if you need more information about something.\nAnd that concept flows through the entire structure of this class. You can look at build() and because it\u0026rsquo;s very small you can see immediately that it creates a BorderPane, and that the heading is in the top and the buttons are in the bottom. Maybe createCentre() could be better named to something like createDataEntry(), or createBody() but it\u0026rsquo;s usually fairly common that the meat of the screen goes into the centre of a BorderPane.\nThe Stylesheet # There\u0026rsquo;s not much to this stylesheet yet. Just some colours and some formatting for our two types of Labels:\n.root { prompt-colour: #a04000; heading-colour: #2090c0; } .heading-label { -fx-text-fill: heading-colour; -fx-font-size: 32px; } .prompt-label { -fx-text-fill: prompt-colour; -fx-font-size: 16px; -fx-font-weight: bold; } The Controller # The Controller needs to be expanded to define the action handler for save. We\u0026rsquo;re not going to do much yet, just call a method in the Interactor to handle the actual \u0026ldquo;Save\u0026rdquo; function:\npublic class CustomerController { private final Builder\u0026lt;Region\u0026gt; viewBuilder; private final CustomerInteractor interactor; public CustomerController() { CustomerModel model = new CustomerModel(); interactor = new CustomerInteractor(model); viewBuilder = new CustomerViewBuilder(model,interactor::saveCustomer); } public Region getView() { return viewBuilder.build(); } } Notable here is the fact that we\u0026rsquo;ve added a dependency between the Controller and the Interactor, as the Interactor now has to have a public method called saveCustomer().\nThe Interactor # While we\u0026rsquo;re not doing much, and certainly not adding the persistence component just yet, we want to make sure that all of the plumbing that we\u0026rsquo;re building is working. So the Interactor has to do something. In this case, we\u0026rsquo;ll just have some console output.\npublic class CustomerInteractor { private CustomerModel model; public CustomerInteractor(CustomerModel model) { this.model = model; } public void saveCustomer() { System.out.println(\u0026#34;Saving account: \u0026#34; + model.getAccountNumber() + \u0026#34; Name: \u0026#34; + model.getCustomerName()); } } Note Now, one thing that you really should make note of is that exactly zero data is sent from the View through the Controller to the Interactor as part of the action. Everything that the Interactor needs is already in the Model from the bidirectional bindings on the two TextFields.\nIt Runs! # Here it is:\nAnd here\u0026rsquo;s the console output:\n\u0026gt; Task :run Saving account: 1234 Name: Fred As expected, it\u0026rsquo;s not pretty but it does work and contains all of the groundwork to connect the GUI to the business logic. Now we\u0026rsquo;re ready for the next step\u0026hellip;\n","externalUrl":null,"permalink":"/beginners/part6a/","section":"JavaFX","summary":"The first feature for our CRUD application has to be “Create”.  In this lesson we’ll get started by designing the GUI and connecting it to the Controller.","title":"Part 6A - First Feature: Minimal Create","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # How to build a layered structure for the back-end of the application. How to simulate a database for development/testing The purpose of a Data Access Object The purpose of a Broker How Domain Objects and the GUI Model interact What you can and should test The Back-End of the Framework # Overall you can view MVCI with this diagram:\nWhat we are going to concentrate on here is the Interactor, Domain and Domain Objects. Let\u0026rsquo;s break this down a little bit more, and look at how it fits in with the whole application:\nA typical application will have some quantity of MVCI frameworks, typically one for each type of function in the application. For instance, you might have one for Customer CRUD operations, one for Inventory management, another for Order management, and so on. Each of these has its own business logic that is unique to its function, and this business logic goes into its Interactor.\nUnderneath all of this is a layer of business logic that is NOT unique to a specific function or MVCI framework, and that is (or potentially could be) shared with other functions and MVCI frameworks in the application. This includes things like retrieving data and saving data in databases or external applications.\nHow you integrate these components with MVCI is what we are going to look at in this article.\nJump down to the very bottom of the diagram. All those icons represent different kinds of external systems that we might need to access. This could be a database server, some kind of web service, a local file system or some kind of a proprietary system with an API or a library that we can use. We need to isolate these from our MVCI framework as much as possible.\nThe lowest level of our application is the \u0026ldquo;Data Access Objects\u0026rdquo;. These are the classes that implement the API or library that allows them to talk to an external system. They understand the data formats that they need to use to transfer information to and from theses systems. They are also the ONLY elements in our system that even know about the existence of these external systems.\nThe next level up is the \u0026ldquo;Broker\u0026rdquo; and \u0026ldquo;Service\u0026rdquo; level. These are classes that know about the Data Access Objects and how to talk to them. They transfer data to and from the DAO level using \u0026ldquo;Data Transfer Objects\u0026rdquo; or \u0026ldquo;DTO\u0026rsquo;s\u0026rdquo;. DTO\u0026rsquo;s are pure data. They can be any format you like, but they are disconnected from the format that comes from the external application.\nThis structure is all about isolation.\nYou do NOT want to have to rewrite a Broker because the data format from the external application changed! The DAO handles that, and then it continues to send data on to the Broker/Service level without changing the structure of the DTO\u0026rsquo;s it uses to communicate to them.\nThe Brokers and Services can perform any number of functions, and they may need to call multiple DAO\u0026rsquo;s in order to complete their tasks. They pass data up to the Interactors via \u0026ldquo;Domain Objects\u0026rdquo;.\nDomain Objects are true business objects, not just data. As such, they can do anything you want and contain internal logic that can be called from any other business logic. For instance Customer might be a Domain Object and it might internally have fields for first name, last name and title. It might have a method called getSalutation() which combines the title with the last name and a string, like, \u0026ldquo;Dear Mr. Smith\u0026rdquo;. Things like that.\nFinally, the Interactor knows about Domain Objects and it knows how to get data from them and the methods that it can call and how to use them. It also knows about the Brokers and Services and what methods they have that it can call and how they will react. This allows it to implement the business logic that is specific to the MVCI framework that it belongs to. It then uses these Domain Objects to create and update the data elements in the Model, which then automatically updates the View.\nThat\u0026rsquo;s what we are looking at here, although we are going to simulate everything and keep it very simple\u0026hellip;\nThe Database # At first I thought about using something like Redis as a database, but then decided that we didn\u0026rsquo;t need the added complexity that it would bring about. After all, this is a tutorial about JavaFX, not database programming. Then I thought that I\u0026rsquo;d store the records as JSON, but discarded this as a useless distraction. So our \u0026ldquo;database\u0026rdquo; is going to be this:\nMap\u0026lt;Integer, Map\u0026lt;String,String\u0026gt;\u0026gt; Many modern databases are designed as Key/Value pairs, with the database record being the value. This is really the same thing, but all in Java. In the outer Map, the keys are the record id\u0026rsquo;s, and the inner Map holds the individual fields - the key being the field name and the value being the field value.\nThis is nice because it\u0026rsquo;s all Java. We don\u0026rsquo;t need to worry about JSON libraries and yet the database storage is distinctly different from how we generally store objects in Java, so that we\u0026rsquo;ll need a translator class. In that respect, it mimics what you\u0026rsquo;d expect to see in the real world.\nBut a database is more than just a single Map. There are indexes and queries and multiple tables and data relationships that all need to be handled. So our database simulator will eventually need to do those things too. To start with, though, we\u0026rsquo;re going to generate internal record id\u0026rsquo;s that we\u0026rsquo;ll use as record keys.\nHere\u0026rsquo;s the start for our database class:\npublic class CustomerDatabase { private Map\u0026lt;Integer, Map\u0026lt;String, String\u0026gt;\u0026gt; data = new HashMap\u0026lt;\u0026gt;(); private Integer nextKey = 0; public int saveCustomer(Map\u0026lt;String, String\u0026gt; customerRecord) { customerRecord.put(\u0026#34;_id\u0026#34;, (++nextKey).toString()); data.put(nextKey, customerRecord); return nextKey; } Map\u0026lt;Integer, Map\u0026lt;String,String\u0026gt;\u0026gt; getData() { return data; } } This is pretty straight-forward. We have one public method, which takes a \u0026ldquo;record\u0026rdquo; and saves it in the database, assigning it a unique key. The key is just a counter that starts at zero.\nThere\u0026rsquo;s not a lot here, but it is testable, so we should test it.\nTesting # One of the big issues with building JavaFX applications is that the way that the FXAT works makes it very difficult to test with standard automated code-based testing tools. People have pulled their hair out trying to figure out ways around this, but it is just about impossible. That\u0026rsquo;s because the FXAT treats every Event as a job, and there is no continuity between jobs. Testing tools tend to work on the basis of, \u0026ldquo;Run this code, check the result\u0026rdquo;. But if the \u0026ldquo;Run this code\u0026rdquo; part launches something on the FXAT, then you get into trouble.\nThis means that you are out of luck when it comes to automated testing of your layouts, unless you use some \u0026ldquo;Robot\u0026rdquo; based testing tool like Selenium to do the testing.\nHowever, the nice thing about using a structure like MVCI is that virtually everything from the Interactor down to the DAO\u0026rsquo;s can run without the FXAT getting involved. And that means that we can write automated tests for it. For this project I\u0026rsquo;m using JUnit 5.\nExplaining JUnit is way beyond the scope of this course. If you understand it, that\u0026rsquo;s great. If you don\u0026rsquo;t understand how it works, at least you can see that the tests can be done, and what they test for.\nThe main take-away here is that you understand that a properly organized JavaFX application can - and should - have automated testing for its business logic.\nThis is the bottom layer of our application structure. It\u0026rsquo;s vital that we know that it works as intended, so that we can put it out of our minds when we our investigating issues with the higher layers of our application. Here\u0026rsquo;s a test class:\nclass CustomerDatabaseTest { @Test void saveCustomerIdIncrementTest() { CustomerDatabase dataBase = new CustomerDatabase(); assertEquals(1,dataBase.saveCustomer(new HashMap\u0026lt;String, String\u0026gt;()), \u0026#34;Id is incremented\u0026#34;); } @Test void saveCustomerIdIncrementTwiceTest() { CustomerDatabase dataBase = new CustomerDatabase(); dataBase.saveCustomer(new HashMap\u0026lt;String, String\u0026gt;()); assertEquals(2,dataBase.saveCustomer(new HashMap\u0026lt;String, String\u0026gt;()), \u0026#34;Id is incremented\u0026#34;); } @Test void saveCustomerIdInsertionTest() { CustomerDatabase dataBase = new CustomerDatabase(); HashMap\u0026lt;String, String\u0026gt; customerRecord = new HashMap\u0026lt;\u0026gt;(); dataBase.saveCustomer(new HashMap\u0026lt;String, String\u0026gt;()); assertEquals(\u0026#34;1\u0026#34;,dataBase.getData().get(1).get(\u0026#34;_id\u0026#34;), \u0026#34;Id is inserted\u0026#34;); } @Test void saveCustomerRecordCorrectTest() { CustomerDatabase dataBase = new CustomerDatabase(); HashMap\u0026lt;String, String\u0026gt; customerRecord = new HashMap\u0026lt;\u0026gt;(); customerRecord.put(\u0026#34;full_name\u0026#34;, \u0026#34;Fred\u0026#34;); dataBase.saveCustomer(customerRecord); assertEquals(\u0026#34;Fred\u0026#34;, dataBase.getData().get(1).get(\u0026#34;full_name\u0026#34;), \u0026#34;Id is inserted\u0026#34;); } } In order to do the testing, we need to be able to reach inside and see the raw Map data independent of any functional method calls. So there\u0026rsquo;s a getter for the Map in CustomerDatabase. We might be able to remove this later on, when we have some retrieve functions in our application, but for now we\u0026rsquo;ll use this. In the real world of corporate application development, this approach might not be possible as it exposes parts of the database class to outside classes that really shouldn\u0026rsquo;t be opened up.\nThe first two tests are to make sure that the ID is incremented by just one each time that saveCustomer() is called. The next test ensures that the internal _id field is added to our record when it is saved. The final test makes sure (to some degree) that the data that we wanted saved in the database is actually saved.\nThe Data Access Object (DAO) # The DAO class is the class that understands how to access the database. It knows about the various tables, the record structures, the relationships between the tables and how to query and retrieve data, as well as how to store it.\nHere\u0026rsquo;s our DAO so far:\npublic class CustomerDAO { static CustomerDatabase database = new CustomerDatabase(); public int saveCustomer(CustomerDTO customerRecord) { return database.saveCustomer(createCustomerRecord(customerRecord)); } Map\u0026lt;String, String\u0026gt; createCustomerRecord(CustomerDTO customer) { Map\u0026lt;String, String\u0026gt; customerRecord = new HashMap\u0026lt;\u0026gt;(); customerRecord.put(\u0026#34;full_name\u0026#34;, customer.getFullName()); customerRecord.put(\u0026#34;account_number\u0026#34;, customer.getAccountNumber()); return customerRecord; } } and here\u0026rsquo;s the Data Transfer Object:\npublic class CustomerDTO { private String accountNumber = \u0026#34;\u0026#34;; private String fullName = \u0026#34;\u0026#34;; public CustomerDTO(String accountNumber, String name) { this.accountNumber = accountNumber; this.fullName = name; } public String getAccountNumber() { return accountNumber; } public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; } public String getFullName() { return fullName; } public void setFullName(String fullName) { this.fullName = fullName; } } There\u0026rsquo;s not much to it. The database is instantiated as a static field since we only ever want to have one database in our application, no matter how we instantiate the DAO, or how often we instantiate it.\nThere\u0026rsquo;s only two methods so far, and at this point saveCustomer() merely delegates to the database saveCustomer() method. There is nothing to test here - at least so far.\nThe other method converts CustomerDTO to the database structure, which is Map\u0026lt;String, String\u0026gt;. This is something we should test:\npublic class CustomerDAOTest { @Test void createCustomerRecord_AccountNumberTest() { CustomerDAO dao = new CustomerDAO(); CustomerDTO customer = new CustomerDTO(\u0026#34;1234\u0026#34;, \u0026#34;Fred\u0026#34;); assertEquals(\u0026#34;1234\u0026#34;, dao.createCustomerRecord(customer).get(\u0026#34;account_number\u0026#34;), \u0026#34;Account number check\u0026#34;); } @Test void createCustomerRecord_CustomerNameTest() { CustomerDAO dao = new CustomerDAO(); CustomerDTO customer = new CustomerDTO(\u0026#34;1234\u0026#34;, \u0026#34;Fred\u0026#34;); assertEquals(\u0026#34;Fred\u0026#34;, dao.createCustomerRecord(customer).get(\u0026#34;full_name\u0026#34;), \u0026#34;Customer name check\u0026#34;); } } The Broker and Domain Objects # Now we get to the application layer that introduces the idea of a \u0026ldquo;Domain Object\u0026rdquo;. Domain Objects are business objects. They hold the data required to perform business operations and business logic. In our application we have just one kind of Domain Object - the Customer:\npublic class Customer { private String accountNumber = \u0026#34;\u0026#34;; private String name = \u0026#34;\u0026#34;; public String getAccountNumber() { return accountNumber; } public void setAccountNumber(String accountNumber) { this.accountNumber = accountNumber; } public String getName() { return name; } public void setName(String name) { this.name = name; } } Yeah, this almost identical to our Model but without the Properties. It feels like a duplication of information. That\u0026rsquo;s mostly because our Model at this stage is so simple. We don\u0026rsquo;t have Properties to control data validation, or to handle other aspects of State that become important in a real application. So, for now, we just have to live with this feeling of duplication.\nThe Broker lives in a world in-between the business logic and the DAO. It knows about Domain Objects and database record structures. It knows how to communicate with the DAO, but it doesn\u0026rsquo;t know anything about tables, queries or database commands.\nThe Broker\u0026rsquo;s job is to turn Domain Objects into database-friendly data and pass them to the DAO for storage. Eventually it will do the opposite - ask the DAO for records and turn them into Domain Objects.\nHere\u0026rsquo;s our Broker so far:\npublic class CustomerBroker { private final CustomerDAO dao = new CustomerDAO(); public int saveCustomer(Customer customer) { return dao.saveCustomer(createCustomerDTO(customer)); } CustomerDTO createCustomerDTO(Customer customer) { return new CustomerDTO(customer.getAccountNumber(), customer.getName()); } } We have some independent, functional code here, so we\u0026rsquo;ll need create some tests. I\u0026rsquo;ve deliberately made createCustomerRecord() package private, so that we can call it directly in a test. If we didn\u0026rsquo;t do this, then we\u0026rsquo;d have to use a mocking library to mock the DAO so that we can see what\u0026rsquo;s being passed to it. That\u0026rsquo;s too much of a side trip.\nHere\u0026rsquo;s the test class:\nclass CustomerBrokerTest { @Test void createCustomerRecord_AccountNumberTest() { CustomerBroker broker = new CustomerBroker(); Customer customer = new Customer(); customer.setAccountNumber(\u0026#34;1234\u0026#34;); customer.setName(\u0026#34;Fred\u0026#34;); assertEquals(\u0026#34;1234\u0026#34;, broker.createCustomerDTO(customer).getAccountNumber(), \u0026#34;Account number check\u0026#34;); } @Test void createCustomerRecord_CustomerNameTest() { CustomerBroker broker = new CustomerBroker(); Customer customer = new Customer(); customer.setAccountNumber(\u0026#34;1234\u0026#34;); customer.setName(\u0026#34;Fred\u0026#34;); assertEquals(\u0026#34;Fred\u0026#34;, broker.createCustomerDTO(customer).getFullName(), \u0026#34;Customer name check\u0026#34;); } } One note here: When I write these tests, I always fiddle with the \u0026ldquo;expected value\u0026rdquo; parameter of the Assert to something that is wrong, just to make sure that the test will fail if the CUT (Class/Code Under Test) is broken. Then I put it back to the correct value so the test passes.\nBack to the Interactor # Now that we have our back-end built, we can go back to the Interactor and integrate it into our application:\npublic class CustomerInteractor { private final CustomerModel model; private final CustomerBroker broker = new CustomerBroker(); public CustomerInteractor(CustomerModel model) { this.model = model; } public void saveCustomer() { int result = broker.saveCustomer(createCustomerFromModel()); System.out.println(\u0026#34;Saving account: \u0026#34; + model.getAccountNumber() + \u0026#34; Name: \u0026#34; + model.getCustomerName() + \u0026#34; Result: \u0026#34; + result); } Customer createCustomerFromModel() { Customer customer = new Customer(); customer.setAccountNumber(model.getAccountNumber()); customer.setName(model.getCustomerName()); return customer; } } At this point it becomes clear that the Interactor is the place where the Domain Objects and the GUI Model co-exist. The Broker only deals in Domain Objects, and will never have any knowledge of our GUI Model.\nYou can see that we\u0026rsquo;ve added a field for the Broker, and then added a call to it into CustomerBroker.saveCustomer(). I\u0026rsquo;ve left the console output in there so that we can see how it works from the GUI. Aside from that, saveCustomer() itself simply delegates to the Broker, so there\u0026rsquo;s no need for a test here.\nHowever, we do have a step that takes the data from the Model and turns it into a Domain Object. That\u0026rsquo;s something that we do need to test:\nclass CustomerInteractorTest { @Test void createCustomer_NameTest() { CustomerModel model = new CustomerModel(); CustomerInteractor interactor = new CustomerInteractor(model); model.setCustomerName(\u0026#34;Fred\u0026#34;); model.setAccountNumber(\u0026#34;ABCDE\u0026#34;); assertEquals(\u0026#34;Fred\u0026#34;, interactor.createCustomerFromModel().getName(),\u0026#34;Check customer name\u0026#34;); } @Test void createCustomer_AccountTest() { CustomerModel model = new CustomerModel(); CustomerInteractor interactor = new CustomerInteractor(model); model.setCustomerName(\u0026#34;Fred\u0026#34;); model.setAccountNumber(\u0026#34;ABCDE\u0026#34;); assertEquals(\u0026#34;ABCDE\u0026#34;, interactor.createCustomerFromModel().getAccountNumber(),\u0026#34;Check customer name\u0026#34;); } } Even though CustomerModel is composed of Properties that are JavaFX elements, it\u0026rsquo;s OK to access them from a JUnit test without any FXAT running at all. This means that we can test methods in Interactor without any issues.\nThe Data Classes # We now have four different data object types, each primarily used at a different level of the application. At this point in our application, all four of these object types are holding pretty much the same information just in different ways.\nThe Model This holds the data inside Observable classes that can be integrated with the View Nodes. This might also be known as the \u0026ldquo;Presentation Model\u0026rdquo; or \u0026ldquo;GUI State\u0026rdquo; object. The Domain Object This is Customer and it holds the data in regular Java data fields. Note that the customer\u0026rsquo;s name is stored in name. The Data Transfer Object This is CustomerDTO and it also holds the data in regular Java data fields. It differs from the Domain Object in that the data is organized in a fashion oriented towards the database storage. Note that the customer\u0026rsquo;s name is stored in fullName, not name. The Database Record This is a Map\u0026lt;String, String\u0026gt;, but it still holds exactly the same data as the other three classes, just in a different format. I don\u0026rsquo;t want to repeat myself too much, but the reason that we have 4 different data classes, all holding very similiar information, is to prevent coupling. With this structure, the database can completely change and that change will not ripple up past the DAO, and the DTO will remain the same. Changes at the DAO won\u0026rsquo;t ripple up past the Broker, and the Domain Objects will remain unchanged.\nNote This is so important that it cannot be overstated. Coupling is the thing that will make your application difficult to extend and maintain, so you need to avoid it whenever possible.\nConclusion # That\u0026rsquo;s our back-end completed and our first, bare-bones, feature all wrapped up. We have an application that can create a Customer and store it in our database. We have tests for everything that can and should be tested.\nOnce again, this thing runs!\nIt\u0026rsquo;s also potentially deployable, even though it\u0026rsquo;s ugly and doesn\u0026rsquo;t have any error checking or validation controls.\n","externalUrl":null,"permalink":"/beginners/part6b/","section":"JavaFX","summary":"In this lesson we finish up a “bare-bones” Create function by building the back-end and connecting it to the business logic in the Interactor.  Part of this involves creating a simulated database.","title":"Part 6B - Adding Persistence","type":"javafx"},{"content":" The Application So Far # Now we have an application that allows the user to enter in an account number and name, and then to save it in our database. It looks like this when it\u0026rsquo;s running:\nWhere Do We Go From Here? # What should we do next?\nTypically, in a Agile development scenario, you would get feedback from the Product Owner who is responsible for setting the priorities on the \u0026ldquo;Product Backlog\u0026rdquo;. The Product Backlog is just a big list of possible updates to the application, which might be new features, bug fixes or changes to something that\u0026rsquo;s already been done. The Product Owner always works from the position that what\u0026rsquo;s been delivered so far is the final product, unless they ask for something to be done.\nIn our case, we have no Product Owner, but we can speculate about how a reasonable Product Owner would decide about the priorities for development.\nOur Product Backlog # The Product Backlog is just a list of things that we might do to our application. It\u0026rsquo;s a dynamic thing, and every time we update our application and show it to the users, they\u0026rsquo;ll come up with new ideas that go into the Product Backlog. We\u0026rsquo;ll keep our list to just things that seem likely at this stage.\nDivide the Name Field # Just having everything in one field is a bit loose. What\u0026rsquo;s the format? \u0026ldquo;Fred Smith\u0026rdquo;? What about \u0026ldquo;Mr. Fred Smith\u0026rdquo;? Maybe \u0026ldquo;Smith, Fred\u0026rdquo;? It\u0026rsquo;s probably a good idea to break it down into three fields: a salutation, the first name and the last name.\nDatabase Latency # Just about every database that you can use has at least the possibility of latency and blocking. Our simulated database has none of that, and our application doesn\u0026rsquo;t have any way to handle it. This is a big problem, and we need to deal it before we can go \u0026ldquo;live\u0026rdquo;.\nEmpty Records # Let\u0026rsquo;s run the application and hit \u0026ldquo;Save\u0026rdquo; without entering anything in the TextFields:\n\u0026gt; Task :run Saving account: Name: Result: 1 That\u0026rsquo;s not good. We shouldn\u0026rsquo;t be able to save empty records.\nDuplicate Records # If you run the application, enter an account number and name and then hit save several times, you\u0026rsquo;ll get console output that looks like this:\n\u0026gt; Task :run Saving account: 1234 Name: Fred Smith Result: 1 Saving account: 1234 Name: Fred Smith Result: 2 Saving account: 1234 Name: Fred Smith Result: 3 Saving account: 1234 Name: Fred Smith Result: 4 Oops! It looks like we saved the same record in our database 4 times!\nEven worse, if we change the name each time, we get this:\n\u0026gt; Task :run Saving account: 1234 Name: Fred Smith Result: 1 Saving account: 1234 Name: George White Result: 2 Saving account: 1234 Name: Robert Bland Result: 3 Saving account: 1234 Name: Sara Brown Result: 4 Yikes! Now we have 4 different records for account \u0026ldquo;1234\u0026rdquo;, each for a different person.\nWe should fix this.\nMore Fields # There\u0026rsquo;s very little practical use for an application that only stores the customer name. We\u0026rsquo;ll need to add fields for address, phone number and email.\nLook and Feel # This application is still pretty ugly, and it will only get worse as we expand it. Maybe we should do some work to clean up the look and feel before we go any further?\nRetrieve # So far, we\u0026rsquo;ve only done the \u0026ldquo;Create\u0026rdquo; part of \u0026ldquo;CRUD\u0026rdquo;. We really cannot do \u0026ldquo;Delete\u0026rdquo; or \u0026ldquo;Update\u0026rdquo; until we\u0026rsquo;ve implemented \u0026ldquo;Retrieve\u0026rdquo;, so that should be the next major feature that we add.\nThe Decision # At this point, our application works but it relies on the users behaving themselves and not creating duplicates or empty records. These things can corrupt our database, so they need to be addressed before we expand the feature set.\nEven more importantly though, is that our application wouldn\u0026rsquo;t work very well with a real database that introduces latency and potentially blocks while waiting for a response. From that respect, our application is \u0026ldquo;non-functioning\u0026rdquo; and needs to fixed before we do anything else.\n","externalUrl":null,"permalink":"/beginners/part7/","section":"JavaFX","summary":"Now we’ve completed the core programming for our “Create” function, where do go next.  We’ll look at the idea of a “Product Backlog”, and talk about how we should prioritize our next steps.","title":"Part 7 - What Next?","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # The importance of using background threads. How to use Task to process on a background thread. Considerations for the GUI while a background task is running. Avoiding concurrency issues when multi-threading Avoiding Button double-clicks. The Problem With Our Application # Virtually any database or external API you call from your application has at least the potential to take a measurable amount of time to respond, or to possibly time out and not return an answer at all.\nWhat happens is that your Java code eventually needs to hand off to some other process to do the work. Maybe you\u0026rsquo;ve executed an HTTP \u0026ldquo;Get\u0026rdquo; command, or you\u0026rsquo;ve asked the kernel to read a file, or you\u0026rsquo;ve invoked an API handler for an application running on your computer. In all of these cases, your Java code is going to invoke an external process and then execute a wait() statement, or at least its equivalent. When the external process completes, it will send a signal to your Java process to wake up and continue on.\nThe problem that this presents for JavaFX is that it\u0026rsquo;s always busy. It\u0026rsquo;s always checking for mouse movements, events that are triggered, button clicks, data changes that impact the GUI. It\u0026rsquo;s always busy and it all happens on a single thread - the \u0026ldquo;FX Application Thread\u0026rdquo; or \u0026ldquo;FXAT\u0026rdquo;. This includes the code that you write that works as an EventHandler - it all runs on the FXAT by default (as it should).\nThe one thing you should NEVER do is execute a wait() (or its equivalent) on the FXAT. When you do this, everything in your GUI - EVERYTHING - stops dead. As a matter of fact, you should never execute anything on the FXAT that takes more than a few milliseconds to run.\nRight now, our database is an inaccurate simulation because it has no latency. Let\u0026rsquo;s fix that\u0026hellip;\nAdding Latency to Our Database # The easiest way to add latency is to simple put a Thread.sleep() command in our database method. Like this:\npublic class CustomerDatabase { private Map\u0026lt;Integer, Map\u0026lt;String, String\u0026gt;\u0026gt; data = new HashMap\u0026lt;\u0026gt;(); private Integer nextKey = 0; public int saveCustomer(Map\u0026lt;String, String\u0026gt; customerRecord) { try { Thread.sleep(10000); } catch (InterruptedException e) { throw new RuntimeException(e); } customerRecord.put(\u0026#34;_id\u0026#34;, (++nextKey).toString()); data.put(nextKey, customerRecord); return nextKey; } Map\u0026lt;Integer, Map\u0026lt;String,String\u0026gt;\u0026gt; getData() { return data; } } If you run this now, you\u0026rsquo;ll see that the GUI goes entirely inactive for 10 seconds. Nothing happens when you click on a TextField, click the button or try to expand the window. I\u0026rsquo;d show you, but there\u0026rsquo;s simply nothing to see. It\u0026rsquo;s just\u0026hellip;dead.\nThere\u0026rsquo;s a problem with this approach, though. Our tests now take forever to run. Let\u0026rsquo;s look at the console output from running the database tests:\n\u0026gt; Configure project : Project : =\u0026gt; \u0026#39;ca.pragmaticcoding.beginners\u0026#39; Java module \u0026gt; Task :compileJava UP-TO-DATE \u0026gt; Task :processResources UP-TO-DATE \u0026gt; Task :classes UP-TO-DATE \u0026gt; Task :mergeClasses SKIPPED \u0026gt; Task :compileTestJava \u0026gt; Task :processTestResources NO-SOURCE \u0026gt; Task :testClasses \u0026gt; Task :test BUILD SUCCESSFUL in 52s 4 actionable tasks: 2 executed, 2 up-to-date 11:36:56 a.m.: Execution finished \u0026#39;:test --tests \u0026#34;ca.pragmaticcoding.beginners.part8.CustomerDatabaseTest\u0026#34;\u0026#39;. 52 seconds!\nThis is not acceptable. Unit tests should never take more than a few milliseconds to run each. This is one of the reasons that you never access live databases from unit tests.\nWe need to find a way to add the latency to the application without slowing down our tests.\nOne way to do this is to implement the sleep() in a separate class extended from our database class. Then we can continue to test the original database class, while using a \u0026ldquo;slow\u0026rdquo; version for our production code:\npublic class SlowCustomerDatabase extends CustomerDatabase { private final int delay; public SlowCustomerDatabase(int delay) { this.delay = delay; } @Override public int saveCustomer(Map\u0026lt;String, String\u0026gt; customerRecord) { delay(); return super.saveCustomer(customerRecord); } private void delay() { try { Thread.sleep(delay); } catch (InterruptedException e) { throw new RuntimeException(e); } } } Then we\u0026rsquo;ll modify CustomerDao to use our \u0026ldquo;slow\u0026rdquo; version for the production code:\npublic class CustomerDAO { static CustomerDatabase database = new SlowCustomerDatabase(5000); public int saveCustomer(Map\u0026lt;String, String\u0026gt; customerRecord) { return database.saveCustomer(customerRecord); } } Now our production code runs with a delay, and our test code completes in less than 1 second.\nI\u0026rsquo;ll admit that this is a bit of a cheat. In a real world example you don\u0026rsquo;t have the luxury of simulating a database without latency for your automated testing. But in the real world, you never run automated tests against an actual database. What you would have to do is create Mocking classes for the database connections that can respond instantly and in exactly the way that you want - every time. But that is way beyond the scope of this tuturial.\nThe Solution: A Background Thread # Now, if our database has latency and blocks, meaning that we can\u0026rsquo;t access it on the FXAT, then the correct solution is to do the access on some other thread. This is usually referred to as a \u0026ldquo;background thread\u0026rdquo;, because it runs in the background while the FXAT keeps doing its thing.\nJavaFX has a tool just for doing this. It\u0026rsquo;s called Task. So that\u0026rsquo;s what we\u0026rsquo;re going to use.\nIn MVCI, the Controller is responsible for controlling threads. So that\u0026rsquo;s where we are going to implement our Task:\npublic class CustomerController { private final Builder\u0026lt;Region\u0026gt; viewBuilder; private final CustomerInteractor interactor; public CustomerController() { CustomerModel model = new CustomerModel(); interactor = new CustomerInteractor(model); viewBuilder = new CustomerViewBuilder(model,this::saveCustomer); } private void saveCustomer() { Task\u0026lt;Void\u0026gt; saveTask = new Task\u0026lt;\u0026gt;() { @Override protected Void call() { interactor.saveCustomer(); return null; } }; Thread saveThread = new Thread(saveTask); saveThread.start(); } public Region getView() { return viewBuilder.build(); } } Some Information About Task # Task is a utility class that is designed to run some code in a background thread. Task itself is abstract, and has 1 abstract method: call(). The most common way to use Task is to instantiate an anonymous inner class that extends Task and supply the implementation of call(). That\u0026rsquo;s what we\u0026rsquo;ve done in this example. Task extends classes that implement Runnable, so that when we supply it to a Thread in it\u0026rsquo;s constructor and then invoke Thread.start() it will invoke Task.call() in that Thread.\nThis means that any code that we put into Task.call() will be run on a background thread.\nWhen you start dealing with threads, you need to wrap your mind around the idea that pieces of your code will run simultanously, and you can no longer expect your code to run in a linear fashion. The code that instantiates the Task, defines Task.call() and starts the Task running on a background thread all runs on the FXAT. Whatever code that comes after Thread.start() will happen immediately, and will not depend on the completion of Task.call(). You cannot wait for it on the FXAT, because then your GUI would freeze.\nIn our example, saveThread.start() is the last line of code in CustomerController.saveCustomer() and is probably the last line of code in the job that the FXAT is running when it executes it. So the FXAT is going to going to go on and handle the next job in its queue.\nYou may also notice that Task is generic, and that Task.call() returns a value of whatever type we declare our instance of Task as. In our case, we don\u0026rsquo;t have a value to return, and so we are just using Void.\nBack to Our Example # Really, at this point, all we\u0026rsquo;ve done is take the call to CustomerInteractor.saveCustomer() and run it in another thread. We don\u0026rsquo;t need Task to do that, do we?\nLet\u0026rsquo;s take a look at what happens when this runs. There\u0026rsquo;s nothing much to see, but the GUI isn\u0026rsquo;t dead any more. I can interact with it while I\u0026rsquo;m waiting for the database call to complete, and it works.\nBut there\u0026rsquo;s a problem. Here\u0026rsquo;s what I did:\nEntered account, \u0026ldquo;123\u0026rdquo; and name \u0026ldquo;Fred\u0026rdquo; Clicked \u0026ldquo;Save\u0026rdquo; Clicked \u0026ldquo;Save\u0026rdquo; again. Went back to the name TextField and added \u0026quot; Smith\u0026quot; Clicked \u0026ldquo;Save\u0026rdquo; again. Clicked \u0026ldquo;Save\u0026rdquo; again. Here\u0026rsquo;s the console output:\n\u0026gt; Task :run Saving account: 123 Name: Fred S Result: 1 Saving account: 123 Name: Fred Smith Result: 2 Saving account: 123 Name: Fred Smith Result: 3 Saving account: 123 Name: Fred Smith Result: 4 Oh, oh! There\u0026rsquo;s no save for just \u0026ldquo;Fred\u0026rdquo;! The first save is for \u0026ldquo;Fred S\u0026rdquo;. At no point did I click when the name TextField said just \u0026ldquo;Fred S\u0026rdquo;. What\u0026rsquo;s going on here?\nIt turns out the reason for this is in the Interactor:\npublic void saveCustomer() { int result = broker.saveCustomer(createCustomerFromModel()); System.out.println(\u0026#34;Saving account: \u0026#34; + model.getAccountNumber() + \u0026#34; Name: \u0026#34; + model.getCustomerName() + \u0026#34; Result: \u0026#34; + result); } We call the Broker, which calls the DAO which calls the \u0026ldquo;slow\u0026rdquo; database which returns a results after 5 seconds. Then we output some information to the console which combines the return value from database with data from the Model. But the Model is now shared between two threads, the FXAT and our background thread. And when we click over and over, it\u0026rsquo;s shared by even more threads.\nThis can be a problem.\nSince changes to the GUI are \u0026ldquo;live\u0026rdquo; changes to State, whenever we need to use State in a background thread we need to do one of two things:\nCopy the elements of State to some place private to the background thread. Lock the GUI so that it cannot update State. Let\u0026rsquo;s do the first here (this code is in the Interactor):\npublic void saveCustomer() { String customerName = model.getCustomerName(); String account = model.getAccountNumber(); int result = broker.saveCustomer(createCustomerFromModel()); System.out.println(\u0026#34;Saving account: \u0026#34; + account + \u0026#34; Name: \u0026#34; + customerName + \u0026#34; Result: \u0026#34; + result); } That\u0026rsquo;s an improvement. Now when I enter \u0026ldquo;123\u0026rdquo; and \u0026ldquo;Fred\u0026rdquo;, click \u0026ldquo;Save\u0026rdquo; and then change the name to \u0026ldquo;Fred Smith\u0026rdquo;, then click save two more times I get:\n\u0026gt; Task :run Saving account: 123 Name: Fred Result: 1 Saving account: 123 Name: Fred Smith Result: 2 Saving account: 123 Name: Fred Smith Result: 3 That\u0026rsquo;s way better. It\u0026rsquo;s probably a safe bet that the background thread is going to launch and the broker call is going to start executing before anyone could ever get their hand off the mouse and start typing into any of the TextFields. That being said, this is something that you might want to look out for in a real application.\nBut it\u0026rsquo;s still disturbing that you could click on that Button three times before it finished processing. That\u0026rsquo;s something that we should deal with.\nMore About Task - Completion Events # You may have been wondering, \u0026ldquo;Why bother with Task? Why not put a Runnable in a Thread?\u0026rdquo;.\nTask is specifically designed to allow you to co-ordinate code on the FXAT with background code, which is a crucial ability. One of the ways that it does this is with a set of Events that are fired when the background job ends. These are defined in the Task as ObjectProperty\u0026lt;EventHandler\u0026gt; and you can put EventHandlers in them.\nThere are three ways that a Task can end:\nCompleted This is fired when the code in Task.call() completes normally. Failed This happens only when the code in Task.call() terminates with an uncaught, unhandled Exception of any type. Cancelled This fires when some code calls Task.cancel(). If you don\u0026rsquo;t have any code that does this, then you don\u0026rsquo;t have to worry about it. You can set up these event handlers by calling Task.setOnCompleted(), Task.setOnFailed() or Task.setOnCancelled().\nThe key idea behind this is that all EventHandlers run on the FXAT! This means that you can write some code, in an EventHandler, that will be executed on the FXAT when the Task completes its job. This turns out to be very, very powerful\u0026hellip;\nBack to Our Example (Again) # One of the ways that you can utilize this power is to create a \u0026ldquo;workflow\u0026rdquo; for creating and running a Task. Generally, that workflow is something like this:\n[FXAT] Put the GUI in the state that you want it while the Task runs. [Background] Execute the Task. [FXAT] Put the GUI back into its \u0026ldquo;normal\u0026rdquo; state when the Task completes. The first and last steps are performed on the FXAT, the Task executes on the background thread.\nThere\u0026rsquo;s just one problem, though. The GUI stuff is the property of the View, and the Task is part of the Controller. How do you handle this?\nThe first thing we\u0026rsquo;re going to do is change our saveHandler from a Runnable to a Consumer\u0026lt;Runnable\u0026gt;. Then we\u0026rsquo;re going to have our Controller execute that Runnable passed through the Consumer in the Task completion EventHandler. It\u0026rsquo;s more complicated to explain than it is to just see the code:\nprivate void saveCustomer(Runnable postTaskGuiActions) { Task\u0026lt;Void\u0026gt; saveTask = new Task\u0026lt;\u0026gt;() { @Override protected Void call() { interactor.saveCustomer(); return null; } }; saveTask.setOnSucceeded(evt -\u0026gt; postTaskGuiActions.run()); Thread saveThread = new Thread(saveTask); saveThread.start(); } Now saveCustomer() is passed a Runnable, which it runs when the Task completes.\nWhat happens in the View is a little bit more interesting, first the constructor:\npublic CustomerViewBuilder(CustomerModel model, Consumer\u0026lt;Runnable\u0026gt; saveHandler) { this.model = model; this.saveHandler = saveHandler; } You can see here that we\u0026rsquo;ve changed the type of saveHandler from a Runnable to a Consumer\u0026lt;Runnable\u0026gt;. Let\u0026rsquo;s take a look at that a little closer. A Consumer is a \u0026ldquo;Functional Interface\u0026rdquo; that accepts a single parameter and returns nothing. It \u0026ldquo;consumes\u0026rdquo; the value that it is passed. In this case, it will be consuming a Runnable, which is just a snippet of executable code.\nRemember that this saveHandler is passed in to the View. The View isn\u0026rsquo;t going to define it, it is going to invoke it. But the View is going to define the Runnable that it passes to saveHandler.\nLet\u0026rsquo;s look at how that works in the code for the Button:\nprivate Node createButtons() { Button saveButton = new Button(\u0026#34;Save\u0026#34;); saveButton.setOnAction(evt -\u0026gt; { saveButton.setDisable(true); saveHandler.accept(() -\u0026gt; saveButton.setDisable(false)); }); HBox results = new HBox(10, saveButton); results.setAlignment(Pos.CENTER_RIGHT); return results; } The Runnable that is passed to saveHandler is the single command, saveButton.setDisable(false). This is passed to the Consumer via Consumer.accept(), and is the code that is then passed back to the Controller as a Runnable and then gets put into the onCompleted EventHandler of the Task. So it\u0026rsquo;s actually passed around twice.\nNow, when the Button is clicked, the first thing that happens is that it is disabled. Then the saveHandler is invoked, passing it a Runnable that re-enables the Button. Like this:\nThe Button will remain disabled until the Task completes, and the onCompleted EventHandler is executed. That EventHandler will call Runnable.run() which will execute saveButton.setDisable(false) and the Button will be re-enabled.\nIn this way, we keep the GUI code in the ViewBuilder, and the control over the threading in the Controller.\nKeeping Secrets # Let\u0026rsquo;s look at what the elements of our MVCI framework \u0026ldquo;know\u0026rdquo;, and \u0026ldquo;don\u0026rsquo;t know\u0026rdquo; about each other.\nThe Controller knows that it needs to supply the View with a Consumer that it can call to pass back a Runnable that is associated with the completion saving a customer record. Invocation of this Consumer should trigger the customer record save.\nThe Controller has no knowledge of what that Runnable does.\nThe Controller has no knowledge of how the Consumer will be invoked.\nThe View knows that it will get a Consumer from whatever class instantiates it. It knows that this Consumer is related to saving a customer and needs to be invoked (via Consumer.accept()) to execute the save.\nThe View does not know what the Consumer does.\nThe View does not know how the Runnable that it supplies will be invoked.\nThe Controller knows that it has to call Interactor.saveCustomer() via a Task in response to invocation of the Consumer by the View.\nThe Controller has no knowledge of what Interactor.saveCustomer() does.\nThe Interactor has no knowledge of any external components except the Model.\nThe Interactor is NOT aware that saveCustomer() will be run on a background thread.\nThis last item is a bit fluid. There may be occasions when an Interactor method is designed to run on either the FXAT or a background thread and you\u0026rsquo;ll need to keep away from doing certain things in either case.\nConclusion # Connecting your application to the real world means that you have to cope with blocking API calls, and that means you have to use background threads to handle these connections. But multi-threading means concurrency and concurrency brings its own issues. This is unavoidable, as the GUI needs to remain active while external access is running, and just something you\u0026rsquo;ll need to learn how to handle.\nProgrammers are used to imperative programming, where programs execute in a linear fashion and you always know how you got somewhere and the state of your application when you get there. Reactive programming breaks this paradigm, and multi-threaded programming complicates it even more. It\u0026rsquo;s not really difficult to cope with, but it takes some time to get used to the new way of thinking.\n","externalUrl":null,"permalink":"/beginners/part8/","section":"JavaFX","summary":"Our simulated database doesn’t really do a good job standing in for a real database because it works too fast.  We’ll slow it down and see what horrible things this does to our application.  Then we’ll see how to deal with this.","title":"Part 8 - Dealing With Latency and Blocking","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # How to define a business rule How to link a business rule to the Model with a Binding How to control a Button with Binding How to keep the business rules out of the View Next Problem - Empty Fields # We still have a functional problem with our application. It\u0026rsquo;s possible to save a customer without specifying an account number or a name. We\u0026rsquo;ll need to prevent this.\nEmpty Fields is a Business Rule # Here\u0026rsquo;s a very important concept: Validation rules about data and when things can be saved are business rules. They cannot be defined in the View, they need to be defined in the Interactor.\nHow do these rules get to the View, then?\nThrough the Model!\nThis means that we\u0026rsquo;re going to need to create something in the model that says, \u0026ldquo;It\u0026rsquo;s OK to save\u0026rdquo;:\npublic class CustomerModel { private final StringProperty accountNumber = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty customerName = new SimpleStringProperty(\u0026#34;\u0026#34;); private final BooleanProperty okToSave = new SimpleBooleanProperty(false); } I\u0026rsquo;ve left out all of the getter and setter stuff, because I\u0026rsquo;m sure you understand how that all works. We have a new BooleanProperty called okToSave.\nHow does CustomerModel.okToSave get set?\nThrough the Interactor. We\u0026rsquo;re going to bind okToSave to the other fields through custom Binding that makes sure that all of the fields have valid values:\npublic CustomerInteractor(CustomerModel model) { this.model = model; model.okToSaveProperty().bind(Bindings.createBooleanBinding(this::isDataValid, model.accountNumberProperty(), model.customerNameProperty())); } private boolean isDataValid() { return !model.getAccountNumber().isEmpty() \u0026amp;\u0026amp; !model.getCustomerName().isEmpty(); } Let\u0026rsquo;s take a look at isDataValid() first. It\u0026rsquo;s straight-forward method that just looks at all of the fields in CustomerModel that must have a value in them before the data can be saved. If any one of them is empty, then it will return false.\nIn the constructor of the Interactor we\u0026rsquo;re going to bind CustomerModel.okToSave to the other fields through a BooleanBinding. We\u0026rsquo;re using the Bindings library, which has huge number of static methods to create different kinds of Bindings for us. Here we\u0026rsquo;re using Bindings.createBooleanBinding().\nBindings.createBooleanBinding() takes at least two parameters, and can take many more. The first parameter is always a Supplier\u0026lt;Boolean\u0026gt;, and here we\u0026rsquo;re using a method reference to isDataValid(). The remaining parameters are the Properties that will trigger the Binding to recalculate whenever they change. In this case, it\u0026rsquo;s CustomerModel.accountNumber and CustomerModel.customerName.\nThis means that whenever either of those Properties change, the Binding will be recalculated and CustomerModel.okToSave will always be synchronized to them. In other words, okToSave will always have the correct value.\nThis is how we inject a business rule into the Model without putting the code in the Model. Now it\u0026rsquo;s available for the View to use.\nIs This the Best Approach? # There is a school of thought that the Interactor should never treat the elements of the Model as ObservabaleValues - which we are clearly doing here. Instead you should treat your Observable fields in the Model as generic containers for values.\nThere are some who believe that it should be possible to have an Interactor without any javafx.* imports at all. Here, we have:\nimport javafx.beans.binding.Bindings; because we have that call to Bindings.createBooleanBinding().\nHow can you create the binding without having the code to do so in the Interactor?\nThe only way you can do this is to move the binding creation into the Model itself. You can do this, but, if you want to keep the business logic in the Interactor, you\u0026rsquo;ll have to leave isDataValid() in the Interactor.\nThis would be fine, except that you\u0026rsquo;ll now have a reference to the Interactor in the Model. Something that we didn\u0026rsquo;t need before.\nI feel that this would be a mistake. As it stands, the Model is the central dependency that all of the other components share, but it has zero dependencies of its own. All of the dependencies are to the Model, and none of them are from the Model. I wouldn\u0026rsquo;t change this just to remove the JavaFX nature of the Model from the Interactor.\nAlternatively, you could move the binding code into the Controller. The Controller already knows about methods in the Interactor, so it could call Interactor.isDataValid() without creating onerous new dependencies between the Controller and the Interactor. Additionally, if I was to implement a ChangeListener on some field in the Model, I\u0026rsquo;d probably implement it in the Controller. Setting up a binding is just a small step from that.\nI feel that there\u0026rsquo;s a benefit to having both the binding code and the business logic that supports it in the same class. Why spread it around and make it harder to keep track of? Additionally, I feel that the Interactor isn\u0026rsquo;t really supposed to be agnostic towards the GUI environment. This is a JavaFX construct, and it can be acknowledged inside the Interactor without any real practical implications.\nAdding the Validation to the View # We are going to control the ability to save by disabling the \u0026ldquo;Save\u0026rdquo; Button when the data is not valid. We are going to do this with a Binding.\nThere\u0026rsquo;s a complication, though. We are already manually setting and unsetting Button.disable() as part of our Button action. So we\u0026rsquo;re going to need to deal with that:\nprivate Node createButtons() { Button saveButton = new Button(\u0026#34;Save\u0026#34;); saveButton.disableProperty().bind(model.okToSaveProperty().not()); saveButton.setOnAction(evt -\u0026gt; { saveButton.disableProperty().unbind(); saveButton.setDisable(true); saveHandler.accept(() -\u0026gt; saveButton.disableProperty().bind(model.okToSaveProperty().not())); }); HBox results = new HBox(10, saveButton); results.setAlignment(Pos.CENTER_RIGHT); return results; } Now we are creating the Button and then immediately binding it\u0026rsquo;s disable Property to CustomerModel.okToSave with the not() modifier. That\u0026rsquo;s pretty simple.\nBut we cannot directly set a Property that\u0026rsquo;s bound. We\u0026rsquo;ll get a runtime error if we do that. So in the Button onAction EventHandler we have to first unbind the Property, then set it to true. In the Runnable that we pass to saveHandler, we re-establish the binding instead of setting it to false.\nNow it Works # That\u0026rsquo;s it, and it was pretty painless, too.\nYou can see how we\u0026rsquo;ve established the business rule in the Interactor, connected it to the View via the Model, and the View uses it without having any knowledge about how it works.\nNow, let\u0026rsquo;s look at it in action:\nThe \u0026ldquo;Save\u0026rdquo; Button is disabled. Then with just account number filled out:\nIt\u0026rsquo;s still disabled. And then with both fields completed:\nNow it\u0026rsquo;s enabled and save can happen. We\u0026rsquo;re done!\n","externalUrl":null,"permalink":"/beginners/part9/","section":"JavaFX","summary":"We’ve also got a big problem with our application because there’s no data validation in our screen.  This means that users can save invalid customer data.  We’ll look at how to cope with this properly.","title":"Part 9 - GUI Validation","type":"javafx"},{"content":" Note This is not a beginners subject. You\u0026rsquo;ll just be scared away from Reactive JavaFX if you read this article as your introduction to Properties and Bindings.\nIf you have ever wondered, \u0026ldquo;What\u0026rsquo;s the difference between ObservableValue and ObservableObjectValue?\u0026rdquo;, or \u0026ldquo;When and how should I use ReadOnlyObjectProperty?\u0026rdquo;, or \u0026ldquo;What\u0026rsquo;s a ReadOnlyObjectWrapper?\u0026rdquo;, then this is the series for you to read.\nI spent hours and hours pouring over all of the JavaDocs and the source code for pretty much all of the JavaFX observable types (interfaces and classes) to figure out how they relate to each other and what they do. I don\u0026rsquo;t think that you\u0026rsquo;ll find a similar analysis anywhere else, and I think it can be quite valuable whenever you\u0026rsquo;re attempting to do something a bit more complex with JavaFX, especially if you venture into creating your own custom observable classes.\n","date":"28 August 2024","externalUrl":null,"permalink":"/javafx/elements/observables/properties/observable-classes/","section":"JavaFX","summary":"A three part series (so far) that provides an in-depth examination of all of the classes and interfaces that make up the library of observable types in JavaFX.","title":"Guide To the Observable Classes","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # Dealing with external API failures The difference between checked and unchecked exceptions How and where to handle exceptions Translating exceptions into Task results Communicating errors to the users through Alerts The Last Functional Issue # The last problem we have with out application is that it is possible to save customer records with duplicate account numbers. This is different from the last problem because we cannot know that there is an issue until after we attempt to save it in the database. Even if we wanted to build a retrieve function to check first, we still have to go to the database to determine if there is a problem.\nIn a lot of databases, you\u0026rsquo;d define the account number that we\u0026rsquo;re using as a \u0026ldquo;unique index\u0026rdquo;. This would mean that it was defined in our database as a field that we wish to do quick look-ups on, and that it will always have unique values. Attempting to save a new record with a value in a unique index field would generate an error.\nIt\u0026rsquo;s not really reasonable to take all of the rules in our database and bake them into our front end. At some point, we have to cope with the fact that our database might reject our update and somehow handle that situation and notify the user through the GUI. That\u0026rsquo;s what we are going to do here.\nDefining the Rule in the Database # The bulk of the work in this section is actual about how to get our database to simulate a real-world database that enforces rules about how data can be saved. So let\u0026rsquo;s get to it\u0026hellip;\nIn order to enforce this rule in the database, we\u0026rsquo;re going to need a way to report that it has been broken. This will be with a Checked Exception:\npublic class CustomerAlreadyOnFileException extends Exception { public CustomerAlreadyOnFileException(String accountNumber) { super(\u0026#34;Customer account: \u0026#34; + accountNumber + \u0026#34; is already on file\u0026#34;); } } That\u0026rsquo;s pretty simple. Now, we are going to need to put some code in our database to establish \u0026ldquo;account_number\u0026rdquo; as a unique index in our database:\npublic class CustomerDatabase { private final Map\u0026lt;Integer, Map\u0026lt;String, String\u0026gt;\u0026gt; data = new HashMap\u0026lt;\u0026gt;(); private Integer nextKey = 0; public int saveNewCustomer(Map\u0026lt;String, String\u0026gt; customerRecord) throws CustomerAlreadyOnFileException, IllegalArgumentException { Optional\u0026lt;String\u0026gt; accountNumberOptional = getAccount(customerRecord); if (accountNumberOptional.isPresent()) { String accountNumber = accountNumberOptional.get(); if (!isAccountOnFile(accountNumber)) { return saveCustomer(customerRecord); } else { throw new CustomerAlreadyOnFileException(accountNumber); } } else { throw new IllegalArgumentException(\u0026#34;Account number must be included in Customer Record\u0026#34;); } } int saveCustomer(Map\u0026lt;String, String\u0026gt; customerRecord) { customerRecord.put(\u0026#34;_id\u0026#34;, (++nextKey).toString()); data.put(nextKey, customerRecord); return nextKey; } boolean isAccountOnFile(String accountNumber) { return data.values().stream().anyMatch(record -\u0026gt; getAccount(record).map(value -\u0026gt; value.equals(accountNumber)).orElse(false)); } private Optional\u0026lt;String\u0026gt; getAccount(Map\u0026lt;String, String\u0026gt; customerRecord) { return Optional.ofNullable(customerRecord.get(\u0026#34;account_number\u0026#34;)); } Map\u0026lt;Integer, Map\u0026lt;String, String\u0026gt;\u0026gt; getData() { return data; } } We\u0026rsquo;ve now wrapped the call to saveCustomer in a new method called saveNewCustomer that has the logic to ensure that duplicate account numbers are not saved. This new method is now the public method that will be called from the DAO. The names accurately represent what they do.\nIn order to enforce the unique account numbers, we\u0026rsquo;ll need to search through the existing records, extract the \u0026ldquo;account_number\u0026rdquo; field from each one and check if it matches our new account number, which we\u0026rsquo;ll also have to extract from the new customer record.\nA method called getAccount() was created to do the extraction of the account number from a customer record. At this point we need some further explanation:\nBecause \u0026ldquo;account_number\u0026rdquo; is now an index value, it\u0026rsquo;s not allowed - at the database level - to be empty. Ignore the fact that our front-end doesn\u0026rsquo;t allow empty, that doesn\u0026rsquo;t matter this far down as we cannot know what the GUI is doing. This is now a database rule and needs to be enforced here. So getAccount() now returns an Optional, and it\u0026rsquo;s up to saveNewCustomer() to deal with the Optional.isEmpty() situation - which is to throw an exception and refuse to save the record.\nChecked vs Unchecked Exceptions # A little diversion here to explain why only one of these exceptions is checked\u0026hellip;\nChecked exceptions are used when there\u0026rsquo;s a possibility of a method failing for unavoidable reasons that can occur at runtime. In this case, we have a database that has no retrieve function, so there\u0026rsquo;s no way for our DAO to check that the record isn\u0026rsquo;t on file before attempting to save it.\nBut even if we did have a retrieve function there, it still might be reasonable to have a duplicate record. Some other user, or some other process or thread might sneak in and create the record in between our call to do a test retrieve and our call to save the record. It\u0026rsquo;s extremely unlikely, but it\u0026rsquo;s not impossible, and that means we have to have some way to deal with it in our code.\nOn the other hand, the requirement to have an \u0026ldquo;account_number\u0026rdquo; field in our new customer record is something that the DAO can check before calling CustomerDatabase.saveNewCustomer(). We need to check for it in our database because allowing this to happen would corrupt our database, but it should never occur in production because the DAO should prevent it from happening.\nChecked Exception An issue that cannot be avoided by writing good code, but must be handled by the code when it occurs in production. Unchecked Exception An issue that needs to guarded against but can absolutely be avoided by writing good code in the calling method. An unchecked exception usually indicates a program bug. Testing the Database # This is going to add a whole new set of testing requirements to our database. So clean-up and reorganization of the test class has been done, as well as to add some new test cases:\nclass CustomerDatabaseTest { CustomerDatabase dataBase; @BeforeEach void init() { dataBase = new CustomerDatabase(); } @Test void saveCustomerIdIncrementTest() { assertEquals(1, dataBase.saveCustomer(createFred()), \u0026#34;Id is incremented\u0026#34;); } @Test void saveCustomerIdIncrementTwiceTest() { dataBase.saveCustomer(createFred()); assertEquals(2, dataBase.saveCustomer(createGeorge()), \u0026#34;Id is incremented\u0026#34;); } @Test void saveCustomerIdInsertionTest() { dataBase.saveCustomer(createFred()); assertEquals(\u0026#34;1\u0026#34;, dataBase.getData().get(1).get(\u0026#34;_id\u0026#34;), \u0026#34;Id is inserted\u0026#34;); } @Test void saveCustomerRecordCorrectTest() { dataBase.saveCustomer(createFred()); assertEquals(\u0026#34;Fred\u0026#34;, dataBase.getData().get(1).get(\u0026#34;name\u0026#34;), \u0026#34;Id is inserted\u0026#34;); } @Test void findAccountTest_Found() { dataBase.saveCustomer(createFred()); dataBase.saveCustomer(createGeorge()); assertTrue(dataBase.isAccountOnFile(\u0026#34;123\u0026#34;), \u0026#34;Lookup Fred\u0026#34;); } @Test void findAccountTest_NotFound() { dataBase.saveCustomer(createFred()); dataBase.saveCustomer(createGeorge()); assertFalse(dataBase.isAccountOnFile(\u0026#34;7777\u0026#34;), \u0026#34;Lookup someone not present\u0026#34;); } @Test void saveWithoutAccountNumber() { HashMap\u0026lt;String, String\u0026gt; customerRecord = new HashMap\u0026lt;\u0026gt;(); customerRecord.put(\u0026#34;name\u0026#34;, \u0026#34;Fred\u0026#34;); assertThrows(IllegalArgumentException.class, () -\u0026gt; dataBase.saveNewCustomer(customerRecord)); } @Test void saveRecordTwiceTest() { dataBase.saveCustomer(createFred()); assertThrows(CustomerAlreadyOnFileException.class, () -\u0026gt; dataBase.saveNewCustomer(createFred())); } private Map\u0026lt;String, String\u0026gt; createFred() { HashMap\u0026lt;String, String\u0026gt; customerRecord = new HashMap\u0026lt;\u0026gt;(); customerRecord.put(\u0026#34;name\u0026#34;, \u0026#34;Fred\u0026#34;); customerRecord.put(\u0026#34;account_number\u0026#34;, \u0026#34;123\u0026#34;); return customerRecord; } private Map\u0026lt;String, String\u0026gt; createGeorge() { HashMap\u0026lt;String, String\u0026gt; customerRecord = new HashMap\u0026lt;\u0026gt;(); customerRecord.put(\u0026#34;name\u0026#34;, \u0026#34;George\u0026#34;); customerRecord.put(\u0026#34;account_number\u0026#34;, \u0026#34;567\u0026#34;); return customerRecord; } } Added here are some tests for isAccountOnFile(), as well as for the two exceptions that might be thrown.\nHandling the Exception # The situation where you have a method that calls another method that, in turn, calls another method, and so on - and the method all the way at the bottom throws a checked exception happens a lot. Your code needs to work backwards through the method chain, deciding what to do with exception in each method. There are three options available in each method. Here they are, in order of preference:\nHandle it Transform it into a more meaningful exception and throw that. Throw it to the calling method. Obviously, if a method can effectively deal with an exception, then that\u0026rsquo;s end of it and the method that calls it need not know that it happened. If your method can\u0026rsquo;t deal with it, then it\u0026rsquo;s possible that your method can add better context to what has happened, in which case it should throw a new exception.\nOtherwise, just specify that the method can throw that exception type in it\u0026rsquo;s signature and let it move on up to the previous method in the call chain.\nLet\u0026rsquo;s look at the DAO and the Broker.\nThere\u0026rsquo;s nothing the DAO can do about this error, and there\u0026rsquo;s no context that the DAO can meaningfully add to the error, either. So that DAO should just let it propagate up to the calling method in the Broker.\nThe Broker can\u0026rsquo;t do anything with it either. Once again, there\u0026rsquo;s no context that it can add. So it just throws it again and it goes to the Interactor.\nHandling the Exception in the Interactor # The Interactor is the lowest class that can actually do something about this error, as deciding what should happen next is part of the business logic. So, what should it do?\nThe main thing that needs to happen is that we notify the user that it couldn\u0026rsquo;t be saved and tell them why. This is something that the Interactor cannot do by itself. We\u0026rsquo;ll need to get the Controller involved. We also need to transform this exception into some sort of data that we can communicate back to the Controller.\nWe only have two possible results: the save worked, or the saved failed because of a duplicate account. This means we can get away with a simple Boolean result.\nHere\u0026rsquo;s the new Interactor code:\npublic boolean saveCustomer() { String customerName = model.getCustomerName(); String account = model.getAccountNumber(); try { int recordId = broker.saveCustomer(createCustomerFromModel()); System.out.println(\u0026#34;Saving account: \u0026#34; + account + \u0026#34; Name: \u0026#34; + customerName + \u0026#34; Result: \u0026#34; + recordId); return true; } catch (CustomerAlreadyOnFileException e) { return false; } } We\u0026rsquo;ve changed the return value from void to boolean. Then we return true unless we catch the CustomerAlreadyOnFileException where we return false.\nCommunicating With the User # The Controller is going to be responsible for making sure that the user is told about this issue. We\u0026rsquo;re going to use an Alert to do this.\nWe need to transform our Task from one that returns Void to one the returns Boolean. Then we can test for this in our onSuccess EventHandler. Task always completes with success unless its call generates an uncaught exception. Since we\u0026rsquo;re catching the exception in the Interactor it will still complete successfully, even though the save itself failed.\nHere\u0026rsquo;s what our method looks like now:\nprivate void saveCustomer(Runnable postTaskGuiActions) { Task\u0026lt;Boolean\u0026gt; saveTask = new Task\u0026lt;\u0026gt;() { @Override protected Boolean call() { return interactor.saveCustomer(); } }; saveTask.setOnSucceeded(evt -\u0026gt; {Alert postTaskGuiActions.run(); if (!saveTask.getValue()) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setContentText(\u0026#34;This customer is already on file, cannot save.\u0026#34;); alert.show(); } }); Thread saveThread = new Thread(saveTask); saveThread.start(); } And this is what it looks like:\nWe still need to run the postTaskGuiActions, but now we need to check the results of the Task\u0026rsquo;s call() method to see if the save was successful. If it wasn\u0026rsquo;t then we\u0026rsquo;ll show an Alert with an appropriate message.\n","externalUrl":null,"permalink":"/beginners/part10/","section":"JavaFX","summary":"The last problem with our application is that it can save duplicate customer records, corrupting our database.  This means adding some rules to our database and telling our GUI when those rules have been broken.  We’ll see how to handle exceptions from the back-end in our application.","title":"Part 10 - Handling Database Errors","type":"javafx"},{"content":" Reviewing What We\u0026rsquo;ve Done So Far # At this point we\u0026rsquo;ve actually got a simple, but fully functional \u0026ldquo;Create\u0026rdquo; application. It doesn\u0026rsquo;t have anything more than an account number and a name, but to get this far we had to put together a lot of moving parts.\nThis is probably a good time to pause, and look at what we\u0026rsquo;ve done.\nWe\u0026rsquo;ve Built on a Framework # If you\u0026rsquo;ve followed along this far, then the value of building on a framework should be readily apparent.\nHere are some of the things it does for us:\nWe Can Use Unit Tests Because we now have a pretty clear dividing line between the Reactive JavaFX code and the rest of our application, it\u0026rsquo;s now clear that some of our code is just plain old Java code and there\u0026rsquo;s nothing special about it. That means that we can test it, and in turn that means that we should test it. Our GUI is Just the GUI There\u0026rsquo;s no business logic in our GUI, it\u0026rsquo;s just layout. Coupling is Minimized Within the framework, there\u0026rsquo;s no dependency between the View and the Interactor. They both deal with the Model, but don\u0026rsquo;t even know of the existence of each other. Within our View, the various screen widgets have virtually no knowledge of one another. Everything is Easy to Find There\u0026rsquo;s no need to go looking for code. You won\u0026rsquo;t find business rules in the View or the Model, they\u0026rsquo;re all in the Interactor. Everything is Small No 1000 line classes here. Keeping everything broken up into discrete parts means that all of the parts stay small. Other Important Ideas # Reactive Programming Through this processes, we\u0026rsquo;ve seen how Reactive programming concepts make it easier to separate our components without having to worry about how they are going to be used. Having a synchronized data object holding our application state enables this. Background Threads We\u0026rsquo;ve learned about the importance of keeping blocking operations off the FXAT, and how to use Task to start up a process that runs on a background thread. We\u0026rsquo;ve also learned how to set up our GUI before a background task starts, and how to trigger the GUI to respond to the completion of a background task. Single Responsibility Principle We looked at how a method should only be directly responsible for a single thing and how, by delegating to other methods our code stays simple and easy to read. Don\u0026rsquo;t Repeat Yourself (DRY) We\u0026rsquo;ve seen that this is the most important principle for dealing with the boilerplate code that bog down a JavaFX layout. As we go on, we\u0026rsquo;ll see this become even more important. GUI Validation We\u0026rsquo;ve applied validation to our GUI to enforce business rules built by the Interactor and embedded into the Model. Simulated Database and Access We\u0026rsquo;ve seen how to simulate a database and connect it to our Interactor through a Broker and a DAO. We\u0026rsquo;ve dealt with the issues that latency can have when testing these components. Handling Exceptions We\u0026rsquo;ve learned about the difference between checked and unchecked exceptions and how to deal with checked exceptions that happen deep down a chain of method calls. We learned how to transform an exceptions into an action result, and integrate it into our UX. Moving On From Here # Our next steps are going to build on what we\u0026rsquo;ve done so far:\nAdd Another Field We\u0026rsquo;ll add a \u0026ldquo;Phone Number\u0026rdquo; field to the screen so that we can see how the feature build cuts all the way through the application from GUI to the database. Clean Up the GUI It\u0026rsquo;s going to become clear that our GUI is a bit ugly when we add another field. In the process of cleaning up the GUI, we\u0026rsquo;ll see how our layout has become cluttered with stuff that isn\u0026rsquo;t strictly\u0026hellip;layout. We\u0026rsquo;ll clean that up and see how an even nicer looking GUI can actually take up much less code in our ViewBuilder. Split the Name We\u0026rsquo;ll divide the \u0026ldquo;name\u0026rdquo; field up into three parts including a \u0026ldquo;salutation\u0026rdquo; field that will let us use a ComboBox. Add the Rest of the Fields We\u0026rsquo;ll add a bunch of address fields to our screen, and then down through to our database. Add a \u0026ldquo;Retrieve\u0026rdquo; Function Finally, we\u0026rsquo;ll be ready to add the next CRUD function, \u0026ldquo;Retrieve\u0026rdquo;. Without \u0026ldquo;Retrieve\u0026rdquo; we can\u0026rsquo;t do \u0026ldquo;Update\u0026rdquo; or \u0026ldquo;Delete\u0026rdquo; ","externalUrl":null,"permalink":"/beginners/part11/","section":"JavaFX","summary":"At this point we have a pretty clean “Create” application.  It’s error resistant, doesn’t corrupt our database and ready to add some more functionality.  Let’s look at what we’ve done so far.","title":"Part 11 - A \"Working\" Application","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # How easy it is to expand the application by a single field Some more DRY How to keeping code clean as we expand functionality Adding a Field # At this point we\u0026rsquo;re going to add a single field to our screen. We\u0026rsquo;re going to do just one field because that will clearly show the process for extending the scope of our application, in this respect, down through the entire application. Later on, we\u0026rsquo;ll add a bunch of fields at once, but it will be virtually the same operation, but just with more than one field.\nAs we do this, we\u0026rsquo;re going to see the potential for this process to corrupt our nice clean code. We vigilant about how our code follows the two key priciples: Dry and the Single Responsibility Principle.\nStart with the Data # We have three kinds of data in our application:\nThe Model - which represents State Customer - which is a \u0026ldquo;Domain Object\u0026rdquo; Database record - which is the external data format of our database Since our database records are essentially free-form, there won\u0026rsquo;t be any need to make any changes there, nor will we need to write any new tests at that layer of our application.\nLet\u0026rsquo;s look at the new Model:\npublic class CustomerModel { private final StringProperty accountNumber = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty customerName = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty email = new SimpleStringProperty(\u0026#34;\u0026#34;); private final BooleanProperty okToSave = new SimpleBooleanProperty(false); } and now, the Customer:\npublic class Customer { private String accountNumber = \u0026#34;\u0026#34;; private String name = \u0026#34;\u0026#34;; private String email = \u0026#34;\u0026#34;; } All the getters and setters have been left out - you should know how that stuff works.\nThese changes are pretty trivial, we\u0026rsquo;ve simply added a new String-based field to each class.\nThe Broker # The job of the Broker is to turn the Customer object into a database record. So we need to account for the new field:\nMap\u0026lt;String, String\u0026gt; createCustomerRecord(Customer customer) { Map\u0026lt;String, String\u0026gt; customerRecord = new HashMap\u0026lt;\u0026gt;(); customerRecord.put(\u0026#34;name\u0026#34;, customer.getName()); customerRecord.put(\u0026#34;account_number\u0026#34;, customer.getAccountNumber()); customerRecord.put(\u0026#34;email\u0026#34;, customer.getEmail()); return customerRecord; } Of course, new functionality means a new test:\nclass CustomerBrokerTest { @Test void createCustomerRecord_AccountNumberTest() { CustomerBroker broker = new CustomerBroker(); Customer customer = createCustomer(); assertEquals(\u0026#34;1234\u0026#34;, broker.createCustomerRecord(customer).get(\u0026#34;account_number\u0026#34;), \u0026#34;Account number check\u0026#34;); } @Test void createCustomerRecord_CustomerNameTest() { CustomerBroker broker = new CustomerBroker(); Customer customer = createCustomer(); assertEquals(\u0026#34;Fred\u0026#34;, broker.createCustomerRecord(customer).get(\u0026#34;name\u0026#34;), \u0026#34;Customer name check\u0026#34;); } @Test void createCustomerRecord_EmailTest() { CustomerBroker broker = new CustomerBroker(); Customer customer = createCustomer(); assertEquals(\u0026#34;abc@def.com\u0026#34;, broker.createCustomerRecord(customer).get(\u0026#34;email\u0026#34;), \u0026#34;Customer email check\u0026#34;); } private Customer createCustomer() { Customer customer = new Customer(); customer.setAccountNumber(\u0026#34;1234\u0026#34;); customer.setName(\u0026#34;Fred\u0026#34;); customer.setEmail(\u0026#34;abc@def.com\u0026#34;); return customer; } } It\u0026rsquo;s become obvious that we were getting into some repeated code here, and it was only going to get worse as we added more fields. So DRY kicks in and we\u0026rsquo;ve put the repeated code into a method, createCustomer(). Other than that, we\u0026rsquo;ve just added a single test case to handle the email address.\nThe Interactor # This change affects the Interactor in one spot - when it creates the Customer from the Model. So we\u0026rsquo;ll update that method and then add a test case:\nCustomer createCustomerFromModel() { Customer customer = new Customer(); customer.setAccountNumber(model.getAccountNumber()); customer.setName(model.getCustomerName()); customer.setEmail(model.getEmail()); return customer; } and the test class:\nclass CustomerInteractorTest { @Test void createCustomer_NameTest() { CustomerModel model = createCustomerModel(); CustomerInteractor interactor = new CustomerInteractor(model); assertEquals(\u0026#34;Fred\u0026#34;, interactor.createCustomerFromModel().getName(), \u0026#34;Check customer name\u0026#34;); } @Test void createCustomer_AccountTest() { CustomerModel model = createCustomerModel(); CustomerInteractor interactor = new CustomerInteractor(model); assertEquals(\u0026#34;ABCDE\u0026#34;, interactor.createCustomerFromModel().getAccountNumber(), \u0026#34;Check customer name\u0026#34;); } @Test void createCustomer_EmailTest() { CustomerModel model = createCustomerModel(); CustomerInteractor interactor = new CustomerInteractor(model); assertEquals(\u0026#34;abc@def.com\u0026#34;, interactor.createCustomerFromModel().getEmail(), \u0026#34;Check customer email\u0026#34;); } private CustomerModel createCustomerModel() { CustomerModel model = new CustomerModel(); model.setCustomerName(\u0026#34;Fred\u0026#34;); model.setAccountNumber(\u0026#34;ABCDE\u0026#34;); model.setEmail(\u0026#34;abc@def.com\u0026#34;); return model; } } You\u0026rsquo;ll see that we\u0026rsquo;ve applied DRY again here, to keep our code as simple as possible.\nFinally, The View # At this point we have a back-end that will handle our new field; it\u0026rsquo;s tested and ready to go. All we need to do now is to add it to our View:\nprivate Node createCentre() { VBox results = new VBox(6, accountBox(), nameBox(), emailBox()); results.setPadding(new Insets(20)); return results; } private Node accountBox() { return new HBox(6, promptLabel(\u0026#34;Account #:\u0026#34;), boundTextField(model.accountNumberProperty())); } private Node nameBox() { return new HBox(6, promptLabel(\u0026#34;Name:\u0026#34;), boundTextField(model.customerNameProperty())); } private Node emailBox() { return new HBox(6, promptLabel(\u0026#34;eMail:\u0026#34;), boundTextField(model.emailProperty())); } Do you see how simple that is? We just add a new builder method to create the HBox, then call it from the constructor of the Centre\u0026rsquo;s VBox.\nHowever, we do have some repeated code that\u0026rsquo;s starting to get ugly. This is a little bit harder to see, since we\u0026rsquo;ve already applied DRY to pull the HBox creation out into separate methods. But all three of these methods are pretty much the same, we just put in different strings and Properties. So let\u0026rsquo;s fix that:\nLet\u0026rsquo;s try it:\nprivate Node createCentre() { VBox results = new VBox(6, accountBox(), nameBox(), emailBox()); results.setPadding(new Insets(20)); return results; } private Node accountBox() { return rowBox(\u0026#34;Account #:\u0026#34;, model.accountNumberProperty()); } private Node nameBox() { return rowBox(\u0026#34;Name:\u0026#34;, model.customerNameProperty()); } private Node emailBox() { return rowBox(\u0026#34;eMail:\u0026#34;, model.emailProperty()); } private Node rowBox(String prompt, StringProperty boundProperty) { return new HBox(6, promptLabel(prompt), boundTextField(boundProperty)); } That\u0026rsquo;s removed the repeated code, as much as we can. We could stop at this point, but it\u0026rsquo;s probably better to refactor out those three methods since they probably don\u0026rsquo;t add much value any more.\nThe question here is: Which is more clear? Having methods with names that describe what they are doing, or having less code? Personally, I lean towards less code, following the idea that, \u0026ldquo;less code is better code.\u0026rdquo;\nLet\u0026rsquo;s see what it looks like:\nprivate Node createCentre() { VBox results = new VBox(6, rowBox(\u0026#34;Account #:\u0026#34;, model.accountNumberProperty()), rowBox(\u0026#34;Name:\u0026#34;, model.customerNameProperty()), rowBox(\u0026#34;eMail:\u0026#34;, model.emailProperty())); results.setPadding(new Insets(20)); return results; } private Node rowBox(String prompt, StringProperty boundProperty) { return new HBox(6, promptLabel(prompt), boundTextField(boundProperty)); } Honestly, I think this is clearer. The prompt text is what you\u0026rsquo;ll see on the screen, so it\u0026rsquo;s what you\u0026rsquo;d be looking for in the code. The method names are OK, but this is much more compact, and the intent of the code is obvious. If you want to know the gory details about rowBox(), then go look at it. But you probably won\u0026rsquo;t.\nAnd if there were 30 rows of prompt/TextFields, this code would still be clear. You can also see that adding a new field just adds another parameter to the VBox constructor. We\u0026rsquo;ll stick with this.\nWrapping it Up # At this point it should be clear that adding a new field, or a dozen new fields, is just a matter of adding a row to the GUI and then following the data down to the Broker. It\u0026rsquo;s rinse and repeat, \u0026ldquo;paint by numbers\u0026rdquo; for each field.\nWhat\u0026rsquo;s far more important is that you keep your code clean as you do this. Things that seemed reasonable with just one or two fields become obviously bad as you add more and more to your application. If not actually \u0026ldquo;bad\u0026rdquo;, then certainly \u0026ldquo;sub-optimal\u0026rdquo;. Most of the things we fixed in this lesson were already issues with the code; things that, to be honest, we should have already fixed. They really jumped out as problems once we added that third field.\nYou need to constantly take a step back and look at your code with a critical eye and make sure that you\u0026rsquo;re keeping on top of emerging issues. Make sure that you\u0026rsquo;re following DRY and the Single Responsibility Principle. Make sure that in those cases where you are not, that it\u0026rsquo;s because you think the code is cleaner in its current state.\nAnd don\u0026rsquo;t forget, as we\u0026rsquo;ve seen here, all of this goes for your JUnit test classes as well.\n","externalUrl":null,"permalink":"/beginners/part12/","section":"JavaFX","summary":"It’s finally time to add a third field to our screen!  In this article we’re going to add just a single new field, for email, to our screen.  We’re going to see how the process goes from the View all the way down to the Broker.  Most importantly, we going to see how we need to be vigilant in ensuring that our code stays clean as we add more features.","title":"Part 12 - Adding a New Field: eMail","type":"javafx"},{"content":" What You\u0026rsquo;ll Learn # The idea of Custom Widgets as View building blocks What is Feature Envy The principle of \u0026ldquo;Put Stuff Where it Belongs\u0026rdquo; Building a Widgets library How much cleaner code is when Feature Envy is dealt with Introduction # Even though we\u0026rsquo;ve been really careful to try to apply DRY and the Single Responsibility Principle, our ViewBuilder code has started to grow some weeds. It\u0026rsquo;s not as clean as it can be, and it now suffers from \u0026ldquo;Feature Envy\u0026rdquo;. Let\u0026rsquo;s look at what\u0026rsquo;s happened\u0026hellip;\nCustom Widgets # When I build screens I tend to think about them in terms of smaller pieces as building blocks, and that these smaller pieces are often complete, self-contained pieces of functionality. I think of them as \u0026ldquo;Custom Widgets\u0026rdquo;. I like the term \u0026ldquo;Widgets\u0026rdquo; better than \u0026ldquo;Nodes\u0026rdquo;, because the term \u0026ldquo;Node\u0026rdquo; has a specific definition as a Class type in JavaFX. \u0026ldquo;Widget\u0026rdquo;, however, is a generic term commonly used in talking about GUI\u0026rsquo;s and it simply refers to a screen element that interacts with the user. So, a widget can be just about anything on the screen, but a Node refers to a JavaFX class.\nA \u0026ldquo;Custom Widget\u0026rdquo; is something that somebody, probably you, has built, styled and configured. It can be, but usually isn\u0026rsquo;t, an extension of a Node class, and can be a container class with other widgets inside it.\nLet\u0026rsquo;s take a look at a piece code from our GUI:\nprivate Node rowBox(String prompt, StringProperty boundProperty) { return new HBox(6, promptLabel(prompt), boundTextField(boundProperty)); } The thing that this method returns is a custom widget. It\u0026rsquo;s an HBox, but it\u0026rsquo;s been configured and populated with a Label and a TextField. But it\u0026rsquo;s not treated as an HBox. It\u0026rsquo;s really just a generic widget that performs a function and that is placed into a layout. That\u0026rsquo;s why it\u0026rsquo;s returned as Node.\nNow, what does this widget do? Well\u0026hellip;it puts a prompt Label on the screen and a TextField beside it. The name \u0026ldquo;rowBox()\u0026rdquo; implies that it\u0026rsquo;s going to be oriented horizontally - in a row.\nThink about these questions:\nOnce we\u0026rsquo;ve instantiated it, do we care that it has a Label in it? No, not really. We care that there\u0026rsquo;s something on the screen that indicates whatever our prompt parameter specifies, but we don\u0026rsquo;t really care that it\u0026rsquo;s a Label. It could be a Text, or it could be an ImageView, or graphic of some sort. It doesn\u0026rsquo;t need to be a Label. We don\u0026rsquo;t care. What about the TextField? We don\u0026rsquo;t really care about that either. It could be any kind of control that allows user input that can be bound to our boundProperty parameter. Maybe TextArea, or some custom input control we downloaded in a library. As long as we get user input, we don\u0026rsquo;t really care. The important point is that everything that rowBox() needs to do it\u0026rsquo;s job is passed to rowBox() as a parameter. When we get the result back from rowBox(), we don\u0026rsquo;t care about any aspect of it except that we can put it in our layout. It\u0026rsquo;s self-contained. We can treat it as a fairly anonymous custom widget, that does its job.\nFeature Envy # \u0026ldquo;Feature Envy\u0026rdquo; is when a component (usually a class) takes on functionality which doesn\u0026rsquo;t really belong to it. It grows more features so it can be more important!\nWe want to avoid Feature Envy as much as possible.\nFeature Envy is the opposite of the principle of, \u0026ldquo;Put Stuff Where it Belongs\u0026rdquo;. And this idea itself is really is just a natural outcome of following the Single Responsibility Principle, and DRY.\nLet\u0026rsquo;s take a look at another method in our ViewBuilder to see how this works:\nprivate Node boundTextField(StringProperty boundProperty) { TextField textField = new TextField(); textField.textProperty().bindBidirectional(boundProperty); return textField; } We added this method to stick to DRY and to avoid repeating the code inside the method. But let\u0026rsquo;s take a closer look at it.\nFirst of all, the return value is essentially another Custom Widget. It\u0026rsquo;s a bound TextField.\nWhat does this code have to do with our layout, or our ViewBuilder class? Nothing much other than the fact that it\u0026rsquo;s called from it. It doesn\u0026rsquo;t use any fields from the class, nor does it call any other methods in the class. In fact, it could be somewhere else and it wouldn\u0026rsquo;t matter. That\u0026rsquo;s a problem, because stuff should only be in a class because it really needs to be there.\nLet\u0026rsquo;s look at it from the perspective of DRY. In any normal business application, you\u0026rsquo;re likely to have many screens, and each and every one of them is likely to have TextFields that are bidirectionally bound to StringProperties. Are you going to repeat this method in every one of those ViewBuilders? And is it going to be exactly the same code in each one? Yes, it would be if you did it, and this would be a clear violation of DRY - on an application level.\nNow let\u0026rsquo;s look at it from the perspective of the Single Responsibility Principle, but at the level of our ViewBuilder class. It\u0026rsquo;s only direct responsibility is to build and configure the View layout. This method handles a detail outside of that responsibility, and should be delegated to somewhere else.\nFinally - and this is really telling - this method is only called from rowBox() which itself is another method that has nothing to do with our layout.\nLet\u0026rsquo;s Put These General Purpose Custom Widgets Somewhere Else # When something doesn\u0026rsquo;t belong somewhere, then you have to put it someplace else. Here we are talking about fairly generic methods; ones that accept plain data and standard JavaFX properties as parameters, and return configured standard JavaFX Nodes. None of it has anything to do with our application directly. So, \u0026ldquo;someplace else\u0026rdquo;, means some other class in some other package.\nI\u0026rsquo;ve created a new class with static methods called Widgets. I\u0026rsquo;m putting it in a package called \u0026ldquo;widgets\u0026rdquo;, and it looks like this:\npublic class Widgets { public static Node promptLabel(String contents) { return styledLabel(contents, \u0026#34;prompt-label\u0026#34;); } public static Node headingLabel(String contents) { return styledLabel(contents, \u0026#34;heading-label\u0026#34;); } public static Node styledLabel(String contents, String styleClass) { Label label = new Label(contents); label.getStyleClass().add(styleClass); return label; } public static Node promptedTextField(String prompt, StringProperty boundProperty) { return new HBox(6, promptLabel(prompt), boundTextField(boundProperty)); } public static Node boundTextField(StringProperty boundProperty) { TextField textField = new TextField(); textField.textProperty().bindBidirectional(boundProperty); return textField; } } It has all of the Feature Envy methods from our ViewBuilder class. The name rowBox() has been changed to promptedTextField() because it needs to be more easily understood without any context. Maybe promptedInput() would be better? I don\u0026rsquo;t know. Naming things is hard!\nOn the subject of naming things: For these kinds of methods I prefer names that describe the returned value, not an action type name. The normal rules of naming would have use names like createHeadingLabel() or makeStyledLabel(), but these don\u0026rsquo;t really add any clarity in the context of a Widget library. Furthermore, they just make the names longer, which then makes them more cumbersome to use in constructors for container classes.\nSo I prefer to stick to names like Widgets.headingLabel() and to think of the value that it returns as HeadingLabel - even though it\u0026rsquo;s not an actual class that is returned. This makes sense to me, so it\u0026rsquo;s the convention I follow.\nAll of this stuff is now out of our layout code:\npublic class CustomerViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final CustomerModel model; private final Consumer\u0026lt;Runnable\u0026gt; saveHandler; public CustomerViewBuilder(CustomerModel model, Consumer\u0026lt;Runnable\u0026gt; saveHandler) { this.model = model; this.saveHandler = saveHandler; } @Override public Region build() { BorderPane results = new BorderPane(); results.getStylesheets().add(Objects.requireNonNull(this.getClass().getResource(\u0026#34;/css/customer.css\u0026#34;)).toExternalForm()); results.setTop(Widgets.headingLabel(\u0026#34;Customer Information\u0026#34;)); results.setCenter(createCentre()); results.setBottom(createButtons()); return results; } private Node createCentre() { VBox results = new VBox(6, Widgets.promptedTextField(\u0026#34;Account #:\u0026#34;, model.accountNumberProperty()), Widgets.promptedTextField(\u0026#34;Name:\u0026#34;, model.customerNameProperty()), Widgets.promptedTextField(\u0026#34;eMail:\u0026#34;, model.emailProperty())); results.setPadding(new Insets(20)); return results; } private Node createButtons() { Button saveButton = new Button(\u0026#34;Save\u0026#34;); saveButton.disableProperty().bind(model.okToSaveProperty().not()); saveButton.setOnAction(evt -\u0026gt; { saveButton.disableProperty().unbind(); saveButton.setDisable(true); saveHandler.accept(() -\u0026gt; saveButton.disableProperty().bind(model.okToSaveProperty().not())); }); HBox results = new HBox(10, saveButton); results.setAlignment(Pos.CENTER_RIGHT); return results; } } Once you take it out, you realize how much those Feature Envy methods were messing up the layout code. You can take it in at a glance now. In comparison, the previous version of our ViewBuilder was cluttered.\nOne Last Point # Imagine for a minute that JavaFX came equipped with - straight out of the box - a Node class that did exactly what promptedTextField() does. Let\u0026rsquo;s call it PromptedTextField, and it had a constructor that took a String and a StringProperty.\nWould you go and look at the source code for it to see how it works, or to check if it was working properly?\nNo, you wouldn\u0026rsquo;t. I might, but you wouldn\u0026rsquo;t. You\u0026rsquo;d use it and you\u0026rsquo;d be glad it was there and assume that it was going to work properly.\nWidgets is really nothing different. It\u0026rsquo;s a set of utilities that are so generic that you\u0026rsquo;ll use them over, and over, and over again. And, because you use them all the time, you know that they will work properly. Most of them, just like the ones here, are so simple that there\u0026rsquo;s no reason to worry about them not working properly.\nBut they shift a huge burden of detail out of your layout code. And they shift a huge burden out of your mind when you\u0026rsquo;re working with your layout code. That\u0026rsquo;s a huge win.\n","externalUrl":null,"permalink":"/beginners/part13/","section":"JavaFX","summary":"In this article we’re going to look at how Feature Envy has crept into our View code.  We’re going to look at why this happens, and how to deal with it.  Finally, we’re going to see how clearing out Feature Envy makes our code much cleaner and easier to understand.","title":"Part 13 - Feature Envy","type":"javafx"},{"content":"","date":"4 November 2026","externalUrl":null,"permalink":"/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"","date":"4 November 2026","externalUrl":null,"permalink":"/categories/homelab/","section":"Categories","summary":"","title":"Homelab","type":"categories"},{"content":" Introduction # When you establish a homelab and start to use it to replace services that you previously rented from cloud providers, you have to wrestle with the concept of security. To a degree, you were able to rely on those cloud providers to provide all of the safeguards that you needed, but when you\u0026rsquo;re hosting those services yourself, you have to do the security yourself too.\nTo be sure, security isn\u0026rsquo;t sexy, and usually not a lot of fun either. But it\u0026rsquo;s simply not something you can put aside.\nThis article is absolutely not a \u0026ldquo;how to\u0026rdquo; kind of article at all. Rather, I\u0026rsquo;m trying to explain the concepts that you need to understand in order to make intelligent decisions about your own homelab security.\nThis means that we have to start with the basics. Basics that are more about the network traffic itself before we can even start to talk about security.\nThe Illusion of Connectivity # The first and most important thing to understand about how the Internet works is that you don\u0026rsquo;t actually \u0026ldquo;connect\u0026rdquo; to anything.\nI know that when you log into a website like YouTube and watch a video, it really feels like you\u0026rsquo;ve established a concrete connection to YouTube and the video stream runs down that connection. It makes the Internet feel like it works like an old-time switchboard, with virtual cables connecting you to remote sites.\nBut that is not how the Internet and network communications work. You are better off thinking about it as a really, really high speed postal system that delivers postcards.\nAll network traffic is divided into something called \u0026ldquo;packets\u0026rdquo;. Each packet has an address and a data payload. Just like the postcard, it\u0026rsquo;s all just written there for everyone to see.\nYou drop off your postcard in the mailbox and it\u0026rsquo;s picked up and sent to some central station. It\u0026rsquo;s sorted and they figure out where to send it next, maybe it\u0026rsquo;s another, bigger, central station, or maybe it\u0026rsquo;s handed off to the postal service in another country. It goes through as many hops as it needs, with each handler examining the address and deciding where it needs to be sent next in order to get it to the correct address.\nAnd the Internet works exactly the same way. The data that you want to send is carved up into packets roughly 1500 bytes long. Each packet contains a header that has the destination address in it. Your computer has information about how to send packets to various places, and it figures out what computer to pass it to based on that. That computer, in turn, has its own rules about how it routes packets, and it will send it to the next computer on the way to each packet\u0026rsquo;s destination. It goes on and on like that until it gets to the destination.\nYou may have noticed that anybody in mail delivery system can read that postcard. The same is true of Internet traffic. This is why most websites now use SSL to encrypt traffic. Now your postcard looks like this:\nThe address is still in plain text, because the delivery system needs to be able to read that. But the private stuff in the content can now only be read by the intended recipient.\nWhat About Connections? # When you watch a video on YouTube, you actually start a pen-pal type relationship with YouTube.\nThis is how YouTube sends you the data for the stream:\nThe first data in the stream is carved up into ~1500 byte chunks and put into packets. The data for each chunk contains some kind of sequencing information, because there is no guarantee that chunk #1000 is going to be delivered before chunk #1001, or even chunk #1200. As the data comes in to your video app or browser, it reassembles the chunks back into a stream of data in the correct order. From time to time, it will send a message back to YouTube that says something like \u0026ldquo;I\u0026rsquo;ve received frame 2,876,983 of video XYZ, send me more.\u0026rdquo;. And then YouTube will figure out the next amount of video to send, carve it into those ~1500 byte chunks and send it out.\nTo you, watching the video, it feels like you have an active connection right back to YouTube. But you don\u0026rsquo;t. Shut off your app or browser without stopping the video and nothing special happens. It\u0026rsquo;s not like the video data keeps streaming down a cable and dribbling out into your computer. What does happen is that your computer never sends any more of those \u0026ldquo;\u0026hellip;send me more.\u0026rdquo; messages. So YouTube never sends any more data.\nBehind the scenes, everything is handled to make it seem like a connection. If a chunk gets lost on the way, then there\u0026rsquo;s a protocol to allow your computer to ask for it to be resent.\nBut it\u0026rsquo;s still not a connection, it\u0026rsquo;s all individual packets of data.\nStateless Web Servers # Most web servers these days are designed to be \u0026ldquo;Stateless\u0026rdquo;, and this is the final nail in the coffin for the idea of Internet connections.\n\u0026ldquo;Stateless\u0026rdquo; means that the web server views every single request that it receives as a unique entity, unconnected to requests that have come before or the ones that will come in the future. There\u0026rsquo;s no idea of, \u0026ldquo;Oh! You were just here. I remember what we were doing.\u0026rdquo; Everything that your request needs in order for the web server to answer it needs to be included in your request. Everything.\nIf your new request depends on the fact that you\u0026rsquo;ve logged into the web server, then your request needs to be able to tell the web server that. This usually means that the web server gives your browser all the information that it will need for future requests when it processes your login attempt. And this needs to be done for every page it sends back thereafter.\nIn other words, every single page sent from the server contains all of the information that your browser needs in order to make further requests. The web server isn\u0026rsquo;t keeping track of it for you.\nWhy Does this Matter? # If you picture the Internet as a bunch of connections, the way that most people do, you\u0026rsquo;ll never understand how Internet security and routing work.\nOnce you can picture how the Internet is just chunks of data flying around in every conceivable direction, each one complete within itself, you can see how each individual routing and security decision can be made just by looking at that single packet.\nVPNs and Tunnels # You\u0026rsquo;ve undoubtedly heard of VPNs, and you\u0026rsquo;ve probably heard of them refered to as \u0026ldquo;Encrypted Tunnels\u0026rdquo;. What does that mean?\nLet\u0026rsquo;s go back to our postcard analogy. You\u0026rsquo;ve encrypted the text of your message, but what if you don\u0026rsquo;t want anyone to know who you\u0026rsquo;re corresponding with? One way to get around this is to put your postcard into a envelope, and send it to someone else who can mail it for you.\nNow the mail delivery system can see that you\u0026rsquo;re sending something to a forwarding address, but they cannot see anything else. If the forwarding address is providing the same service for millions of people, then there\u0026rsquo;s no way to tell when your particular postcard is sent on to it\u0026rsquo;s intended address.\nThat\u0026rsquo;s how VPNs work. You contract with a service and you send some or all of your traffic encrypted to that service instead of directly to the websites you want to contact. Then the VPN service unencrypts the message, and passes it on to the website that you want to communicate with. You can still encrypt your traffic before it\u0026rsquo;s passed on to the VPN, and the VPN will encrypt it a second time.\nThis process is called an \u0026ldquo;Encrypted Tunnel\u0026rdquo;. It\u0026rsquo;s a extension of the \u0026ldquo;Connection\u0026rdquo; idea that we\u0026rsquo;ve already talked about. If you imagine your connection to the Internet to be like a giant, transparent pipeline, where everyone can see all the traffic that is flowing through it, then a VPN connection is like a smaller, pipeline, or tunnel, running inside it. Everyone can see that there is a tunnel there; they can see where it starts and ends, but they cannot see the data inside it or know where it is going.\nOf course there is no tunnel, and no construct even remotely like a tunnel. But the fact that all of the traffic is encrypted means that seems to behave like a tunnel that is private and goes directly from your computer to the VPN exit point. To the rest of the Internet, it looks like your traffic is coming from that VPN end point.\nIf you use a VPN and check your IP address from one of the services in the Internet that does this, it will tell you that your address is the VPN\u0026rsquo;s address. If your VPN exit point is in a different country, many websites will detect that and give you pages in that country\u0026rsquo;s language, or ads for that country. They might even let you access content that\u0026rsquo;s only available in that country.\nHow Firewalls Actually Work # Anybody who has ever watched a TV show or movie with hackers gets the same, wrong, picture of how firewalls work.\nWe\u0026rsquo;re through the third layer of the firewall! 20 more minutes before we\u0026rsquo;ve cleared the next two and we\u0026rsquo;re in!\nThe word \u0026ldquo;firewall\u0026rdquo; itself is decieving. It makes you picture some kind of physical obstruction in the data path, and brute force can get you through it:\nBut it\u0026rsquo;s not like that at all.\nHere\u0026rsquo;s a better way to picture it:\nHere, the baby is the firewall. The pile of blocks on the left is traffic going in and out of your internal networks. The pile on right is the traffic to and from the Internet. The baby picks up the blocks from one pile and decides if it will throw it in the garbage, or put it in the other pile. The workman with the wheelbarrow and the postman with his bag can only pickup and drop off blocks in their own piles. They can\u0026rsquo;t influence the baby because it\u0026rsquo;s just a baby, and only does what its mother says.\nAnd if they hurt the baby\u0026hellip;well, then it\u0026rsquo;s not going to be moving any blocks, is it?\nThe blocks, of course, are the ~1500 byte network packets.\nSilly as this is, this is how a firewall really works.\nTraffic is Just Data # From this analogy, you can see that network traffic is just data. Just like a bunch of wooden blocks, it has no agency at all.\nYou cannot batter down a firewall with data.\nTheoretically, you could send some combination of data packets that could exploit some vulnerablity of a firewall that would cause it to let all traffic pass. I\u0026rsquo;ve never heard of that, though. And I imagine that any such vulnerablity would be big news and patched really quickly if it was found. So let\u0026rsquo;s consider that a long shot that we don\u0026rsquo;t need to actively worry about.\nIn fact, the only way that you can let your firewall be compromised, is by allowing an outside agent to connect to the configuration port of your firewall. Then they could conceivably log in and reconfigure your firewall.\nVirtually all firewalls operate on the idea of \u0026ldquo;inside\u0026rdquo; and \u0026ldquo;outside\u0026rdquo; interfaces, and they are configured by default not to allow connections to the configuration port of the firewall from the \u0026ldquo;outside\u0026rdquo; interface. You might be able to change this, but it should be clear by now that this is not a good idea.\nThe other way that your firewall might be compromised is if you allow traffic through that could, as an example, establish a terminal session on a system on one of your internal subnets and from there connect to a managment session on your firewall. Most firewalls allow you to restrict managment sessions to specific addresses on your internal subnets, however.\nA Firewall is a Router # Every firewall is actually a router. This means that it has 2 or more network interfaces, and it \u0026ldquo;routes\u0026rdquo; the traffic between them.\nTypically, every interface on a router has an address on a different subnet.\nDecisions about how to route a packet usually involve two decisions in this order:\nIs the destination for this packet on the same subnet as one of my interfaces? If so, then send it directly to its destination out that interface.\nUse a table to determine the address of the next hop to forward this packet towards its destination. Send it to that address through the interface on the same subnet as that address.\nThat\u0026rsquo;s it. That table in decision #2 can actually be amazingly complex, but we won\u0026rsquo;t go into that here.\nA firewall, on the other hand, has three decisions to make:\nDo I allow this packet to pass?\nIs the destination for this packet on the same subnet as one of my interfaces? If so, then send it directly to its destination out that interface.\nUse a table to determine the address of the next hop to forward this packet towards its destination. Send it to that address through the interface on the same subnet as that address.\nIt should be clear that decision #1 is doing a lot of work for a firewall. That\u0026rsquo;s what we\u0026rsquo;ll talk about next.\nFirewall Functionality # Let\u0026rsquo;s look at the mechanisms that a firewall uses to make that first decision\u0026hellip;\nThe Prime Directive: No Unsolicited Traffic from the Outside # Just what the heading says:\nImportant The most important rule is just this: Anything that just appears at the outside interface without an invitation gets discarded.\nYou need to understand that there is crucial philosophical difference between a firewall set up to reject any and all incoming traffic and one that allows it, or some of it, in.\nSetting up a blanket rule that says \u0026ldquo;No\u0026rdquo;, and sticking to it means that you never have to worry about an enormous category of potential security problems before they ever arise.\nNow, if you are a company hosting your own public website, and you need to allow incoming traffic in order to do business, that\u0026rsquo;s an entirely different thing. Then you need to worry about the security of the web server, and you need to worry about the security of every other device that web server can reach. And so on, and so on, and so on. You need to do penetration testing. You need to patch everything every time somebody finds a vulnerablity. You need to worry about this constantly. But that\u0026rsquo;s the cost of doing business, so you have no choice.\nIf you\u0026rsquo;ve got a homelab, and if you\u0026rsquo;re doing normal homelab stuff, then you don\u0026rsquo;t want to go there. When your spouse hears about some brand new Internet horror and asks you, \u0026ldquo;Do we need to worry about this? How do you know we\u0026rsquo;re safe?\u0026rdquo;, then you can answer honestly that there is nothing to worry about. You didn\u0026rsquo;t go there.\nAnd that means, NO PORT FORWARDING. I\u0026rsquo;m not going to even explain it here, because you shouldn\u0026rsquo;t do it. Especially not if you\u0026rsquo;re reading an introductory article like this.\nAddress Translation # This is a useful concept to understand. It\u0026rsquo;s more of a router function than a firewall function, but it lays the groundwork for understanding a lot of firewall concepts.\nPrivate IP Addresses # If you\u0026rsquo;ve just unboxed the WiFi router that your ISP sent you and plugged it in, you\u0026rsquo;re probably using a subnet like 192.168.1.0/24, or 192.168.0.0/24. Pretty much every WiFi router ever made uses these. But why?\nThere is a specification published years and years ago called, \u0026ldquo;RFC 1918: Address Allocation for Private Internets\u0026rdquo;, that sets aside 3 subnets for private use. These are:\n10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 You don\u0026rsquo;t see 172.16.0.0/12 used very often, because the it\u0026rsquo;s a bit tricky to configure due to the \u0026ldquo;/12\u0026rdquo;. But the others are used commonly.\nThe thing that makes these subnets special is that they are set aside purely for the use of private networks. What this means is that you will never find a publicly accessible Internet address in any of these subnets. And that means that if you set your IPTV box to have IP address 192.168.1.73, then it will never collide with any server out there in the Internet with the same address.\nAnd those are the subnets than everybody uses for their home networks. This is guaranteed to never cause problems.\nBut how does Internet traffic get to your IPTV box with a private address?\nYour Home Network\u0026rsquo;s Internet Address # If you are working on your laptop with address 192.168.2.17 and you browse to a site advertised to show your IP address, you\u0026rsquo;re not going to see 192.168.2.17. You are going to see the IP address that your ISP has assigned to your house.\nSo how does traffic from the Internet get from the Internet to your computer with a private address?\nYour firewall does this for you. It\u0026rsquo;s called \u0026ldquo;Network Address Translation\u0026rdquo;, or just \u0026ldquo;NAT\u0026rdquo;. To understand it, you have to understand another concept called \u0026ldquo;Ports\u0026rdquo;.\nNetwork Ports # Your computer isn\u0026rsquo;t just doing one thing, it\u0026rsquo;s likely to have lots of processes on the go at any given time, and each of those processes might be talking to the network. Something is needed to allow the traffic for each of those processes to get to the right place.\nThink of your computer as an apartment building. It has a single address for the building itself, and then each of the individual flats has a unit number. When you go to an apartment, you\u0026rsquo;ll usually see something like this:\nNow, if you wanted to send a postcard to the Wizard, you\u0026rsquo;d have to address with his unit number. Like this:\nPorts are exactly the same thing, just a number that gets tacked on to the end of the address after a colon: 192.168.3.72:7865 is typical.\nYou use them all the time, even though you don\u0026rsquo;t see it. There are a whole set of \u0026ldquo;well known ports\u0026rdquo; that are typically used for particular services, and are the defaults for those services. For instance, port 80 is usually HTTP without SSL, while 443 is HTTP with SSL. Port 25 is SMTP (mail servers), and port 20 is FTP.\nThis means that when you type \u0026ldquo;www.somewebsite.com\u0026rdquo; into a modern browser, it\u0026rsquo;s probably going to go to \u0026ldquo;https://www.somewebsite.com:443\u0026rdquo; automatically.\nAnd, by the way, when your browser sends out a request to a website, the return address that it uses will have some randomish port assigned to it. So if your IP address is 192.168.2.103, your request might have a return address of 192.168.2.103:53002. That way the response can get to the right process, and the right tab in your browser.\nPorts at the Firewall # Your firewall uses ports to handle address translation. Typically, your firewall will have one interface designated as \u0026ldquo;outside\u0026rdquo;, and it will have an IP address supplied by the modem that your ISP gave you. Just one address.\nBehind the firewall, you\u0026rsquo;ll have all kinds of devices, each with its own IP address on whatever private address range you\u0026rsquo;ve set up for your home network.\nHere\u0026rsquo;s what happens when your browser sends that request with the return address of 192.168.2.103:53002\u0026hellip;\nA packet hits an internal interface of your firewall, and its routing tables determine that it needs to go through the outside interface to the internet. The firewall tracks the IP address and the port of the packet and stores them a table, along with unique port number for that packet\u0026rsquo;s address/port combination. The firewall changes the return IP address of the packet to the IP address of its outside interface, and changes the port to the one that it assigned in its table. The firewall sends the packet on to the modem. When the return packet comes, it will have the IP address of the outside interface of the firewall, and the port number that it generated. The firewall looks up that port number in its table, and retrieves the internal IP address and original port number. The firewall replaces the address and port with its stored values. The firewall sends the packet out the appropriate internal interface. The end result is that your computer is able to communicate with the outside world without you having to worry about how your private IP address is routable from the outside world.\nStateful Inspection # At the top of this article, I explained how the idea of a \u0026ldquo;connection\u0026rdquo; isn\u0026rsquo;t real, it\u0026rsquo;s just the way that TCP/IP networking seems to work to users.\nFirewalls do a lot of their work by putting the connections back in.\nLook at the NAT explanation above. When you think about it, when the firewall says, \u0026ldquo;I see that this address/port on the inside has sent out a packet and is probably expecting a response, so I\u0026rsquo;ll need to track it so that I can route the response back\u0026rdquo;, that\u0026rsquo;s the firewall establishing a \u0026ldquo;connection\u0026rdquo;.\nYou\u0026rsquo;ll also remember the \u0026ldquo;Prime Directive\u0026rdquo; earlier. And you\u0026rsquo;ll probably remember the weasel word I inserted: \u0026ldquo;Unsolicited\u0026rdquo;. What does that mean.\nThink about it. If all Internet traffic is \u0026ldquo;Stateless\u0026rdquo;, and no traffic gets to come in from the outside, then how does a response from an outside web server get through the firewall?\nThe answer is that the firewall introduces the idea of a connection, and injects state into that connection. In the normal course of operations, the only inward traffic that gets through your firewall is traffic that is deemed to be part of one of those connections.\nWhen a device on the inside of your firewall reaches out to a device on the outside of your firewall, the firewall registers that communication as the start of a connection, and then any incoming traffic will be evaluated to decide if it is part of that connection. There\u0026rsquo;s more to it than just remembering the source and destination addresses and ports, but you get the idea.\nThis is fundamental working model of most firewalls. They establish connections with state, and then inspect the network traffic in the context of that state - Stateful Inspection.\nFirewall Rules # You might be wondering how the firewall implements Stateful Inspection and how you control it.\nVirtually every firewall uses tables of rules. These rules control whether or not a packet gets through the firewall and onto its destination.\nUsually - not every time, but most often - rules are evaluated from the top of the table down to the bottom for each packet. The first rule that applies to a packet determines what will happen to the packet.\nWhat is a rule? A rule has two components:\nDescription of the packet. An action, usually \u0026ldquo;Pass\u0026rdquo; or \u0026ldquo;Block\u0026rdquo; although a rule may specify some routing information, too. The description of the packet can include:\nThe state The protocol The source address/port The destination address/port The interface Using Rules # Firewalls can vary in how they implement rules and their default actions. OPNSense, and I believe pfSense, has a rules table for every interface that you define, as well as some floating rules and built-in default rules that apply to all interfaces. Let\u0026rsquo;s look at how you would go about creating a rule set for a single interface\u0026hellip;\nThe usual approach is to start with an empty table and put a \u0026ldquo;Block all packets\u0026rdquo; rule at the bottom of it. This becomes the default rule and means that any traffic which isn\u0026rsquo;t specifically allowed through will be blocked. It\u0026rsquo;s a good starting point. This is also the basis for \u0026ldquo;no outside traffic gets in\u0026rdquo;.\nAbove this rule you start to describe various types of packets that you explicitly want to be allowed to pass, or that you want blocked, and you add rules for them. Organize the rules such that the ones that override others are at the top. This means that the exceptions go at the top, and the rules tend to get less and less specific as you go down the table.\nLet\u0026rsquo;s say you decide that you\u0026rsquo;re not trying to restrict access from everyone\u0026rsquo;s laptop and phone going out to the internet. So you create a new rule that allows all traffic from inside addresses to go outbound to anywhere in the Internet. And you put that new rule right above that \u0026ldquo;Block all packets\u0026rdquo; rule.\nThen you realize that you never want the streams from your cameras going out to the Internet. So you add rules that block outgoing traffic from the addresses that correspond to your cameras. You put those rules on the top. You might have one rule per camera, or you might be able to put all of the camera addresses into a group, and just have one rule for the group.\nBut there have been a lot of porch pirates in your neighbourhood, and the local police have asked people with doorbell cameras to stream them to their server to monitor them in real time. So you create a rule that allows just that one camera to stream to that particular police server, and you put that one on the top.\nNow, when a packet hits the firewall, the following evalutaions happen:\nThe first rule asks if this is from the doorbell camera going to the police server. If so, the packet passes and you\u0026rsquo;re done evaluating rules. Then it asks if this is coming from one of the cameras. If so, it gets blocked, and you\u0026rsquo;re done evaluating rules. Finally, it asks if this packet is coming from the inside. If so, then it passes and you\u0026rsquo;re done evaluating rules. If all of these rules fail to trigger an action, then the last rule blocks the packet and you\u0026rsquo;re done. That\u0026rsquo;s the way it works.\nDeep Packet Inspection and Intrusion Detection # Typically, Stateful Inspection is fairly superficial. It looks at the timing of packets, the addresses and ports, and the protocols used. It is possible, however, that a outside party could use techniques to inject themselves into a connection, or a connection may simply be to a malicious outside party. Most firewalls provide some ability to employ something called \u0026ldquo;Deep Packet Inspection\u0026rdquo;.\nWith DPI, your firewall looks at the actual contents of the packets and matches them against known signatures of dangerous or malicious traffic. Generally speaking, DPI is an integral part of an \u0026ldquo;Intrusion Prevention System\u0026rdquo;.\nIntrusion Detection Systems, on the other hand, watch network traffic for patterns of suspicious behaviour. For instance, you might be blasted with a series of probes against sequential port numbers on your firewall and that might indicate that some malicious operator was attempting to locate a known vulnerability.\nWith OPNSense, DPI, IDS and IPS are typically implemented through optional plugins. CrowdSec is an example of these.\nProtecting From Internal Threats # The truth is that you probably have lots of devices on your internal networks that you really don\u0026rsquo;t know that much about. This could be the laptop that your brother-in-law brought with him when he visited, or it could be a smart light bulb that you bought off Temu.\nThese devices constitute a real threat to your home network. There have been articles lately about smart TV\u0026rsquo;s using AI to subvert other devices on their owner\u0026rsquo;s networks to take over microphones and other hardware. And who knows what viruses your brother-in-laws laptop has on it?\nA firewall is a key element in containing these threats. It will allow you to divide your internal network up into several subnets, and then it can control the flow of traffic between them. Put devices that you want to protect from other devices, or that might threaten other devices together on their own subnets.\nYour brother-in-law\u0026rsquo;s laptop? Create a \u0026ldquo;Guest\u0026rdquo; subnet, and set up your WiFi with a \u0026ldquo;Guest\u0026rdquo; SSID that connects to it. Give devices on that \u0026ldquo;Guest\u0026rdquo; subnet ZERO access to any other devices in your network. Allow them only to go out to the Internet, and nothing more. Don\u0026rsquo;t even give them access to your DNS servers.\nAll those smart devices and IOT gizmos? Create a separate subnet for them, give them no access to any other internal subnet, and strictly limit their access to the Internet. Then the only things they can hack are each other.\nWith most firewalls, each interface can have its own rule table. This makes it easy to group devices together and create default rules for them.\nAccessing Your Services from Outside # The ability to access your services when you are out and about, and not connected to your home WiFi can be really important. What\u0026rsquo;s the point of installing Immich to replace Google Photos if you cannot access it when you\u0026rsquo;re away from home?\nBut how do you do it?\nAnd how do you do it without breaking that \u0026ldquo;Prime Directive\u0026rdquo;.\nThe answer is to us a VPN. There are two ways to go about this, and they both involve WireGuard, which is the easiest VPN technology to use.\nThe first is to set up a \u0026ldquo;Road Warrior\u0026rdquo; VPN. This is a VPN hosted by your firewall that uses SSL keys to validate remote clients. Then, those remote clients can access internal devices based on a rule set.\nThe second is to use TailScale. TailScale also uses WireGuard, but installs it directly on your internal devices. These devices then generate a VPN connection outward, through your firewall, to the TailScale servers. You have an account with TailScale, and you control access to these VPNs through that account. Then, on your remote devices you install the TailScale client software and connect it to your TailScale account.\nTailScale is almost trivially easy to set up and get running, and the way I\u0026rsquo;d recommend everyone start out exposing services to the outside. The nice thing about TailScale is that it establishes the VPN from the inside of your firewall going out. So you can enable it simply be having a rule that allows outgoing WireGuard traffic from an internal interface. No need to allow actual outside access.\nVPN Hygiene # Regardless of which approach you take, it\u0026rsquo;s undeniable that you are compromising your security to some degree. It\u0026rsquo;s probably negligible, but it\u0026rsquo;s prudent to deploy it without giving wide open access to your internal network.\nFirst, deploy a Reverse Proxy Server like NGINX, Caddy or NPM inside your DMZ. A \u0026ldquo;DMZ\u0026rdquo; is a subnet that is intended for access from the outside world. Consider that any device deployed in it might be compromised at some time. Set up rules that deny access to any other subnet on your internal network by default.\nSet up a proxy host for every service that you want to expose via VPN. Configure it with an SSL certificate and enforce secure access.\nEvery proxy host requires that you specify both the address and the port of the service that it is proxying. Create a firewall rule to allow access from your reverse proxy server to that exact address and port. Do this for each service that you wish to have available from outside.\nOf course, the default rule for your DMZ needs to be \u0026ldquo;Block all packets\u0026rdquo;. This will ensure that only traffic from proxy hosts that you enable are allowed access to specific services in your homelab.\nSet up some form of \u0026ldquo;Split Horizon DNS\u0026rdquo; so that any connections to your reverse proxy server from outside will be via the WireGuard VPN, while internal connections go directly to the reverse proxy server.\nNow, all of your external connections via TailScale or WireGuard are going to terminate at your reverse proxy server located in a very restrictive DMZ subnet, and all of those connections are going to use SSL.\nImportant Assume that any device that you have exposed to incoming traffic, no matter how well guarded, might be compromised. Configure your network such that any compromised device can do as little damage as possible.\nI realize that these instructions are more than a little vague. Detailed instructions for setting up SSL secured proxy hosts in a DMZ with split horizon DNS and TailScale will be the subject of its own article in the future.\nConclusion # As I have been writing this article, news broke that supermarket freezers have been hacked again, this time in Belgium and Sweden. People seem to be focusing on the fact that the password was apparently set to the current date, but I\u0026rsquo;m mystified as to why an internal system like this was exposed to the public Internet at all.\nFor homelab owners, this should be treated as a cautionary tale. Security shouldn\u0026rsquo;t just be an afterthought, and you need to understand how network communications work in order make good decisions.\nUse a firewall. For myself, I purchased an old HP T740 Mini system with minimal memory and SSD. I bought this model because is supports a single PCI card, which allowed me to install a dual NIC card, giving me 3 Ethernet ports. It cost me about $130 CAD, plus another $20 or so for the network card. I run OPNSense on it, which is a very capable and free firewall. Even such a modest system is more than adequate to run all of the firewall functions that I need. It struggles a bit if I try to use deep packet inspection and IPS plugins, but I\u0026rsquo;m not convinced I need them.\nThe real cost of security is always effort and convenience. You cannot avoid this cost. It takes effort to set up a firewall, and to organize your network and the firewall rules to lock things down. It inconveniences everyone that uses services from your homelab when they have to install TailScale, or use passwords to access those services. The trick is to find the right spot, where the effort and inconvenience is tolerable while the compromises to security remain acceptable.\nNote While the images in this article are clearly AI generated, the words are not. Not just the ideas, but the actual sentences were composed by me, a real human being. If you want an explanation from AI you can just ask a chatbot yourself.\n","date":"4 November 2026","externalUrl":null,"permalink":"/homelab/security/","section":"Homelabs","summary":"Understanding basic Internet security, and how your firewall works","title":"Homelab Security and Firewalls","type":"homelab"},{"content":"","date":"4 November 2026","externalUrl":null,"permalink":"/homelab/","section":"Homelabs","summary":"","title":"Homelabs","type":"homelab"},{"content":" PragmaticCoding # This is website about practical and pragmatic approaches to building software and managing technology.\nFrom Merriam-Webster:\nPragmatic # Dealing with the problems that exist in a specific situation in a reasonable and logical way instead of depending on ideas and theories : practical as opposed to idealistic.\nIf you\u0026rsquo;re a programmer doing real work, or someone building a Homelab, then you\u0026rsquo;re probably primarily interested in building stuff that works, works well and keeps working for a long time. And you are going to be faced with problems every day. Problems that you need to solve, and implement so you can move on to the next thing. That\u0026rsquo;s what this website is all about; talking about practical, pragmatic approaches to building applications and systems that just work.\nJavaFX # I spent 7 years working with JavaFX to build enterprise applications integrated with legacy applications. During that time, I developed an approach to JavaFX that lets me build complex, scalable applications that don\u0026rsquo;t make your brain melt when you work with them. If you\u0026rsquo;re interested in learning how to do this, you\u0026rsquo;ll find dozen of articles explaining the essential elements and techniques you\u0026rsquo;ll need to know. There\u0026rsquo;s even a 13 part course designed for Absolute Beginners in JavaFX.\nSelf-Hosting # About a year ago I embarked upon a project to \u0026ldquo;decloud\u0026rdquo; my household and all of its technologies. I\u0026rsquo;ve created a Homelab with a cluster of tiny Lenovo computers running Proxmox and a few dozen virtual servers running on them. I\u0026rsquo;ve implemented an OPNSense firewall and redundant, load-sharing ISP connections.\nThroughout it all, I\u0026rsquo;ve been shocked at how much my decades of experience as an \u0026ldquo;IT Guy\u0026rdquo; - not just a programmer but someone responsible for planning and maintaining a corporate IT infrastructure - has been a virtual requirement for success. Can non-professionals do this? My goal is to try to provide some guidance for technical minded but non-professional people to plan and build their own self-hosting infrastructure.\nKotlin and General Programming # As a professional programmer for nearly 40 years, and as someone who has always spent a lot of time thinking about how we think about programming (metathinking!), I have developed a very pragmatic approach to coding. Hence the name of this website.\nI have found that while coding can be complicated, it doesn\u0026rsquo;t have to be complicated. There are some key concepts that, once you embrace them, tear out vast amounts of complexity in any application. Virtually every programming article that you find on this website, and every snippet of example code that you find, places an emphasis on understanding and following these fundamental principals. In fact, my Absolute Beginner\u0026rsquo;s course in JavaFX is almost more about how to leverage the techniques to write uncomplicated code than it is about screen widgets and Bindings and other tools.\nIf you\u0026rsquo;re a Java programmer, you owe it to yourself to try Kotlin. Kotlin is everything that you secretly wanted Java to be but didn\u0026rsquo;t realize it. A few years back, I started writing all of my example code in Kotlin and I think it actually makes it easier to understand the concepts I\u0026rsquo;m trying to illustrate, even if the readers aren\u0026rsquo;t all that familiar with Kotlin. I\u0026rsquo;ve included some articles to help you get started with Kotlin.\n","date":"4 November 2026","externalUrl":null,"permalink":"/","section":"PragmaticCoding","summary":"PragmaticCoding # This is website about practical and pragmatic approaches to building software and managing technology.\nFrom Merriam-Webster:\n","title":"PragmaticCoding","type":"page"},{"content":" This Was Never Really a Blog! # When my career ended I had this idea that all of this knowledge about JavaFX that I had accumulated over the past 7 years shouldn\u0026rsquo;t disappear with it. Maybe it was egotistical of me, but I felt that I had developed an approach to JavaFX that might help people.\nMy first foray into this was to contribute some articles to one of those blogging community websites. I cannot remember which one now, but it was primarily aimed at programmers and technology people. After about 6 or so articles, it became apparent that this environment was really working for me.\nI went looking for another, better but still free way, to host my blog.\nI settled on GitHub pages, because I was already using GitHub and it was easy to get started. This approach led me to Jekyll, and a lot of investigating led me to a theme called \u0026ldquo;Minimal Mistakes\u0026rdquo;. Minimal Mistakes was a good fit for me, but I did need to do a fair bit of customization to get it to behave in a manner closer to what I wanted. So much that I was scared to ever update it in case I broke my customizations.\nThat lasted for years. But there was also some cognitive dissonance involved. I was publishing articles in a blog roll, and then using permalinks to organize the content into a structure and creating manual links inside some static pages to hold it all together.\nHonestly, I didn\u0026rsquo;t know any better. This website stuff wasn\u0026rsquo;t my thing. It was just a tool I used get the content published and it didn\u0026rsquo;t get in the way too much. So fine.\nBut now I\u0026rsquo;ve gone through the process of converting it all over, I realize that virtually none of my content is anything that you would call a \u0026ldquo;blog\u0026rdquo;. I was essentially doing the blogging part by posting announcements on sites like Reddit in relevant sub-reddits.\nHugo and Blowfish # For those that care, the new site is built with Hugo and uses a theme called \u0026ldquo;Blowfish\u0026rdquo;. Hugo is a lot more modern, and Blowfish is - right out of the box - a better fit for my content style than Minimal Mistakes. So far, and I realize that this is still early days, I have almost no customizations implemented on the Blowfish theme.\nThere are some fundamental differences in the approached taken between Hugo and Jekyll, and they use different markdown processors which introduce some minor changes to the syntax. The biggest issue, however, was taking the content out of the blog roll and organizing it into structured content directories.\nNew Look # The first thing you\u0026rsquo;ll notice is that dark mode is now the default. There is a light mode, but I don\u0026rsquo;t think it looks quite as nice. The old Jekyll site only had a single mode, and it was designed as light mode. I don\u0026rsquo;t know if this is because 5 years ago light mode was still the defacto standard or I just didn\u0026rsquo;t know what I was doing. Either is possible, but that\u0026rsquo;s all I had.\nI like the stupid purple brain image, so I\u0026rsquo;ve decided to keep it as the default image for any article that doesn\u0026rsquo;t have a dedicated feature image shown in the card view. It\u0026rsquo;s also used as the \u0026ldquo;hero\u0026rdquo; image for all of the pages that don\u0026rsquo;t have something article specific. I needed something, and this website is primarily about thinking, so the brain is it.\nOne place the brain imagery has disappeared is in what Hugo calls \u0026ldquo;admonitions\u0026rdquo;. These are little sections inserted into the page that have something especially note-worthy that needs to stand out. They could be \u0026ldquo;warning\u0026rdquo; or \u0026ldquo;info\u0026rdquo; or whatever. I had spent way too much time customizing the HTML for these to show icons and have colours, and they still wouldn\u0026rsquo;t do things like highlight code snippets and other formatting inside. But some of them had a version of the brain in them.\nIn Hugo, these just work, and while they lose the specific site feel that they had, the new look is a bit more professional.\nNote This is a notice admontion and can see that it supports italics, bold and codeSnippets()\nTip Here\u0026rsquo;s a tip. All of the colours are defined as part of the theme.\nImportant This is an \u0026ldquo;Important\u0026rdquo; admonition.\nAway From GiHub Pages # One of my other goals was to move away from GitHub, mostly due to my philosophical objections to its owner. Initially I wanted to move to Codeberg Pages, since I will be migrating all of my projects to Codeberg over time.\nHowever, my development environment is fully behind my firewall and Codeberg Pages does not support DNS challenge and response for authentication. There\u0026rsquo;s no documentation about this, but that\u0026rsquo;s the error message you get when you finally clear through all of the other issues and errors when trying to configure workflow actions out of my Forgejo site.\nSo I went for Grebedoc.dev, which has been built, I believe, out of frustration with some issues with Codeberg Pages. Anyways, it seems to be a good web hosting site, it\u0026rsquo;s free and integrates with my Forgejo/Hugo infrastructure exactly the way it should.\nIn case you\u0026rsquo;re curious about this. I have a Forgejo server with a repository for this website on it. I do the editing on my workstation with Pulsar, and then commit/push the changes up to Forgejo. The repository has a webhook implemented to call to my Hugo server after every push. That webhook has the Hugo server do a fetch, and then Hugo rebuilds the site. Then I can surf to the Hugo server and see the development version of the site in my browser. It takes only a couple of seconds for me to see the results.\nWhen I want to publish, I have a workflow that calls a pair of runners. One is on the Hugo server and it downloads the site from Forgejo, builds it and uploads it as an artifact to the Forgejo repository. Then the second runner is called on a Docker server that is used to run a git-pages action that needs to run in a Docker container. This runner downloads the artifact from Forgejo and uploads it to Grebedoc.dev.\nI probably would have gone a different route, but I already had a Docker server running, and since it\u0026rsquo;s the same instance of Hugo running for both my development builds and my deployment builds I don\u0026rsquo;t have to worry about version differences with Hugo.\nI\u0026rsquo;m likely to write an entire article about this setup. Maybe generally about maintaining a local Forgejo service with integrations to outside publishing, including Codeberg repositories.\n","date":"1 November 2026","externalUrl":null,"permalink":"/blog/new-website/","section":"Ramblings and Rants","summary":"After 5 years with my old, blog-based, website it was time to transition to a new modern website design that integrates blogging with structured content.  Here’s how things have changed.","title":"A New Website","type":"blog"},{"content":"","date":"1 November 2026","externalUrl":null,"permalink":"/blog/","section":"Ramblings and Rants","summary":"","title":"Ramblings and Rants","type":"blog"},{"content":" Introduction # As someone who has already gone through the process of registering and managing a domain for my public website, specifically \u0026ldquo;pragmaticcoding.ca\u0026rdquo;, it was a fairly trivial step to register one for personal use. I already had a GoDaddy account from years and years ago, so it was just a matter of finding a domain name and charging it to my account.\nBut why would you want to do this? How can it help with self-hosting? Let\u0026rsquo;s take a look\u0026hellip;\nWhat is a Public Domain? # A \u0026ldquo;public domain\u0026rdquo; (my term, since I couldn\u0026rsquo;t find a better one anywhere on the web) is any domain that is registered into the system of domains that are managed by the Internet Corporation for Assigned Names and Numbers (ICANN).\nEveryone is familiar with the \u0026ldquo;top level\u0026rdquo; domains. These are things like .com and .net and .uk or .ca. There are also some subdomains of these that you see quite often. You might have seen British domains that are part of .co.uk, because in the UK, all of the commercial sites have been forced into .co.uk domain. In Canada you might see a .on.ca or a .bc.ca which are domains for Ontario and British Columbia. Back in the early days of the internet, to get a .ca domain, you needed to prove that you did business across several provinces. Otherwise they told you to register with your provincial domain. They don\u0026rsquo;t do that any more.\nIf your domain ends with any of the ICANN top level domains, then it\u0026rsquo;s what I would call a \u0026ldquo;public domain\u0026rdquo;.\nAnd you should have one.\nRegistering a Domain # Each of these higher level domains maintain a registrar. If you want to get a subdomain of one of these domains, you must put in a request with the registrar. In practice, nobody deals directly with the registrars. Instead you deal with a company that acts as an agent (or maybe a delegate of a registrar), and you pay through them. These are companies like GoDaddy, and NameCheap. In Canada there is one called \u0026ldquo;Register.ca\u0026rdquo;, which isn\u0026rsquo;t the .ca registrar, but just another agent. You can register domains in .net, .org, .com as well as .ca through \u0026ldquo;Register.ca\u0026rdquo;.\nWho you pick is up to you. It literally doesn\u0026rsquo;t matter which one you pick, but it would probably be best if you pick one that\u0026rsquo;s likely to be around for a few years. This is because the future management of your domain is going to be connected to your account at that agent, and tranferring it might be problematic if they go out of business with little warning. Most of these companies also provide a range of other services like email and web hosting. Prices for the same domain may vary between agents, too.\nPersonally, I use GoDaddy. This is mostly because I was involved in a commercial operation many years ago that required me to set up an account with them. I still have the account, and it was easy just to use it. I\u0026rsquo;m not sure that registrations with GoDaddy are the cheapest, but they\u0026rsquo;ll probably be around for a while.\nDepending on the domain name, and the top level domain that you register with, you can expect to pay from $15 to $30 a year for a domain for your self-hosting.\nDomain Name Services # The important thing to remember about this is that all of that registration stuff is just about the names. But these names are important because they provide the structure to allow everyone on the Internet to find the actual addresses of services they are looking for. This facility is provided by \u0026ldquo;Domain Name Servers\u0026rdquo;, often just called DNS servers.\nThe one thing that every public domain must have is a DNS server that can service public requests for information about the domain. For the most part, the registrar of the parent domain that holds your domain will have some level of DNS services for your domain, although that may just redirect to the agent through which you registered the domain.\nGenerally speaking, information about your domain is stored in something called a \u0026ldquo;Zone File\u0026rdquo;. A \u0026ldquo;zone\u0026rdquo; is usually just a domain. The kind of information held in a zone file would be the addresses of the DNS servers that it uses, the addresses of the email servers that it uses and address of web servers, or application servers that it controls.\nThis is where the public part becomes very important. Anything that you put into your zone file can be seen by anyone in the internet. This is important for things like email handling, because you want email servers anywhere in the world to know where to connect to deliver email to your domain.\nThis public visibility is why you need a public domain.\nEmail Handling # Years and years and years ago, my wife and I got our first email addresses from our ISP, which was Bell Canada, and they had a service called \u0026ldquo;Sympatico\u0026rdquo;. This meant that we had name@sympatico.ca addresses. Over time, the \u0026ldquo;Sympatico\u0026rdquo; service disappeared, but those email addresses continued to work as part of the Bell service. However, we had migrated to Gmail 20 years ago, so those sympatico.ca addresses were largely unused and forgotten over time.\nIf you live in Canada, then you probably know that Canada is especially poorly served with both cell phone and internet services. There\u0026rsquo;s a tiny handful of providers, they\u0026rsquo;re ridiculously expensive, and the service is low quality.\nAbout a year ago, I finally became exhausted with the cost of my internet and TV service from Bell when the monthly bill increased by yet another $5 a month. Also, Bell is the fibre provider for most of Canada, and in my neighbourhood, which is part of a large metropolitan suburban area, they haven\u0026rsquo;t bothered to install fibre to the home. This means the fastest speed possible was a paultry 25Mbps.\nAs a result, I cancelled my fibre connection, held my nose, and signed up with the only alternative which was cable internet.\nI didn\u0026rsquo;t really think to much about it, but it was clear to me that as soon as I cancelled the service with Bell, those sympatico.ca address were going to cease to function.\nI\u0026rsquo;m suprised at how quickly the fallout happened. Just a couple of days, in fact.\nMy wife asked me, \u0026ldquo;Did anything strange happen with email, I\u0026rsquo;m not getting email updates from Expedia?\u0026rdquo;.\nIt seems that her Expedia account was so old that it still used her sympatico.ca address. Oops! You cannot change the email address on an account without having access to the old account because they send it a confirmation email that you have to acknowledge. We had some accumulated reward points that we didn\u0026rsquo;t want to lose, and Expedia\u0026rsquo;s Customer Service told us that she\u0026rsquo;d have to create a new account and then they could transfer the points over when it was set up. So that\u0026rsquo;s what we did.\nI\u0026rsquo;m sure that lots of you have email addresses provided from your ISP\u0026rsquo;s. I\u0026rsquo;m sure that you also have lots of accounts with various websites that depend on those email addresses.\nIn a way, you\u0026rsquo;re locked in with those ISP\u0026rsquo;s unless you\u0026rsquo;re willing to go through the pain and suffering of updating all of your accounts on every website that you use. And remember, you have to do this before you disconnect the ISP address. This could be a real bother if you\u0026rsquo;re moving and don\u0026rsquo;t have the option to keep your old ISP.\nYou could one of the other services, like gmail.com or hotmail.com, but then you\u0026rsquo;re tied in to those. What if, like me, you\u0026rsquo;re fed up with Gmail? They were cool back in the early, \u0026ldquo;Do no evil\u0026rdquo;, days. But those days are long gone.\nHaving a public domain solves that problem. It also means that for as long as you own that domain, you\u0026rsquo;ll never have to change your email address ever again. You can have that email address for life.\nIt also has one other key benefit: There\u0026rsquo;s no doubt that having your own domain-based email address is much more appealing than having whatever email address you could find on a public service. Think on the simplicity of george@thesmiths.org over georgesmith87302@hotmail.com.\nThere are two approaches you can take to achieve this. But first, one idea you should not pursue\u0026hellip;\nDon\u0026rsquo;t Manage Your Own eMail Server # For decades, I was involved with managing the email server for the company I worked for. In the beginning, it was really the only option, because hosted email services from providers like Google and Microsoft simply didn\u0026rsquo;t exist - especially in the corporate space. We installed a Lotus Notes server back in the 1990\u0026rsquo;s, and it was comparitively easy. Just set up the DNS to point to it and configure the Lotus Notes server and turn it on.\nOver the years, it became more and more complicated to manage this server. The big problem was spam - more specifically, the measures that the rest of the world were using to combat spam. This meant that we had to be very careful to make sure that we didn\u0026rsquo;t get blacklisted in the spam filters, and I can tell you this was a shock the first time that it happened.\nIn some ways we were lucky that we had been running our own server for years, because we had established ourselves as a legitimate source which avoided a lot of hassles.\nTechnically, it\u0026rsquo;s not difficult to set up and run an email server in your homelab today. It\u0026rsquo;s not like you\u0026rsquo;ll have to navigate the archane complexity of sendmail or anything like it. On the other hand, all the reports I\u0026rsquo;ve seen say that you\u0026rsquo;ll spend way too much time chasing around delivery problems caused by anit-spam measures implemented by your email recipient\u0026rsquo;s services.\nFinally, the other big downside to hosting your own mail server in your homelab is that, by definition, it has to be directly accessible from the Internet at large. This means that you absolutely have to open up a hole in your firewall to allow SMTP connections to come in from anywhere in the Internet. Not through a VPN, straight in. With care, this is something that you can do, but for me this is something that I\u0026rsquo;d rather not do.\nJust let somebody else handle the headache of running an email server.\nMail Redirect # This is a good alternative if you aren\u0026rsquo;t ready to ditch your Gmail account (or even your ISP\u0026rsquo;s email) but you just want to be able to use addresses from your own domain. You can set it up for free, and it works fairly reliably.\nOne caveat to this approach is that not all of the mail services that you might currently be using support the outbound redirection scheme described below. If this is the case with your mail service provider, you\u0026rsquo;ll have to go with the second solution that I describe.\nIncoming Mail # In order to have mail sent to somebody@yourdomain.org you\u0026rsquo;ll need to use an \u0026ldquo;Email Forwading\u0026rdquo; service. Your best bet here is to simply do a search for \u0026ldquo;email forwarding service\u0026rdquo; and find one that appeals to you. For what it\u0026rsquo;s worth, I\u0026rsquo;m using improvMX.com and it seems to work fine. It\u0026rsquo;s free, too - at least for forwarding just two addresses.\nYou\u0026rsquo;ll need to set up an account with one of these services and then use whatever interface they give you to set up \u0026ldquo;aliases\u0026rdquo; for the email addresses that you want to forward. The alias is the address that you want have people send to, and the forwarded address is the actual address in you email service that will receive the email. For instance you might set up somebody@yourdomain.org to forward to georgesmith9983203@gmail.com.\nThe last step is to set up your domain to enable the email forwarding and to specify the forwarding service as the mail handler for your domain. This involves adding entries to the zone file for you domain in your public DNS server.\nLet\u0026rsquo;s look at the how to direct email to your forwarding service\u0026hellip;\nThis is done via MX entries in your zone file. MX stands for \u0026ldquo;Mail Exchange\u0026rdquo; and each MX entry lists the URL of a mail server for your domain. You can also specify a priority for each mail server.\nSomewhere in the documentation or setup pages for the forwarding service, you\u0026rsquo;ll find information about how to set up your domain. This will include the URL\u0026rsquo;s of the mail servers that you should use. For each one, you create an MX entry in your zone file. The service I\u0026rsquo;m using specifies mx1.improvmx.com and mx2.improve.com with the first one having a priority of 10 and the second one with a priority of 20.\nHow you enter the addresses and priorities specified by the forwarding service you select will depend on the UI provided by your DNS service.\nFinally, you\u0026rsquo;ll need to set up something in your DNS zone that tells the forwarding service that they are authorized to perform the forwarding. At least, this is what ImprovMX.com wants, and I expect that others will too. For ImprovMX.com, they want a TXT entry with a specific string.\nA TXT entry in your zone file is just text string that anybody in the Internet can read. The important thing is that nobody else can write entries in your zone file except you. So if the TXT entry that they are looking for is in your zone file, then you had to have put it there. For my ImproveMX.com account, the string is \u0026ldquo;v=spf1 include:spf.improvmx.com ~all\u0026rdquo;. Presumably, this isn\u0026rsquo;t just authorization, but some sort of configuration as well.\nOutgoing Mail # Not all email service providers support alias accounts for outgoing emails. Gmail does, and I strongly suspect that all of the other big, big providers do as well.\nIn order to do this, however, you are going to need an SMTP relay service. I\u0026rsquo;ve been using Smtp2Go after having issues with Brevo, but there are quite a few free services available. Once again, do an Internet search for \u0026ldquo;smtp relay service\u0026rdquo; and you\u0026rsquo;ll quickly get lists of services that you can use. All of these free services have limits on how many emails you can send in a time period, typically daily or monthly. Smtp2Go, is pretty restrictive at 1,000/month, but even that is way more than I\u0026rsquo;ll ever need.\nWhatever service that you pick, it\u0026rsquo;s almost certainly going to require that you create an account. Then, of course, it will need you to prove that you own the domain from which you are going to be sending emails. That\u0026rsquo;s going to involve creating some specific entries in your DNS zone file. Smtp2Go uses 3 CNAME entries, and they give you the exact information with buttons to copy the data so that you can paste it into the GUI for your DNS server.\nThe last prepratory step is to create a user/password that can send emails through the SMTP service. These are the credentials that you\u0026rsquo;ll be putting into Gmail.\nNow you\u0026rsquo;re ready to set up the outgoing alias on Gmail. Go into the web interface for you Gmail account at https://mail.google.com and log in. Then click the gear icon, which should bring up a settings sidebar, which should have an link called \u0026ldquo;See all settings\u0026rdquo;. Click on that link, and you\u0026rsquo;ll get a settings screen that has about 10 tabs at the top. Click on the \u0026ldquo;Accounts and Import\u0026rdquo; tab, and you\u0026rsquo;ll then see a section that says \u0026ldquo;Send mail as:\u0026rdquo;. Then you can click on the \u0026ldquo;Add another email address\u0026rdquo; link, which should bring up a pop-up window that allows you to enter the details of the new address.\nGive this address an name and then enter the address that you\u0026rsquo;re using from your own domain, for instance somebody@mydomain.org. Make sure the \u0026ldquo;Treat as an alias\u0026rdquo; box is checked off.\nIn the next step you enter the URL and port of the SMTP server, the user id and the password. Then save it and you should be good to go. Use \u0026ldquo;Compose\u0026rdquo; to send a test email to yourself.\nMail Service that Uses Your Domain # My migration path plan is to move my email handling from Gmail and over to a Canadian company called NorthMail (northmail.ca). They are new, and I\u0026rsquo;ve contacted them a few times with some compatibility issues that I encountered, and they\u0026rsquo;ve always indicated that what I wanted was in their future plans.\nThe first thing I asked them about was compatibility with FairEmail, which is the email app I use on my phone. They now support it. However, they didn\u0026rsquo;t support the mail forwarding scheme described above for outgoing mail, although they indicated that they had requests for it.\nI checked back last month, and they now have a \u0026ldquo;For Business\u0026rdquo; product which allows you to set up accounts within your own domain. It will work just as well for families with their own public domains as for a business, and the price per mailbox is about the same as for an individual mailbox. This is the direction that I\u0026rsquo;m planning to go, although it won\u0026rsquo;t be transparent to my wife as she\u0026rsquo;s currently using the Gmail app for email on her phone. I\u0026rsquo;m going to flip her over to FairEmail first. This means it will have to be a process.\nSetting this up is going to be easy for me. Just purchase the account with NorthMail and tell them what my domain name and email addresses will be, and then configure my public DNS service such that my MX entries point to the NorthMail servers instead of the ImprovMX servers.\nI\u0026rsquo;m sure that you can find similar services from thousands of other providers around the world.\nSSL Certificates # Another story from the deep past\u0026hellip;\nBack around 1997 the company that I worked for needed to register a domain and get an SSL certificate for our web server. Back in those days you dealt directly with the registrar, and we had to prove that we did business across the country before we could register directly into the .ca domain. We also dealt directly with VeriSign to get the certificate.\nYou could not get a certicate from VeriSign unless you could prove that you owned the website and the business associated with it. I remember having to go get a copy of our articles of incorporation to provide this proof. In truth, I cannot remember 100% if we needed them for the domain or for the certificate, but I feel like it was more likely to have been the certificate. It was 30 years ago, though.\nI do remember that the whole process was an ordeal that literally took weeks to complete.\nNowadays, it\u0026rsquo;s not even remotely difficult. But you should remember that an SSL certificate does 2 things:\nProves that web server that you are connecting to is actually the one in your address bar. Provides the keys to allow your browser to use encrypted communication with the web server. Both of these are important, but it\u0026rsquo;s the first one which provides the technical hurdles to getting a certificate.\nFirst off, certificates are issued by only a few trusted certificate authorities. Your browser is going to have a list of certificate authorities that it trusts. If it encounters a website with a certificate that is not signed one of these trusted authorities, it will display ugly warning messages. This includes the \u0026ldquo;self-signed\u0026rdquo; certificates that some of your homelab servers may use by default.\nThis means that you need to get your certificates from one of these trusted authorities. Today, we have a system called \u0026ldquo;ACME\u0026rdquo;, which stands for \u0026ldquo;Automatic Certificate Management Environment\u0026rdquo;, that you can use to automate the process of getting a certificate from a trusted authority.\nJust like back in the 1990\u0026rsquo;s, the key element that you have to prove to the certificate authority is that you own and control the web server and the domain for which you are trying to acquire a certificate. There are two ways to do this:\nMake a specific customization to the web server that the certificate authority can connect to and detect. Make a specific customization to the zone file for the domain that the certificate authority can detect. Item (1) is problematic. Many of the servers for which you\u0026rsquo;ll want to issue certificates are not going to be publicly accessible, and therefore cannot be connected to by the certificate authority.\nItem (2) is trivial if you have a public domain.\nAnd remember, certificates expire. And proper certificates expire every few months. So this is something that you want to set up and then forget about. You\u0026rsquo;re not going to want to set up some hole in your firewall so that certificates can be re-issued at any random time.\nDynamic Domain Name System # You are probably aware that if you are using a normal residential internet connection, then your public IP address will change from time to time. Anything that you try to set up that relies on using the public IP address of your connection will need to make sure that it has the current IP address in order to work.\nYou can use a service like \u0026ldquo;Duck DNS\u0026rdquo; to do this. That\u0026rsquo;s going to require that you create an entry in their zone file, and you\u0026rsquo;ll end up with a hostname like \u0026lsquo;fred.duckdns.org\u0026rsquo;. Then you\u0026rsquo;ll need to set up a script that checks your connection\u0026rsquo;s IP address and updates the DuckDNS.org zone file with the new address.\nThis works, but you\u0026rsquo;re tied into yet another service that you\u0026rsquo;ve got to keep track of.\nYou can do exactly the same thing with a public domain. In fact, if you are using OPNSense, this is just another service that you can set up in your firewall and it will run on a scheduled basis and keep your zone file up to date with the current address of your IP connection.\nThis is called \u0026ldquo;Dynamic DNS\u0026rdquo; (or \u0026ldquo;DDNS\u0026rdquo;), because your DNS zone is dynamically adjusted to reflect changes in the IP addresses of the hostnames.\nThis is really useful if you are setting up an incoming VPN, because you\u0026rsquo;ll need to let the roaming clients know where to connect to\u0026hellip;\nIntegration with VPNs # This refers to VPN\u0026rsquo;s that you set up to allow access into your network from outside. You might be familiar with using a VPN as a client, where you connect into a commercial VPN in order to anonymize your traffic, enter the Internet from another country, or to prevent your ISP from snooping on your connections. Generally speaking, at some point the clients for these services are going to need to connect to a specific gateway or group of gateways in order to join the VPN.\nThe same situation exists when you create a VPN for an outside client (or peer) to access your network. You\u0026rsquo;ll need a publicly accessible gateway that the clients can connect to. But you cannot give them an IP address, because your ISP could change it at any given time. This is where DDNS comes in to play.\nTailscale # The other implementation of VPN\u0026rsquo;s that you are likely to use is \u0026ldquo;TailScale\u0026rdquo;. The TailScale service actually maintains its own DNS service for your TailScale network that only your TailScale clients can access. This is great, and allows you to use URL\u0026rsquo;s instead of IP addresses when you\u0026rsquo;re setting up clients.\nHowever, if you are like me and you don\u0026rsquo;t want to go through TailScale when you\u0026rsquo;re on your own WiFi this isn\u0026rsquo;t going to work all by itself. You\u0026rsquo;ll want to connect directly to the server from your WiFi, and when you leave your house you\u0026rsquo;ll want to connect through TailScale.\nIf you have a public DNS server and a private internal DNS server (which you probably should have), then you can set up a situation where your internal DNS server has entries that point directly to the internal addresses of your servers, and the public DNS server contains the TailScale addresses.\nWhen you are on your WiFi, you\u0026rsquo;ll be using your internal DNS server and therefore connect directly to your services without TailScale. When you are outside your house, you\u0026rsquo;ll be using your cell provider\u0026rsquo;s DNS server, which will get your addresses from your public DNS server and route you through TailScale to your severs. This is called \u0026ldquo;Split Horizen DNS\u0026rdquo;.\nAPI Tokens # Some of the things I described require that you are able to update your DNS zone file in an automated fashion. This is usually accomplished through API tokens, which provide a way for applications to connect to your DNS service and make changes to your zone file. Essentially, they are fairly long strings of gibberish which are unique to your account with the DNS service.\nGenerally, you can create multiple API tokens. This allows you to have different tokens for different purposes, and you can usually disable them, or have them expire, or delete them whenever you want.\nNot all DNS services support API tokens, so you\u0026rsquo;ll have to be on the lookout for this when you pick a DNS service. GoDaddy, for instance, only supports API tokens for what they call \u0026ldquo;premium\u0026rdquo; clients - those clients with 50 or more domains registered through them. I think this is a change in the past few years.\nYou don\u0026rsquo;t have to pick your registrar agent with this in mind, though. It\u0026rsquo;s relatively trivial to pass the handling of your DNS services off to any other service provider that you want. I chose to use deSEC.io, which is in Germany and is also free. I also see it popping up as an option in places where you need to pick your DNS service for automated processes. So it\u0026rsquo;s a good choice for me.\nIn my GoDaddy account, I just go to the area for my domain, pick the \u0026ldquo;DNS\u0026rdquo; section and plug in the servers for deSEC, which are ns1.desec.io, and ns2.desec.org. That\u0026rsquo;s it. From then on I just use my account at deSec to manage my DNS.\nConclusion # Having your own public domain with a public DNS server is a great idea because it removes a lot of friction from a few key infrastructure tasks that you\u0026rsquo;re going to need to do to run a homelab.\n","date":"24 June 2026","externalUrl":null,"permalink":"/homelab/public-domain/","section":"Homelabs","summary":"If you are self-hosting, then you should probably have a public domain registered.  In this article, we’ll look at why you should do this, and the things that it helps.","title":"You Need a Public Domain","type":"homelab"},{"content":" Introduction # Domain Name Servers (DNS Servers) are one of the cornerstones of both the Internet, and your own self-hosted infrastructure. In this article we are going to look at what DNS servers do, how they work and integrate with each other, and how to host your own private DNS server for your homelab.\nThe Absolute Basics - Addresses and Names # When you strip away everything else, every device connected to a TCP/IP network - which is just about everything we deal with here - is uniquely identified with an IP address. There are two versions of IP addresses in use now: IPv4 and IPv6. We aren\u0026rsquo;t going to talk about IPv6 in this article because it\u0026rsquo;s a subject all by itself. So, from now on when I refer to IP address, I\u0026rsquo;m talking about IPv4 addresses.\nIPv4 addresses are 32 bits long and divided into 4 octets of 8 bits. So, 4 bytes. They look like 192.168.1.1. Each byte, obviously, can have a value between 0 and 255.\nThe problem with IP addresses is that are clumsy and hard to remember. They are also absolutely attached to a particular machine - this becomes an important limitation later on.\nFinally they don\u0026rsquo;t have a hierachical structure that maps nicely to the real world relationships behind how the Internet is organized.\nTo overcome these issues, we use names to refer to devices on the Internet instead, and everybody is walking around with a whole bunch of these device names in their heads. Furthermore, the names are structured in a hierachical manner that lines up nicely with the way that the Interet is actually organized. This structure also makes them easier to remember.\nInternet names are divided up into sections separated by . characters. They go from most specific on the left, to least specific on the right. Let\u0026rsquo;s look at a typical sort of name: server.dmz.organization.com, and see how it is organized.\nAt the far left is the specific machine, server. It is located inside the domain .dmz.organization.com. Next along is .dmz which is a domain, but it is a subdomain of .organization.com.\nIf we keep going to the right, we get to .organization, which is a subdomain of .com. And, of course, we have .com which is what is referred to as a \u0026ldquo;top level domain\u0026rdquo;. You can think of this as three domains: .dmz.organization.com, .organization.com and .com. Of course, both .dmz.organization.com, and .organization.com are both subdomains of the domains to the right.\nEach domain has absolute control over its own contents. This means that whatever body owns/controls the .com domain is the only body allowed to add or remove anything to/from it. In order for .organization.com to exist, the owner of .com had to add it.\nIn this example, the subdomain .dmz.organization.com looks like it\u0026rsquo;s simply an administrative division inside of the .organization.com domain. It\u0026rsquo;s highly likely that the owner of .organization.com created it for the use of .organization.com, and not as a separately administered subdomain. This is quite normal, and any owner of a domain can freely add whatever subdomains to their domain that they want, for whatever purposes.\nDomain Name Servers # What we need in order to route traffic to server.dmz.organization.com is a way to turn that name into an IP address. This is where name servers, or DNS Servers (the \u0026ldquo;DNS\u0026rdquo; refers to \u0026ldquo;Domain Name System\u0026rdquo;), come into play.\nIn a nutshell, the domain that \u0026ldquo;owns\u0026rdquo; a name is responsible for providing the translation for that name into an IP address. In fact, a name on the Internet (or any IP network for that matter) simply doesn\u0026rsquo;t exist unless it\u0026rsquo;s discoverable in its domain\u0026rsquo;s DNS server.\nThis means that for every domain on any IP network, there has to be a DNS server that can give the IP address for every name in that domain.\nNow, if you type http://server.dmz.organization.com into your browser then how does your browser know where the DNS server for .dmz.organization.com is? How does it query it?\nThe answer is very simple. Each parent domain has to have a an entry in its DNS server that tells everyone where the DNS servers for every single subdomain it holds is.\nThis means that your browser has to go and query the .com domain to find out the location of the DNS server for .organization.com. Then it has to query that DNS server for the address for server.dmz.organization.com. It might be that .dmz.organization.com has its own DNS server, in which case the query have to continue at that server.\nAt least that\u0026rsquo;s the naive view of how it works. In reality, your browser makes a single request to its own local name server and the rest of the work is done by that. Your browser isn\u0026rsquo;t aware of how the name is resolved, just that it happens.\nWhen the DNS servers talk to each other, the process iterates over and over. Your local DNS server will answer the request if it has the information, otherwise it passes the request on to its upstream DNS server, which then goes through the same process. Eventually, some name server will query .com for the DNS server of organization.com and it will give the answer. At that point the process rolls itself back up, each name server passing back the answer until it eventually gets back to your browser.\nNote This is a good way to conceptialize how DNS servers communicate, meaning that it provides a working model that explains the behaviours that you see and the things that you need to do in order to properly configure a DNS server. Under the hood, this may not match up with how modern DNS servers actually go about doing what they do.\nThere was a critical component in this description: \u0026ldquo;DNS server will answer the request if it has the information\u0026rdquo;. How can it have the answer if it isn\u0026rsquo;t the DNS server for the domain specified?\nThe truth is that going through this entire process for every single request would be an incredibly wasteful approach. In reality, DNS servers cache the results that they\u0026rsquo;ve looked up for a period of time. DNS entries, for the most part, don\u0026rsquo;t change very often so it\u0026rsquo;s safe to store them for a while. In fact, every DNS entry includes a \u0026ldquo;Time to Live\u0026rdquo; (TTL) value. This is the number of seconds for which the answer can be assumed to be valid. DNS caches will respond immediately with values that haven\u0026rsquo;t yet reached their TTL.\nThis means that a DNS server can return two types of values:\nAuthoritative This is when the DNS server that responded to the request is the designated DNS server for the domain that was queried.\nNon-Authoritiative This is when the DNS server that responded to the request is NOT the desginated DNS server for the domain that was queried.\nI\u0026rsquo;m only defining this because we\u0026rsquo;ll need to understand this a little when we talk about setting up a DNS server. In real life, you rarely care whether or not an answer was authoritative. In those cases that you do, it\u0026rsquo;s because something went wrong.\nTwo Uses for DNS Servers # From the section above you can see that DNS servers have two direct uses.\nTo act as an agent to look up names from around the Internet. To act as the authoritative definition of the contents of a domain. When you are setting up a homelab, you\u0026rsquo;re going to want your DNS server to act as both.\nWhat Information is in a DNS Server? # The contents of a DNS server are organized into \u0026ldquo;zones\u0026rdquo;. Just about every DNS server you\u0026rsquo;ll encounter will contain multiple zones. Back in the old days, before we had GUI\u0026rsquo;s to manage the DNS servers, we used to configure them by creating \u0026ldquo;Zone Files\u0026rdquo; which were just text files in a particular format that the DNS server could parse. Dinosaurs like me still refer to the contents of a zone as a \u0026ldquo;Zone File\u0026rdquo; even though they aren\u0026rsquo;t really used any more. Saying, \u0026ldquo;Put it in the zone file\u0026rdquo;, is just a susinct way of refering to the process of configuring a zone in a DNS server.\nAll records in a zone file have at least these three elements:\nType Name Value Generally, DNS lookups are done by specifying the type and and the name. It is possible, even common, to do searches for just a name, and accept any type of records that are returned. It\u0026rsquo;s also possible to do searches for just type, for instance if you are looking for MX records to find the email servers for a domain.\nWhat kind of entries might you find in a zone file?\nSOA - Start of Authority This entry specifies the authoritive information about the zone. The name of the record is the name of the zone, but you\u0026rsquo;ll usually see it listed as @. The value is structured, and amongst other things will tell you the name of the primary nameserver for the domain.\nA - Address This is your bread and butter entry. The entry has a name, and the value is an IP address.\nCNAME - Redirection The most common use you\u0026rsquo;ll find for these is when you want to use multiple names for the same address, but you don\u0026rsquo;t want to have to update multiple entries if that address changes. A CNAME entry has a name of its own, and then returns the name of another entry that it shares an address with. An important point here is that if a DNS lookup encounters a CNAME, then it will continue on but now doing a lookup for the value in the CNAME record.\nTXT - Text TXT entries are generic informational entries that don\u0026rsquo;t have any defined purpose in the DNS system. Lot\u0026rsquo;s of proprietary systems use them to store publicly visible information that they need for their processes.\nMX - Mail Exchange These entries tell the internet where the email servers for your domain are located. These can be either IP addresses or names that can be looked up via DNS searches.\nNS - Name Server Like SOA these have a name of @, and they contain the names of authoritative DNS servers for the domain. There can (should) be more than one for each domain.\nPTR - Pointer PTR records are a bit like CNAME records, but they won\u0026rsquo;t cause DNS lookups to carry on looking up the new value. You are going to see these used in \u0026ldquo;reverse lookups\u0026rdquo; more often than not. A reverse lookup uses DNS to locate the name of an entry from its IP address. Most DNS server software will facilitate the creation of reverse lookup zones for this purpose.\nYou may encounter other types (there are lots more), but for a homelab these are the ones you\u0026rsquo;re most likely to care about.\nManaging Your Own DNS Server # Unless you want to remember a bunch of IP addresses - which means you\u0026rsquo;re never going to be able to expose your services to the non-technical people in your family - you\u0026rsquo;re going to need your own name server. At a minimum, you need to be able to assign names to the servers in your network.\nYou have choices. If you are running on OPNSense firewall, you can use one of the services it hosts to run your DNS. Or you can spin up a container in Proxmox and run one of the popular DNS servers.\nLots of people seem to come into DNS through PiHole or AdGuard without even realizing that are running a DNS server.\nPersonally, I prefer Technitium\u0026rsquo;s DNS server. It\u0026rsquo;s easy to install and configure, supports all the ad blocking you could ever want and has a bunch of advanced features that you\u0026rsquo;ll probably want to use sooner or later. The GUI interface is awesome and intuitive and the whole thing just works without any hitches.\nThat\u0026rsquo;s what I\u0026rsquo;m going to talk about here.\nInstalling Technitium # I installed Technitium from the Promox Community Scripts, and I think that\u0026rsquo;s the probably the easiest way to get it up and running.\nThe Script page is here. If you go there, you\u0026rsquo;ll see that the script creates an LXC guest, and defaults to using a single CPU core with 512MB of RAM. It\u0026rsquo;s fairly light-weight, as you would expect.\nIn the \u0026ldquo;INSTALL\u0026rdquo; section, there\u0026rsquo;s a bash command that will download the script and run it for you. You copy that command (there\u0026rsquo;s a little copy icon right at the end of the command), and then paste it into the shell of your Promox node. It will then walk you through a series of questions that will determine how the LXC is configured.\nThe first question asked by the scripts is whether you want to just use the \u0026ldquo;Default\u0026rdquo; or \u0026ldquo;Advanced\u0026rdquo; installation (at least those are the first two options). I always pick the \u0026ldquo;Advanced\u0026rdquo; because there are a couple of networking options that I want to customize, and I don\u0026rsquo;t remember seeing them the very first time I ever installed anything using \u0026ldquo;Default\u0026rdquo;. I also pick the \u0026ldquo;Verbose\u0026rdquo; output so that I can at least see that progress is still happening as the install runs, and I\u0026rsquo;m not sitting there staring at an unchanging screen wondering if it has got stuck or not. Other than that, I usually just accept all of the default values as they come up.\nWhen it\u0026rsquo;s done, it will tell you the address and port to log into the DNS Server\u0026rsquo;s administrative GUI. Plug that into your browser and, after you\u0026rsquo;ve logged in, you\u0026rsquo;ll see a screen that looks like this:\nThe \u0026ldquo;Menu\u0026rdquo; is actually just the tabs across the top of the page.\nUpstream Lookups # In order to use your DNS server, you\u0026rsquo;ll need to make it the primary DNS server for your network. Obviously, systems on your network will need to query this DNS server if they are going to be able to lookup names on your network.\nBut once you do this, then all of the searches from your network are going to go through this DNS server. Even the ones for names in other domains out there in the Internet. So before you can do this, you\u0026rsquo;ll need to tell Technitium how you want it forward your requests for lookups outside your domain.\nTo set this up, go to Settings \u0026ndash;\u0026gt; Proxy \u0026amp; Forwarders and scroll down to the second section. You\u0026rsquo;ll see something like this:\nTechnitium has a reasonable selection of commonly used public DNS servers that you can quickly select. You can, of course, put in the addresses or names of any other DNS servers that you would like to use, including the ones from your ISP - if you really want to do that. You should pick at least 2 from at least two different providers.\nThe \u0026ldquo;Save\u0026rdquo; button is at the bottom of the page. Now you can move on to creating a zone.\nCreating a Zone # Before we get into this, I strongly recommend that you keep your domain flat with a single zone. Unless you\u0026rsquo;re running some kind of complex organization with mulitple sub-units that need to independently assign names without worrying about collisions with names in other sub-units, you\u0026rsquo;ll just be making your life more complicated than you need to if you start adding subdomains and zones.\nSo stick to one zone, and come up with a naming scheme that avoids collisions in that one zone.\nFrom the main Dashboard screen pick the \u0026ldquo;Zones\u0026rdquo; tab, and then at the top left you\u0026rsquo;ll see a button called \u0026ldquo;Add Zone\u0026rdquo;. Click it, and you\u0026rsquo;ll get a screen that looks like this:\nFor internal purposes, you don\u0026rsquo;t have to have a properly registered domain to set up a DNS server. You can call this zone anything you want, but be aware that if you replicate some properly registered domain name then you won\u0026rsquo;t be able to access that external domain from inside your network. That being said, having an actual registered domain with an public, external DNS server is a good idea.\nFor now, just pick \u0026ldquo;Primary Zone\u0026rdquo;. If you do have a registered domain that you also want to use for internal purposes (I would recommend this), then you\u0026rsquo;ll eventually want to change it to \u0026ldquo;Conditional Forwarder Zone\u0026rdquo; (see the section below).\nThat\u0026rsquo;s all you need to do. The \u0026ldquo;Catalog Zone\u0026rdquo; area is for configuring a failover if you have more than 1 Technitium DNS server in your network. It\u0026rsquo;s a good idea to do this, but beyond the scope of this discussion.\nOne last point. If you click on the link: \u0026ldquo;Help: How To Self Host Your Own Domain Name\u0026rdquo; it will take you to an article about how to host a public DNS server for your registered domain. That is not what we are doing here, so that article is not relevant.\nAdding Entries # Once you\u0026rsquo;ve clicked \u0026ldquo;Add\u0026rdquo; on the previous screen, you\u0026rsquo;ll end up with a page that looks like this:\nFor free, you get an SOA record and an NS record that points to your Technitium server. For internal purposes, neither of these two records is particularly important. The default values in the SOA will work just fine, and you\u0026rsquo;ll probably be publishing this server via its IP address with either DHCP, or the static info you set up in all of your devices. So the NS record will never be used in real life.\nNow you can add your first address. Click on \u0026ldquo;Add Record\u0026rdquo;:\nMake sure that \u0026ldquo;Type\u0026rdquo; says \u0026ldquo;A\u0026rdquo;. Fill in the name that you want to add\u0026hellip;\nOk, naming stuff is hard. It\u0026rsquo;s probably the single hardest aspect of programming, and it\u0026rsquo;s only slightly less hard with network administration. Things to keep in mind:\nNames need to be memorable This is the point of using names. Trust me, you don\u0026rsquo;t want to be surfing over to your DNS server to see what you called something every time you want to open it in a browser.\nNames need to be specific It\u0026rsquo;s probably not obvious now, but you are going to find that you have servers and you have services. While each service is hosted by a server, they are not the same thing. And sometimes you\u0026rsquo;ll have several services hosted by the same server. When you start out naming things in your network, you are probably going to be defining servers, not services. So name your servers as servers.\nNames need to follow a scheme This because you will forget the names. But, if you follow a scheme, then you can probably figure out the name you want by re-applying the scheme. A bit of a pain, but still easier than looking it up in the Technitium GUI.\nNames need to be a short as possible Address bars are small, and typing stuff into a phone browser is always painful.\nNames need to be consistent Compose your names the same way each time. If you have 3 Proxmox backup servers then name them pb1, pb2 and pb3, or pb0, pb1 and pb2 (if you are a programmer). Or name them firstpb, secondpb and thirdpb if you hate yourself. Then if you decide to add more name servers, use the exact same convention. Don\u0026rsquo;t call them ns_1, ns_2 and ns_3 if you used pb1, pb2 and pb3. And don\u0026rsquo;t call them ns1, ns2 and ns3 if you used pb0, pb1 and pb2. You get the idea.\nNames can be hard to change Just try and change a name after you\u0026rsquo;ve used it on 4 people\u0026rsquo;s phone apps, referenced it for an API call on 3 different servers, and imbedded it into your smart TV. Eventually you\u0026rsquo;ll have to do this, and then you\u0026rsquo;ll wish you listened to me. Try to get your naming scheme figured out at the beginning, so you\u0026rsquo;re not changing names over and over.\nYou\u0026rsquo;ll have to trust me. These things are important. Also, remember that you can use CNAME entries if you want shortcut names for particular servers or services. You can also use them to help transition through changes to your naming scheme.\nMoving on with this screen: I wouldn\u0026rsquo;t mess with the TTL unless you are planning to change the address as part of a setup process or something. In that case, set it really low, like 30, so that your workstation won\u0026rsquo;t cache the lookups and cause grief. Once you do settle in on a final address, change the TTL back to 3600.\nThe \u0026ldquo;Add reverse (PTR) record\u0026rdquo;, and the \u0026ldquo;Create reverse zone for PTR record\u0026rdquo;, are potentially useful. IFF the address is unique to the name, then just click these two boxes on. This will cause Technitium to create a reverse lookup entry (and potentially the zone to put it in) which will let you find the name from the IP address.\nWhen you need this, you need this. And it costs almost nothing.\nIf you\u0026rsquo;ve got 5 names that all point to the same address, then you\u0026rsquo;ll only want to check off these two boxes when you create what you would consider to be the \u0026ldquo;primary\u0026rdquo; name for that address.\nI find it useful to put something in comments. At least explain what the name is in plain English. Is it a server or a service? Also, if it\u0026rsquo;s a service and it has a particular port that you need to use to access it, I\u0026rsquo;d put it in the comments. It saves having to look through you browser history, or checking the GitHub pages for the project to find how to access the service.\nThat\u0026rsquo;s click \u0026ldquo;Save\u0026rdquo;. Rinse and repeat for all of the other names you want to create in your network.\nConditional Forwarding # This is a bit advanced in concept, but easy in practice.\nThere are lots of good reasons for registering your domain name, and using a publicly accessible DNS server for that domain. For instance, you\u0026rsquo;ve set up your email so that you can have your own email addresses in that domain. Something like fred@myfamily.org. In order to be able to receive email for that domain, other mail servers need to be able to find the MX records for myfamily.org. And that means you\u0026rsquo;ll be using some DNS service for you domain out there in the Internet somewhere, something like CloudFlare.\nBut now you\u0026rsquo;ve just set up an authoritative DNS server for myfamily.org inside your home network. Nobody outside your network can see it or use it, which is good. But anybody inside your network will never see entries that are in that CloudFlare DNS server. And that can be a problem.\nWhat you want is for your local, private DNS server to answer any lookup for myfamily.org from its own records if it can, and if that lookup fails, then to forward the request out to the Internet and let the CloudFlare DNS server answer it.\nThis is called \u0026ldquo;Conditional Forwarding\u0026rdquo;, and it\u0026rsquo;s pretty powerful and easy to set up. All you have to do is to change the type of your zone from \u0026ldquo;Primary\u0026rdquo; to \u0026ldquo;Conditional Forwarding\u0026rdquo;.\nGo into your zone, and at the top of the screen will be a button called \u0026ldquo;Options\u0026rdquo; with triangle, click it and you\u0026rsquo;ll get a drop-down meun. Select \u0026ldquo;Convert Zone\u0026rdquo; and you\u0026rsquo;ll get a screen like this:\n]\nMake sure that \u0026ldquo;Conditional Forwarder Zone\u0026rdquo; is selected and click on \u0026ldquo;Convert Zone\u0026rdquo;. All done.\nAd Blocking # If you\u0026rsquo;re not coming from the PiHole/Adguard world, you may wonder how a DNS server can block ads.\nNobody embeds actual IP addresses in anything, not even for ads. If you look at the source for any web page that has ads, you\u0026rsquo;ll see URL\u0026rsquo;s for the ad content buried in the source. Things like https:\\\\clickme.spammersrus.org\\adcontent8873923. You browser has to perform a lookup to find the address for clickme.spammersrus.org, in order to then connect to that server and download the ad content.\nBut what if your DNS server refused to return a result for clickme.spammersrus.org?\nThat\u0026rsquo;s exactly how DNS ad blocking works. The DNS server has a list of servers or domains that it knows are used for ad content, and it simply won\u0026rsquo;t resolve them. For anyone. This means that the ad content is completely blocked from you network. Not just browsers, but apps on any phone on your WiFi. No more ads at the bottom of your Sudoku app. No more ads baked into any servers that you host. None.\nAnd since everybody hates ads, there are lots of people and organizations out there who compile and update lists of all of the sites known to serve up ad content. And you can tell Technitium which lists you want to use.\nSetting up ad blocking in Technitium is really easy. In the \u0026ldquo;Settings\u0026rdquo; tab there is a sub-tab called \u0026ldquo;Blocking\u0026rdquo;. It\u0026rsquo;s all there.\nThe first check boxes turn it on.\nThen next allows you to disable it temporarily, and allow you to specify some address that you\u0026rsquo;ll exempt from the blocking. Maybe you\u0026rsquo;ve gotten one of those \u0026ldquo;Please disable your ad blocker\u0026rdquo; pop-ups that blocks the content and you really want to see that content? Not for me, but this is how you would temporarily turn off the ad blocking.\n\u0026ldquo;Blocking Type\u0026rdquo;, tells Technitium how to respond when it blocks an address. I just stick with the NXDOMAIN response, which seems to work well.\nThen we get to the the lists and list management. There are quite a few lists pre-loaded into Technitium, but if you want to you can links to whatever lists you can find. You\u0026rsquo;ll also notice that these won\u0026rsquo;t just block ads, but will also block content of various types. For instance, you can get lists with gambling sites, adult content, or malicious content.\nThe best recommendations are to start off with a limited amount of blocking and see how it works. Some lists are much more aggressive than others, and will include sites that might block content that you actually want to let through. If you are finding that this is happening, then shift to a less aggressive list. If too much is still getting through, then add more lists or switch to more aggressive lists.\nOne other thing: You can also use an upstream public DNS that also does some of this filtering and blocking as well. I\u0026rsquo;ve used some of these with my Android phone, and they do work quite well. But Technitium does a good job in this regard, so I don\u0026rsquo;t use them for my network.\nEncrypted Lookups # Obviously, all of your traffic goes out through your ISP to the Internet, and this includes your DNS lookups. It\u0026rsquo;s highly likely that your ISP is analyzing your DNS lookups in order to monetize them through ad revenue. At least this is the accepted belief.\nTechnitium makes it trivial to encrypt your DNS queries via DNS-over-TLS, so just do it. The one caveat is that not all upstream DNS servers support DNS-over-TLS. However, CloudFlare, Google and Quad9 all support it, so this shouldn\u0026rsquo;t be an issue.\nAlso, remember that there\u0026rsquo;s no point encrypting DNS queries to your ISP\u0026rsquo;s DNS servers since they can always see what\u0026rsquo;s going on at the DNS server end.\nTo turn it on, go back to this screen:\nAnd click on \u0026ldquo;DNS-over-TLS\u0026rdquo;. Make sure that the forwarders that you have selected support this protocol.\nConclusion # Although DNS servers are the backbone of so much of the Internet, and they can do some really, really complicated stuff, setting up your own DNS server for you homelab is fairly easy.\nRealistically speaking, this should probably be the first server that you set up on your homelab. For one, it\u0026rsquo;s very straight-forward and just works. This makes it a good learning experience and will introduce you to a lot of homelab concepts. On top of that, you\u0026rsquo;re going to want to start using names right away, so you\u0026rsquo;ll need a DNS server to do that.\n","date":"23 June 2026","externalUrl":null,"permalink":"/homelab/dns-concepts/","section":"Homelabs","summary":"Everything you need to know about DNS if you are self-hosting.  How to use Technitium to host a private DNS server in your homelab.","title":"Managing DNS Services","type":"homelab"},{"content":"","date":"22 June 2026","externalUrl":null,"permalink":"/series/","section":"Series","summary":"","title":"Series","type":"series"},{"content":"","date":"22 June 2026","externalUrl":null,"permalink":"/series/snapcast/","section":"Series","summary":"","title":"SnapCast","type":"series"},{"content":" Introduction # For a number of years now, we\u0026rsquo;ve been using our Google Home/Nest devices to stream music around the entire house. It\u0026rsquo;s good when it works, but we\u0026rsquo;ve found that the music will intermittently just stop playing for no apparent reason. This is guaranteed to happen several times a day. I\u0026rsquo;ve seen that others have reported the same problem.\nWe play a station from the SomaFM streaming service called \u0026ldquo;Groove Salad\u0026rdquo; pretty much all day long. It\u0026rsquo;s ambient electronic music which is a good background to day-to-day living for us. Just as you would expect, we don\u0026rsquo;t like every single song that they play, but some we like a lot, and most just fade into the background.\nGoogle does a pretty good job of synchronizing the various devices, so if you do find yourself in a spot where you can hear two of them, it doesn\u0026rsquo;t sound like a weird echo. I\u0026rsquo;ve even created a custom command to start the music in the group that we\u0026rsquo;ved defined for the whole-house music.\nThat turns out to be worthwhile, because it stops so often.\nAdditionally, there was a time in early 2024 when our audio chromecast device stopped working for a while. It turns out that the security certificate that they set up for in on Google\u0026rsquo;s servers expired after 10 years. It took a few days for them to sort that out, but the incident showed how dependent we were on Google\u0026rsquo;s infrastructure and ongoing support for the devices we were using. It\u0026rsquo;s not like Google has never suddenly cut support for a product before, either.\nI wanted some way to stream the music with the same high quality synchronization without using Google. I also didn\u0026rsquo;t want to trade Google for a lock-in on some other proprietary solution like Sonos. I was hoping to find something that was open source, and would work with a variety of hardware.\nIt turns out that there is a software option out there called \u0026ldquo;SnapCast\u0026rdquo; that will do exactly this.\nThis is a 3 Part Series # I started out writing this as a single article, but it quickly became a monster that Jekyll said would take over half an hour to read. I found myself reluctant to break things down into more sections or to add more information because it was so long already.\nI realize that breaking it up into parts doesn\u0026rsquo;t make it any shorter, but it does make it a little less daunting to read.\nLet\u0026rsquo;s get started\u0026hellip;\n","date":"22 June 2026","externalUrl":null,"permalink":"/homelab/snapcast/","section":"Homelabs","summary":"Introduction # For a number of years now, we’ve been using our Google Home/Nest devices to stream music around the entire house. It’s good when it works, but we’ve found that the music will intermittently just stop playing for no apparent reason. This is guaranteed to happen several times a day. I’ve seen that others have reported the same problem.\n","title":"Whole Home Audio With SnapCast","type":"homelab"},{"content":" Introduction # When I initiated my project to build a homelab, I didn\u0026rsquo;t want a huge up-front expense. But I did have a need for whatever I built to be as reliable as possible. After all, I\u0026rsquo;m planning on replacing a fair number of cloud services - services that are generally pretty reliable - with home-hosted alternatives.\nTo me, this means redundancy.\nI\u0026rsquo;ve looked at lots of photos of people\u0026rsquo;s homelab setups and good number of them, probably even the majority of them, seem to have one server doing most of the work. This is, of course, a single point of failure. In my book, that\u0026rsquo;s just not something that you do.\nProxmox is a great help here. While setting up high availability - where you set up a cluster of hosts, designate vital containers and VM\u0026rsquo;s, and then have them fail over to different hosts when their host fails - is difficult without having lots of fast disk space and multi-gigabit networking, you can still use backups to restore vital servers when a host fails.\nBut, how do you create an effective cluster of servers on a shoestring budget without turning your paycheck over to the electic company?\n1 Litre PC\u0026rsquo;s # It wasn\u0026rsquo;t long after I started thinking about building a homelab that I started seeing videos, posts, and articles about \u0026ldquo;mini\u0026rdquo; PC\u0026rsquo;s that people were using. These were tiny little desktop computers that idled at about 7W and could handle a surprising amount of load.\nIt turns out that there are three basic product lines. HP has an EliteDesk series that they call \u0026ldquo;Mini\u0026rdquo;, Dell has an Optiplex line called \u0026ldquo;Micro\u0026rdquo; and Lenovo has a ThinkCentre line called \u0026ldquo;Tiny\u0026rdquo;. I\u0026rsquo;m not going to say that they are the same, but, they are all pretty close. These are all what are called \u0026ldquo;1 Litre\u0026rdquo; form factor PC\u0026rsquo;s, and they all use external power bricks instead of internal power supplies. A common term referring to these computers collectively is \u0026ldquo;Tiny Mini Micro\u0026rdquo;.\nHere\u0026rsquo;s what the Lenovo version looks like, with a pen so that you can see the scale:\nYou can see that it\u0026rsquo;s pretty darned small.\nThese units were incredibly popular with corporate clients. They bought gazillions of these things because they just worked well for office applications, they were sturdy, and they were cheap. A lot of them were leased.\nAnd when those leases ended\u0026hellip;\nThe leasing companies sold them off in bulk at wholesale prices to recover some of the value, and the companies that bought them have refurbished them and are reselling them. The result is that they are readily available at reasonable prices.\nI was told that the advent of Windows 11 has caused a shortage of these units, and the prices have gone up a fair bit. Even so, they are still a great value.\nWhy Lenovo # In truth, you probably cannot go wrong with either of the three brands. These computers were all designed for the same, commercial, market and they are all executed well. In my area, I was seeing a lot of Dells and Lenovos available, but not so many HPs. I went with Lenovo because they seemed just a little bit more solid, and there was a bit more variety in the models available.\nOne thing I really wanted was to have all of the units in the cluster as similar as possible. I wanted them to be fungible. As I was planning to purchase them over time, I needed the supply to be consistent for at least a while.\nYou should also know that these computers, are still manufactured today and available to purchase new, although these are newer versions with more power and a higher cost..\nThe Hardware # Let\u0026rsquo;s take a look at these boxes and see what you get.\nThese have been in production for quite some time. You\u0026rsquo;ll be able to find models like the M73Q which have 4th gen Intel Core processors for peanuts, but those are probably not going to have enough computing power to be useful. I decided on the Lenove M910Q which has 6th or 7th generation Core processors.\nYou do need to be a little careful. All of these computers look identical, so you have to make sure that you are getting what you think you are getting and that you know what kind of processors go in which models. This product line is also still in production, so you can buy more recent versions with 10-12th generation Intel processors - although at a higher price.\nBeyond that, they all come with a variety of different available processors. So, once again, be careful to make sure that you are purchasing what you think you are. Don\u0026rsquo;t pay an i7 price for an i3 equipped unit.\nM910Q vs M710Q # If you look into these, you\u0026rsquo;ll see that there are two models which look and seem to be very, very similiar; the M910Q and the M710. What\u0026rsquo;s the difference?\nYou can see for yourself the spec-sheets:\nM910 Specs\nM710 Specs\nAs far as I can tell, these two units are nearly the same, except that the M910Q uses the Intel Q270 chipset instead of the B250 chipset in the M710Q. This means that the M910Q supports VPRO, while the M710Q does not.\nBoth models support the same set of processors and memory.\nThe place where I bought mine made no price distinction between the M710Q and the M910Q because the average customer wasn\u0026rsquo;t building homelabs. What they cared about was the CPU. The 7th gen Core i7 cost about double the 6th gen Core i5 models. The price of a system with the same CPU was the identical between the M710Q and the M910Q.\nThe Processors # The M910Q will take a variety of Intel laptop CPU\u0026rsquo;s from the 6th and 7th generations. It will support Core i3, i5 and i7 processors from both generations. Let\u0026rsquo;s take a look at the differences, while ignoring the i3.\nThe i5-7500T is about 10% faster than the the i5-6500T for multithreaded applications, and 8% faster for single-core applications. Both support 4 cores and 4 threads. You see the slightly smaller differences between the i7-6700T and the i7-7700T.\nOf course the i7-6700T supports 8 threads, but the i7-6700T is just 16% faster than the i5-6500T for single-core performance. Multi-threaded performance is significantly higher.\nThe biggest difference is between the i5-6500T and the i7-7700T. Even here, the scores for Cinebench Single-Core come in at 132cb vs 168cb, while Cinebench Mulit-core is 464cb v 805cb. This last comparison is the biggest of all and probably significant for Proxmox host systems.\nI\u0026rsquo;m not saying these performance differences are insignificant, but a Core i5-6500T went for about $50 CAD on eBay, while a Core i7-7700T from the same seller went for $120 CAD at the time that I bought my systems. I was seeing this difference in the prices of the M910Q\u0026rsquo;s in the store as well.\nAbout 9 months later, you can buy i7-7700T CPU\u0026rsquo;s, the most powerful CPU\u0026rsquo;s compatible with these systems, for between $50-$70 (CAD) on eBay. That\u0026rsquo;s less than a stick of 16GB RAM for one of these. This means that upgrading is a very reasonable possibility.\nMemory # The M910Q takes DDR4 laptop style, non-ECC SODIMM\u0026rsquo;s. There are two slots, and each can take up to 16GB of RAM, for a total of 32GB.\nMost of the systems that I have seen for sale, on line or in person, are loaded with a single 8GB stick of RAM. I upgraded all of mine to 16GB when I bought them.\nI\u0026rsquo;m kicking myself now, because that extra 8GB cost me about $25 and I could have taken them up to 32GB for about $50. As I write this, each additional stick of 16GB DDR4 is going to cost me about $75.\nOf all of the resources that I worry about being a constraint after implementing quite a few services, it\u0026rsquo;s RAM. I\u0026rsquo;m nowhere close to getting critical, but I expect it will be the first thing to cause a problem.\nA Look Inside # One of the cool things about the M910Q is that it just needs a single screw on the outside removed in order to gain access to the inside, and most maintenance thereafter is tooless. The top cover slips off, and then this allows a plate on the bottom to also be removed.\nLet\u0026rsquo;s look at the view of the inside from the top:\nThe CPU is socketed, and hidden away under the duct extending back from the fan. From the videos I\u0026rsquo;ve seen, I believe that you might need to undo some screws to remove the heat sink and access the CPU socket.\nThe black plastic construct to the right is the SATA drive cage. Squeeze that semi-circular plastic bar towards the cage and the whole thing pops out. The ribbon cable for the drive is held onto the cage with that yellow tape. On mine, it was zip-tied - which I think is the standard.\nThe cage will accept a 2.5\u0026quot; drive with a height of 7mm. You can install an SSD or an HDD.\nUnderneath it is an M.2 slot for a WiFi card, or something compatible with it. Some models come with the WiFi card and the antenna.\nPull off the plate on the bottom, and this is what you\u0026rsquo;ll see:\nDon\u0026rsquo;t be fooled by the text on the PCB that says, \u0026ldquo;SATA/NVMU/SSD2\u0026rdquo;. It\u0026rsquo;s unpopulated on the M910Q and the M710Q. I believe that the M920Q and the M720Q might have that slot. In this picture, we have an SSD installed in slot #1.\nThe only other things under here are slots for the memory.\nThat\u0026rsquo;s pretty much it. There are a small number of options that might be installed in these. Mine have two DB9 serial ports, but others have additional DisplayPort ports. Presumably, you could swap these out, but the choices are fairly limited.\nFrom what I have seen, some of the later HP EliteDesk Mini\u0026rsquo;s have a optional 2.5GB adapter on a daughterboard that can just be slotted in. But there\u0026rsquo;s nothing like that on the Lenovo\u0026rsquo;s.\nIn one of my units, I\u0026rsquo;ve installed a Coral TPU in the WiFi M.2 slot. This is for some AI processing in my NVR server.\nPorts on the Back/Front # Lenovo sold these in all kinds of configurations, but they all have a pantload of USB 3.1 Gen 1 ports on the back and front.\nMost of them seem to have 2 or more DisplayPort ports. No HDMI. You might miss this if you\u0026rsquo;re not paying attention, and you\u0026rsquo;ll have to make sure that you have a DisplayPort compatible monitor (or a converter) in order to do the initial setup.\nThey use a standard Lenove square power port. This is the same as what you\u0026rsquo;d find on a Lenovo laptop. They use power bricks that come in 60W and 90W variants. You\u0026rsquo;ll probably be fine with the 60W versions for most purposes.\nThere is but a single 1GB ethernet port.\nMine must have come from some kind of lab or engineering environment. They are festooned with DB9 serial ports, which I assume were used to connect to test equipment or other stuff that pre-dated USB. I don\u0026rsquo;t think I\u0026rsquo;ll ever use them.\nLimitations # Without a doubt, the biggest limitation of these systems, if you are going to use them as Proxmox hosts, is that they only have a single 1GB Ethernet adapters. There\u0026rsquo;s no upgrade potential here that doesn\u0026rsquo;t involve soldering and 3D printing.\nThis means that Ceph, and therefore HA clustering in Proxmox, is probably a no-go. However, I am planning on seeing how they perform with 2.5Gbs or 5Gbs USB NICs forming a isolated network for the cluster. It seems that these adapters are getting more stable now.\nIn general, however, beyond adding memory, a SATA drive and possibly putting in a larger SSD in the NVME slot, there\u0026rsquo;s not a lot of room for upgrading or enhancing these systems. There\u0026rsquo;s just no room inside them.\nIf you really, really, really feel like you need the ability to add a PCI card to do something specific, then you might consider the M920Q or the M720Q. These have a PCI slot, although you\u0026rsquo;ll need an additional riser card to be able to use it. Note that you\u0026rsquo;ll probably have to spend about $100-$150 more to get one of these tricked out with the riser card.\nPricing # This is where these little units really shine. The price.\nThe outfit I bought my units from was selling them with a standard configuration of 8GB RAM and a 256GB M.2 SSD. The first two ones I ended up with had DB9 serial connectors, but no WiFi.\nThe basic model with a 6th gen i5 was $100, while the i7 (not sure of the generation) was close to $200. I did end up with one 7th gen i5, and that was $110, the other two were 6th gen i5 processors. I increased the memory in all of them to 16GB, because I strongly suspected that 8GB wasn\u0026rsquo;t going to cut it for Proxmox servers. When I bought the first two, I simply bought an extra 16GB DIMM and them moved one of the 8GB DIMMs to the other system. For the third one, I just paid to swap out the 8GB DIMM for a 16GB one.\nThis is going to make upgrading them to 32GB a bit more expensive, because I\u0026rsquo;ll have to by 4 16GB DIMMs and hope I can trade in the two 8GB DIMMS.\nWith the memory upgrade, I paid about $115 CAD each for the first two, and then about $145 CAD for the third. So, about $375 CAD together. They came with a six month warranty.\nValue Compared to Raspberry Pi\u0026rsquo;s # I checked, and a RPi 5 with 4GB RAM goes for about $105 CAD. The performance of the RPi is significantly lower than that of the Core i5-6500T, especially when it comes to multi-core performance, where the RPi scores about 60% of that of the i5-6500T.\nIt\u0026rsquo;s almost certain that the M910Q draws more power than the RPi 5, especially when idle. So if you do have an application that needs fairly low single-core processing power which is idle for most of the time, an RPi might be a more appropriate solution. Also, if you do have a need for the GPIO capabilities of the RPi, then it\u0026rsquo;s clearly the way to go.\nPerformance # Performance issues are a significant difference between running a corporate data centre and a homelab.\nIn the corporate world, you can reasonbly assume that all of your various servers are going to be running under a consistent and considerable load all day. With hundreds of users and services exposed to the Web, you can be pretty sure that just about everything is doing something at any given time. Especially between 9am and 5pm. This means that you have to provision and configure your data centre to meet that fairly predicatible load across a number of services active at the same time, and then also to handle peaks that happen from time to time.\nBut homelabs aren\u0026rsquo;t like that. You probably have more virtual servers than users, to start with. Some of your services are likely to be 99% idle most of the time.\nThat\u0026rsquo;s what it\u0026rsquo;s like with my homelab. I have a server that streams whole-home audio 24/7, but it only uses 2% of 1 core, and about 60MB of RAM. I have a couple of servicers that wake up once every 5 minutes and check my two ISP connection speeds, log the results and send me a notification if either one runs really slow. I have a service that scans my network for active devices every few minutes.\nAll of the other servers support \u0026ldquo;on demand\u0026rdquo; services. JellyFin is mostly active when someone is watching a video. Immich when someone is uploading, downloading or viewing pictures. Vaultwarden when someone is synching passwords. The backup servers do all of their work in the middle of the night, around 3:00am. You get the idea.\nThis has a very different impact on how your servers perform\u0026hellip;\nCPU # One of the biggest surprises for me was just how little CPU the various Proxmox containers actually consume. I have about 28 VM\u0026rsquo;s and LXC\u0026rsquo;s running right now and the Proxmox dashboard says I\u0026rsquo;m using 4% of my CPU. These containers include HomeAssistant, JellyFin, SnapCast, NetAlertX, Open Media Vault, Immich, an *arr suite, NGINX and two Technitium DNS servers. A couple of containers are running Docker.\nOnce in a while I can peg the CPU on one of the servers. If I upload a ton of images into Immich, it\u0026rsquo;ll run hard for a while as it does its facial recognition and processing. Open Media Vault can get pretty heavy at times, too.\nThe key thing here is that any given virtual service, running full tilt, is only going to be able to occupy 33% of my homelab\u0026rsquo;s CPU, because it only has access to the CPU on its own host. On the other hand, it\u0026rsquo;s probably a good idea to spread your potentially CPU intensive virtual servers across your Proxmox nodes. Maybe even consider which ones are likely to be running at the same time, and keep them on different nodes.\nMemory # Overall, the cluster seems to be running with about 30% of its memory in use. This can vary quite a bit if any of the services gets busy. Most of my servers are deployed as LXC\u0026rsquo;s which are much more dynamic in the way that they consume and release memory than traditional VM\u0026rsquo;s. I think that this has helped to keep the memory requirements fairly low.\nOnce again, virtual servers cannot access resources not on their host node. Looking to keep memory intensive applications spread across all the nodes is probably a good idea.\nPower Consumption # Eventually I got around to installing a UPS for my HomeLab and it has a display that shows how much power it\u0026rsquo;s drawing. With all of the essential components to stay \u0026ldquo;up\u0026rdquo; connected to the UPS, it\u0026rsquo;s pulling about 80 watts. This is 3 M910Q\u0026rsquo;s, the HP T740 hosting the firewall, the external drive bay, the switch and the two modems.\n80 watts. I\u0026rsquo;m impressed.\nThe UPS predicts it can last for a couple of hours with that load, and it\u0026rsquo;s not a big UPS.\nApplication Performance # In terms of user experience, there are no performance issues. These servers are 100% capable of meeting every demand placed on them in response to user activity.\nOne of the things I did when setting up Immich for my wife was to use Google TakeOut to download about 60GB of photos from her Google account. Then I uploaded all of them into Immich. I found that it took about 15 minutes for Immich to fully digest about 1000 photos, which included its facial recognition. This also seemed to line up with the maximum number of files that the upload selection window could handle, which is more of a UI issue than anything else. So it did take some hours of going back and uploading the next batch of 1000 every 15 minutes or so. During those 15 minutes, the Immich server was running at about 90% CPU use, so I was too chicken to try to upload another batch of 1000 before it was done.\nThat was the only time I\u0026rsquo;ve noticed any impact of the server power on user experience.\nAfter Installing Frigate # I have recently installed a Frigate NVR virtual server processing two cameras outside my house. I\u0026rsquo;m going to be adding a video doorbell to the load on this server. I added a Coral TPU to the node it\u0026rsquo;s running on to assist with some of the object detection and recognition that it does. Additionally, I installed Frigate on the only node that I had that has an internal SATA drive, and that\u0026rsquo;s an HDD, not an SSD. I assume that the HDD was idle and not consuming much of any power prior to configuring Frigate to use it as storage for its recordings.\nThis was the one application that I was a little concerned about with servers this low powered. However, it turns out not to be a problem.\nI now see my entire cluster running at about 20% CPU, with the node hosting Frigate running at about 50% most of the time. Memory usage on both the cluster and the Frigate node is averaging about 50%.\nMore than anything else, my UPS is now reporting around 100W consumption most of the time. That\u0026rsquo;s an increase of around 20W, which I assume can be attributed to the increased CPU usage and the HDD drive constantly spinning now.\nI\u0026rsquo;m not noticing any peformance degredation in any of my services. I do however occasionally hear some fan noise coming from that node, that I never heard before. Otherwise, these servers are virtually silent.\nConclusion # It\u0026rsquo;s been about 9 months since I bought these servers, and I\u0026rsquo;ve been constantly adding more and more services to my homelab throughout that time. At no point in time have I encountered any application where they weren\u0026rsquo;t up to the task. These servers are now doing real work all day, and have replaced a bunch of services that I used to have outsourced in the cloud.\nThat being said, I don\u0026rsquo;t think that these units are going to be a good fit for hosting my own AI services, which is something I think I\u0026rsquo;ll probably be looking to do at some point in the future. But even then I would probably keep my existing cluster and add on something specifically for AI processing.\nThe one thing that they simply won\u0026rsquo;t work for, due to the single 1Gb ethernet port, is as a firewall. You need two ethernet adaptors for that. So, for a firewall I implemented an HP T740. It\u0026rsquo;s very similar to the M910Q\u0026rsquo;s, but a little bit bigger, has an AMD processor and comes with a PCI slot with a riser card. This means that I was able to add a 2 port ethernet card to it.\nIf you are just starting out with self-hosting, I think these are an amazing place to begin. You can get a single server for less than $200, even with today\u0026rsquo;s RAM prices, and it will do whatever you need. Add a second or third, and you have a real data centre with all the possiblities of redundancy that you get along with that.\nFor myself, I\u0026rsquo;m really pleased at how these servers have worked out. I had the goal of building out my homelab over time and within our monthly discretionaly budget. The low cost of these servers has meant that I have been able to do this and I don\u0026rsquo;t feel that I\u0026rsquo;ve sacrificed anything with this approach.\n","date":"20 June 2026","externalUrl":null,"permalink":"/homelab/lonovo_tiny/","section":"Homelabs","summary":"A look at the Lenovo M910Q Tiny computers I’ve chosen as homelab servers.","title":"Lenovo M910Q Tiny Homelab Server","type":"homelab"},{"content":" Introduction # Back in 2003 I was doing some work in the company\u0026rsquo;s accounting department when all of the PC\u0026rsquo;s in the area flicked off and rebooted again. It was so fast and simultaneous that I thought that we\u0026rsquo;d had some kind of electrical fault that hit that part of the office, and I didn\u0026rsquo;t think too much about it. Comparing notes with one of the other IT guys much later on, he had also noticed the same thing in another part of the office.\nSometime later, our Internet connection was down. Or so I thought.\nI got in touch with our ISP and they checked and told me that, no, the connection was up and there was actually a LOT of traffic going through it. So I went into the computer room to check.\nBack in those days, our connection between the our firewall and the T3 modem was through an old 10Base-T hub. Not a switch, but a hub. This was okay, because these were the only two devices on the hub, and the 10Mbps, was still faster than our Internet connection.\nThe hub had a little red light on it that flashed whenever it detected a collision when two nodes try to transmit at exactly the same time and interfered with each other. During normal operations, you\u0026rsquo;d expect that light to flicker once or twice a minute. Any more, and you likely had a problem brewing.\nWell, I went into the computer room and that collision light was solid.\nNot flickering, just on.\nThere was so much traffic going down that hub that it was jammed up into constant collisions and nothing was getting through. Back in those days, we hosted our own web sites on premises, and this meant that we had essentially been kicked off the web.\nOur firewall logs had gotten so big in that short time that it had run out of disk space and stopped logging. I had to flush the logs and restart it just to find out what that traffic was.\nIt turns out that we had been hit with a worm. Probably the \u0026ldquo;Welchia\u0026rdquo;, or the \u0026ldquo;Blaster\u0026rdquo; worm. One of the characteristics of it was that, after installing itself, it caused the system to reboot. After that, each infected computer would start scanning like crazy to find more Windows systems that it could infect.\nIt was those scanning probe packets that were jamming up our internet connection. That simultaneous reboot that I had seen was actually a handful of computers getting infected at nearly exactly the same time.\nWe lost of few days of our lives to investigating this, working around it, and cleaning and patching the workstations.\nAt some point in our investigations, we determined that the virus was spread from a laptop that one of our sales/marketing guys was using. He had taken it home or to a client site, and plugged it into a network and got infected. Then he came into the office and plugged it in - behind our firewall.\nIt was like the babysitter late at night in the horror movie. The phone call is coming from the basement - inside the house!\nWe had no defence against this.\nSmart Home Devices # None of us have no problem going to the electronics store or the hardware store or the appliance store and coming back with some new \u0026ldquo;smart\u0026rdquo; device. A fridge. A robot vaccuum. Even a smart plug.\nAnd when we get it home, we take it out of the box, connect to it from our phones and give it our wifi password.\nNow it\u0026rsquo;s in our networks. Behind the firewall\u0026hellip;inside the house!\nYeah. But what can a smart plug do?\nIndeed. What if it opens a VPN tunnel back to some hacker den in North Korea? Really, I have no doubt that you could stuff enough brains inside a smart plug to do that. And once that\u0026rsquo;s done, the hackers half-way around the world can start poking around inside your entire home network. And you would probably never know.\nI\u0026rsquo;m seeing articles on-line that some refrigerators with display screens on them are starting to show ads. What if you don\u0026rsquo;t want ads on your fridge? What if I don\u0026rsquo;t want my printer reporting back to HP about how many pages I print each month? And let\u0026rsquo;s not talk about \u0026ldquo;Smart\u0026rdquo; TVs.\nI did a survey on my network, and between smart plugs and switches, robot vaccuums, garage door openers, streaming devices, smart speakers, printers and a few other things, I counted up about 45 devices.\nWhat do I know about any of them?\nHonestly, not much. So why trust them?\nZero Trust # When it comes to this stuff, \u0026ldquo;trust\u0026rdquo; is a very misguided concept.\nDo you need to trust these things? The answer is, \u0026ldquo;No!\u0026rdquo;\nWhat you need is a \u0026ldquo;Zero Trust\u0026rdquo; network design behind your firewall that lets you sequester these devices such that they can do no harm. Then you don\u0026rsquo;t care if they are malicious, because they\u0026rsquo;ve been neutralized.\nLet\u0026rsquo;s look at how to do that\u0026hellip;\nTCP/IP Subnets and Routing # A quick note to start: This article is about IPv4 routing, not IPv6. So if you are using IPv6, then you won\u0026rsquo;t need the information here.\nBefore we can go any further, we\u0026rsquo;ll need to have a basic understanding about how devices find each other and communicate over TCP/IP.\nIf you\u0026rsquo;ve ever manually set up a device on a network without DHCP, you\u0026rsquo;ll know that you need to provide four pieces of information:\nThe IP Address This is the literally the address of the computer on the network. It\u0026rsquo;s going to be in the format of ###.###.###.### where each ### is a number between 1-255 (ie. one byte). An IP address is always 4 bytes long.\nThe NetMask This tells the device the address space of the subnet on which it sits. It will use this information to determine if a device that it wants to communicate with is on its own subnet or not. We\u0026rsquo;re going to use a netmask of 255.255.255.0 or /24 in CIDR notation for all the subnets in this article which means that the first 3 bytes of the address (or the first 24 bits of the address) represent the subnet while the last byte (8 bits) represents the device\u0026rsquo;s unique place in that subnet.\nThe Default Gateway This should be considered to be a feature of the subnet, rather than the device that you are configuring (for purposes of this discussion). It tells the device the IP address of where to send its packets if it wants to communicate with a device that it has determined is NOT on its own subnet.\nThe DNS Server Addresses This is where to send requests for DNS lookups. It\u0026rsquo;s not relevant to this discussion.\nHow Devices Determine Routing # For purposes of this discussion, it\u0026rsquo;s probably best if you consider TCP/IP to be a \u0026ldquo;broadcast\u0026rdquo; protocol. This means, especially from the viewpoint of each individual device, that any packets it sends out are distributed throughout the subnet with an address attached to them. Only the device that has that address will pay any attention to it. In reality, modern switches and networking equipment handle the packet delivery without the need to actually send every packet to every device - but the devices don\u0026rsquo;t see that.\nEach device knows its own IP address and its netmask so that it can determine if any other device with which it wants to communicate has an address on the same subnet as itself. If so, then it just broadcasts its packets on its subnet with the destination IP address on it. The other device will see its own address and get the packet.\nIf it determines that the other device is NOT on the same subnet, then it still broadcasts it on its own subnet, but this time addressed to the subnet\u0026rsquo;s default gateway - with the actual address of the destination somehow encapsulated inside it. The gateway will get the packet, and then relay it off in some manner so that it gets to its destination.\nHere\u0026rsquo;s an example of what that would look like:\nIn this diagram, the top two PC\u0026rsquo;s belong to the 192.168.1.0 network, which is addresses 192.168.1.0 to 192.168.1.255. This is because they have the netmask 255.255.255.0 which you can see from the CIDR notation /24 in the addresses. These two computers can talk to each other directly.\nIf they want to talk to Server1, with address 10.196.212.116, then they will determine that it is NOT on their own subnet, so they have to send their packets to their default gateway, which is 2811 Router with the address 192.168.1.1. That router will then send it on to 10.196.212.116 through its second network interface, which has the address 10.196.212.1.\nWhat\u0026rsquo;s very important to understand here is that all three PC\u0026rsquo;s are connected to the same switch! Even so, only PC1 and PC2 can talk to each other directly, and any other communication requires relaying through the Router.\nThis is because the physical topology of the network is irrelevant to the TCP/IP routing.\nRouting With Firewalls # For this discussion, your really big takeaway should be this\u0026hellip;\nThe router in the diagram with the name 2811 Router could be a firewall. As a firewall, it would have rules that control if and how devices communicate between subnets.\nAs a matter of fact, firewalls can only control traffic between subnets and not between nodes on the same subnet. For instance, in the diagram above, there is no way that the firewall at 2811 Router could control traffic between PC1 and PC2 because they can communicate directly without involving the router/firewall. On the other hand, rules in the firewall at 2811 Router will control communication between Server 1 and either of PC1 or PC2.\nFrom this, you can deduce that the IP subnet is the basic building block of home network security.\nIntroducing VLAN\u0026rsquo;s # If you do some web searches about VLAN\u0026rsquo;s, you\u0026rsquo;ll find something like this introduction from Wikipedia:\nA virtual local area network (VLAN) is a local area network broadcast domain that is partitioned and isolated in a virtual network at the data link layer (OSI layer 2). A VLAN behaves like a virtual network switch or network link that can share the same physical structure with other VLANs while staying logically separate from them.\nVLANs work by applying tags to network frames that are forwarded within the broadcast domain, creating the appearance and functionality of network traffic that behaves as if it were split between separate networks. In this way, VLANs can keep network applications separate despite being connected to the same physical network, and without requiring multiple sets of cabling and networking devices to be deployed.\nEssentially this is describing something that looks like this:\nYou can see that although all of the ports are on the same switch, ports 1-4 cannot communicate with ports 5-8. Essentially, this turns the single switch into two switches.\nYou can also split a VLAN across switches, like this:\nThis divides both switches into 3 parts, and the ports in both switches can communicate with all of the ports of the same colour across both switches.\nAnd that\u0026rsquo;s about all you\u0026rsquo;ll get before you\u0026rsquo;re really deep, deep into the weeds. You\u0026rsquo;ll quickly find out about trunks and IEEE 802.1Q and lots of other complicated stuff.\nThis seems like a cool idea\u0026hellip;until you start wondering about how something on one VLAN can communicate with something on different VLAN.\nAfter all, if you can\u0026rsquo;t do that, then your VLAN is 100% sequestered and inaccessible to and from the rest of the world. And that is quite a bit less than useful.\nManaged Switches # Before we get into the details, we need to talk about one more thing\u0026hellip;\nYour standard, consumer grade network switch that you can buy for about $10 on any street corner probably won\u0026rsquo;t be able to handle VLAN\u0026rsquo;s. You\u0026rsquo;ll need something called a \u0026ldquo;Managed Switch\u0026rdquo;, which is generally marketted at corporate customers. They\u0026rsquo;re a bit more expensive than unmanaged switches, but you should be able to pick them up for $30 or less. You will, however, have to ditch any unmanaged switches that you already have - you could find a way to use them, but it\u0026rsquo;s not worth the hassle.\nA managed switch will generally allow you to do a whole bunch of things in addition to VLAN\u0026rsquo;s, things like \u0026ldquo;spanning trees\u0026rdquo; and trunking. We\u0026rsquo;re not going to talk about that stuff here, though. Just VLAN\u0026rsquo;s.\nKinds of Port Membership in VLAN\u0026rsquo;s # If you read the Wikipedia snippet from above, you\u0026rsquo;ll have noticed that VLAN\u0026rsquo;s work by attaching \u0026ldquo;tags\u0026rdquo; (whatever they are) to network packets before they are broadcast around the network. The \u0026ldquo;tag\u0026rdquo; is just an numeric ID - generally between 2 and 1000 - and switch ports that understand VLAN\u0026rsquo;s will be programmed to only transmit and receive packets with particular tags.\nTag 1 is special. It\u0026rsquo;s the default \u0026ldquo;no VLAN\u0026rdquo; tag, and get\u0026rsquo;s slapped on any untagged packet that travels around VLAN aware network equipment that hasn\u0026rsquo;t been configured to do anything different. You cannot use tag 1 for anything else.\nFrom my experience, when you configure a managed switch to use VLAN\u0026rsquo;s you need to define each VLAN tag within the switch, and then configure how each port interacts with each VLAN that you have defined. Let\u0026rsquo;s look at how this works\u0026hellip;\nPorts on managed switches can handle VLAN\u0026rsquo;s in two ways:\nUntagged Packets This is what you see in most of the introductory articles and descriptions. In this case, the port is configured to add a VLAN tag to any packets that come into it that aren\u0026rsquo;t already tagged. This means that if you have some device that is not VLAN aware, the port on the switch will assign the device to a VLAN for it. The default setting for this is almost always to add VLAN 1.\nTagged Packets This is a mode where the switch port is assigned membership of one or more VLAN\u0026rsquo;s and it will allow incoming packets tagged with those VLAN id\u0026rsquo;s to pass through. Packets tagged with any other VLAN\u0026rsquo;s will be blocked. This means that if you plug a VLAN aware device into the port, that device can decide which VLAN\u0026rsquo;s it wants to communicate on, assuming the port is a member of those VLAN\u0026rsquo;s.\nConfiguring the Switch # Obviously, the interface for configuring each brand of switch is going to be different. Generally, though, you\u0026rsquo;ll be looking for a section with a name like \u0026ldquo;802.1Q VLAN\u0026rdquo;.\nIn my D-Link switch the interface looks like this:\nIf you click on one of the VLAN number links, you get a screen like this:\nThe method with this switch is that you define each VLAN, give it a name and then define how each port interacts with the VLAN.\nIn this switch, the default VLAN 1 remains the VLAN to which all untagged packets are attached. Then all of the ports are configured as tagged members of all of the custom VLAN\u0026rsquo;s that I created. Essentially, every port will be able to communicate on every VLAN if the packets are already tagged, and will leave the untagged packets untagged.\nI should point out that this is not the final configuration that I intend for this switch. I\u0026rsquo;m still in the process of sorting out all of my network security and I\u0026rsquo;m leaving this wide open while I configure everything else and test it. I don\u0026rsquo;t want something to fail and waste lots of time looking for setup issues in my firewall whilst the actual problem was the wrong VLAN on a port on this switch. This switch is the one closest to my entertainment centre, and has most of the \u0026ldquo;VLAN unaware\u0026rdquo; wired devices on my network. Things like my XBox and my IPTV box. Eventually, I\u0026rsquo;ll be reconfiguring this switch such that those devices go into the IOT VLAN (I think).\nUnderstanding Tagged VLAN Membership # You might find it a little hard to picture how this second case is used at first.\nHowever, if you are using Proxmox for virtualization, you\u0026rsquo;ll have seen this screen for configuring the networking on a Proxmox host:\nHere the Linux Bridge vmbr0, which defines how the VM\u0026rsquo;s on the node will connect to the ethernet adapter, is defined. Note the checkbox beside \u0026ldquo;VLAN aware:\u0026rdquo;, and the range of \u0026ldquo;2-4094\u0026rdquo; that is assigned to \u0026ldquo;VLAN IDs:\u0026rdquo;.\nThen, when you define the networking for a VM on that Proxmox host, you\u0026rsquo;ll see this screen:\nIn this screen eth0 is being defined as connecting to vmbr0 and having VLAN tag 20 attached.\nWhat\u0026rsquo;s important to note is that if we define another Linux container or VM on this same node, attached to the same bridge, but assigned to VLAN tag 30, then it won\u0026rsquo;t be able to communicate directly with the first VM. Even though they are sharing the exact same hardware.\nHowever, these VM\u0026rsquo;s will not be able to communicate with anything unless the port on the switch that the host is plugged into is a member of both VLAN\u0026rsquo;s 20 and 30 as \u0026ldquo;tagged\u0026rdquo;.\nFirewalls and Routers # The second, and probably most important place you\u0026rsquo;ll encounter a \u0026ldquo;VLAN aware\u0026rdquo; device is a firewall or a router. For sure, both OPNsense and pfSense are very VLAN aware.\nUnderstand that a firewall is always, by definition, a router, and that a router is designed to transfer traffic between ports. Essentially, firewalls use VLAN\u0026rsquo;s to define multiple network interfaces on the same hardware port. Each VLAN becomes a \u0026ldquo;device\u0026rdquo; which can have its own ethernet address.\nIn OPNsense, you define a VLAN as a device, and attach it to a physical ethernet port and connect it logically to a VLAN defined on the switch. From that point on, you can treat your VLAN device just like any other network port. You can assign it an IP address, you can create firewall rules for it, and you can create a DHCP server for it.\nThis is what enables you to use VLAN\u0026rsquo;s to control security inside your network. Once you\u0026rsquo;ve done this, you\u0026rsquo;ve created the authoritative connection between a VLAN and a TCP/IP subnet.\nLet\u0026rsquo;s say that you have that VLAN 20 from the screenshot above. On your firewall, you define a network interface called VLAN_DMZ and assign it to your inside ethernet port using VLAN 20. You\u0026rsquo;re going to use VLAN_DMZ as the gateway for the network 10.10.10.0/24, so you give it the address 10.10.10.1.\nNow, if any other device inside your network is on VLAN 20, and if it wants to talk to anything outside VLAN 20 it is going to have to be on 10.10.10.0/24. Otherwise it cannot communicate with the gateway at 10.10.10.1.\nYou can see from this that the firewall configuration \u0026ldquo;locks in\u0026rdquo; VLAN 20 to the 10.10.10.0/24 subnet, and the firewall rules will define how it can behave as a \u0026ldquo;DMZ\u0026rdquo;.\nSo far, none of this particularly uses the isolation qualities of VLAN\u0026rsquo;s. It really just enables us to have just about as many subnets as we want on our network without having to stuff a new NIC inside our firewall for each subnet.\nIn fact, you could just ignore the security aspects of the separation qualities of VLAN\u0026rsquo;s and configure every single port on every single switch that you own to accept traffic from every single VLAN that you create and you probably wouldn\u0026rsquo;t sacrifice too much actual security. I\u0026rsquo;m not suggesting that you do this, but it wouldn\u0026rsquo;t be the end of the world if you did.\nDHCP # The one place where it really is useful to have separation between the VLAN\u0026rsquo;s is the one commonly used TCP/IP service which is truly treated as a broadcast - DHCP.\nWhen a device is attached to the network that has been configured to use DHCP to get its network information it doesn\u0026rsquo;t initially have an IP address or even any knowledge about the network that it\u0026rsquo;s plugging into. How does it communicate with the DHCP server on the network without an IP address?\nThe answer is that it uses pure local broadcast on the IP network. Essentially, all of the communication is performed by using the address 255.255.255.255 which will be seen by every host on the local network. Most importantly, packets addressed to 255.255.255.255 will never pass through a router - they are strictly local.\nFirst, the device broadcasts a \u0026ldquo;discover\u0026rdquo; message to the entire network that essentially says, \u0026ldquo;I\u0026rsquo;m here! Can anybody give me an address?\u0026rdquo; Every DHCP server that sees that broadcast will respond by broadcasting an \u0026ldquo;offer\u0026rdquo; of an IP address. The new device then picks one of the offers, and \u0026ldquo;requests\u0026rdquo; that address be assigned to it (once again by broadcast). The DHCP server that made the offer then broadcasts an \u0026ldquo;acknowledgement\u0026rdquo; of the assignment.\nThis process is known as \u0026ldquo;DORA\u0026rdquo;, which means Discover-Offer-Request-Acknowledge.\nThe key point for us is the part that says, \u0026ldquo;Every DHCP server that sees that broadcast\u0026hellip;\u0026rdquo;. Now go back and look at the first VLAN picture in this article. See how it says, \u0026ldquo;Broadcast message in\u0026rdquo;, and \u0026ldquo;Broadcast message out\u0026rdquo;? And see how the VLAN\u0026rsquo;s prevent the broadcasts from crossing between them?\nWhat this means is that every single VLAN on your network needs its own DHCP server if you want to use DHCP. In our example, we would create a DHCP service for VLAN_DMZ, which listens and broadcasts on VLAN 20.\nOnce again OPNSense works great here. Using \u0026ldquo;Dnsmasq DNS and DHCP\u0026rdquo; - which is the DHCP service that you should be using now - you don\u0026rsquo;t set up multiple DHCP servers, but you do create up rule sets for different interfaces. OPNSense will then listen on each interface that you\u0026rsquo;ve configured, and provide an address, mask, gateway and DNS server tailored for that VLAN.\nThis is the piece that puts it all together.\nOnce you\u0026rsquo;ve done this, you can now connect a device to your network configured to use DHCP and assigned to a VLAN - either through the device itself or through the switch that it\u0026rsquo;s plugged into - and it will be attached to a particular subnet that you have defined for that VLAN.\nWiFi # But what about all those devices that connect via WiFi? They\u0026rsquo;re generally not VLAN aware, and they don\u0026rsquo;t plug into a switch port that you can configure for untagged packets.\nThe answer is that most WiFi access points allow you to associate a WiFi SSID with a VLAN. Even consumer grade WiFi routers allow you to set up a \u0026ldquo;Guest\u0026rdquo; SSID and associate it with a VLAN.\nI\u0026rsquo;ve been using D-Link DBR-X3000-AP access points, and they have the ability to set up 6 \u0026ldquo;Guest\u0026rdquo; zones, each with an SSID and VLAN tag:\nNow, when I\u0026rsquo;m setting up a new smart plug or other device, I just configure it to connect to my \u0026ldquo;IOT\u0026rdquo; SSID, and to use DHCP. Everything else just works. You\u0026rsquo;ll also have to make sure that whatever switch port you plug the AP into is configured to recognize the VLAN\u0026rsquo;s that you use for all of the SSID\u0026rsquo;s that you create.\nThe Default Setup with ISP Equipment # Chances are that you started out with, and might still have, the WiFi router that your ISP sent you when you signed up. It might, or it might not be a separate component from your Internet modem - you\u0026rsquo;re better off if it is separate.\nLet\u0026rsquo;s look at what your network looks like with this equipment:\nThe problem is twofold:\nThe WiFi router is integrated with the internet router. The consumer grade, plug and play, equipment cannot be configured adequately. These two items mean that the equipment is great for people who don\u0026rsquo;t have the technical knowledge to do much more that plug the bits together, and who can\u0026rsquo;t figure out how to even change the WiFi password. And that\u0026rsquo;s probably the vast, vast majority of customers that the ISP\u0026rsquo;s are dealing with.\nBut if you are building your own, self-hosted, services, you\u0026rsquo;ll need something different. For instance, with a standard, consumer, WiFi router, you\u0026rsquo;ll probably have a single \u0026ldquo;guest\u0026rdquo; SSID avaliable, and it might even allow you to associate it with a VLAN. But if you really want to use VLAN\u0026rsquo;s and subnets properly, you\u0026rsquo;ll need a few more SSID\u0026rsquo;s. Also, if you are implementing your own firewall, you\u0026rsquo;ll probably need to turn off all of the routing in your WiFi router, along with things like NAT and DHCP. You\u0026rsquo;ll probably need to put it into \u0026ldquo;Bridge\u0026rdquo; or \u0026ldquo;AP\u0026rdquo; mode if that\u0026rsquo;s possible.\nThe WiFi router that my latest IP sent me is so simple it doesn\u0026rsquo;t even have a \u0026ldquo;Bridge\u0026rdquo; mode. I didn\u0026rsquo;t even bother with it so it\u0026rsquo;s just stayed in the box in a closet.\nGenerally speaking, you\u0026rsquo;ll want to replace your WiFi router with a WiFi \u0026ldquo;Access Point\u0026rdquo; or \u0026ldquo;AP\u0026rdquo;. As soon as you go looking for these, you\u0026rsquo;ll discover that you\u0026rsquo;re not in the consumer marketplace any more. Marketting bumpf will talk about how good the devices are for conference rooms or multi-unit dwellings. Access points are fairly cheap, too.\nSetting up OPNSense to Use VLAN\u0026rsquo;s # Let\u0026rsquo;s take a quick survey of how you would set up OPNSense to enable VLAN\u0026rsquo;s and subnets on your network with just two physical ethernet ports on your server, one of which is the WAN port - and we won\u0026rsquo;t talk about that. This isn\u0026rsquo;t a step-by-step guide, but really just an overview of the things that you need to achieve in order to implement VLAN\u0026rsquo;s and subnets. If you want step-by-step instructions, you can find tons of them on YouTube.\nCreating VLAN Interfaces # When you start out, you\u0026rsquo;ll have an \u0026ldquo;Interface\u0026rdquo; configured for the physical internal ethernet port. This is generally called \u0026ldquo;LAN\u0026rdquo;. You\u0026rsquo;ll neet to create an Interface for each VLAN that you are going to set up.\nCreating the VLAN Device # Before you can create an Interface, you\u0026rsquo;ll need to set up a VLAN device. In the \u0026ldquo;Interfaces\u0026rdquo; menu there is a submenu called \u0026ldquo;Devices\u0026rdquo; and an item in that called \u0026ldquo;VLAN\u0026rdquo;. Click on that option and you\u0026rsquo;ll get a list of all of the VLAN devices that have already been defined, and then click on the \u0026ldquo;+\u0026rdquo; button to add a new one.\nYou\u0026rsquo;ll get a screen that looks like this:\nThe device name is a bit tricky, it needs to start with \u0026ldquo;vlan0\u0026rdquo; and then have some more numeric stuff after that. The parent should be the device associated with your LAN interface. Then you give it whatever VLAN tag that you are going to use in your switches. Give it a description that makes sense to you.\nAssigning the VLAN Device to an Interface # The next step seems like something that should probably happen automatically, but doesn\u0026rsquo;t. In the \u0026ldquo;Interfaces\u0026rdquo; menu there is an item called \u0026ldquo;Assignments\u0026rdquo;. Click on that and you\u0026rsquo;ll get a screen with a list of devices that have been assigned to interfaces. Down at the bottom is a little section to add new assignments:\nThere\u0026rsquo;s not much to it. Just a dropdown and a textbox for the description. Note that this description is what you are going to see all the time when you deal with just about anything else in OPNSense. So pick a name that makes sense to you.\nAt this point, you will have created an Interface for your VLAN! But it still needs to be configured.\nConnecting Your VLAN to a Subnet # Once you\u0026rsquo;ve hit \u0026ldquo;Apply\u0026rdquo;, you\u0026rsquo;ll get a new item in the \u0026ldquo;Interfaces\u0026rdquo; menu with the name of your Interface. Go ahead and click on it:\nNot shown here is the checkbox to enable the Interface. Be sure to click on that to enable the Interface.\nI\u0026rsquo;m just using IPv4 here, so you I\u0026rsquo;m not going to do any IPv6. Select \u0026ldquo;Static\u0026rdquo; for the IPv4 configuration type and then you\u0026rsquo;ll see the box at the bottom with address textbox.\nThat address textbox is where you connect the VLAN to the IP subnet. You are not just giving the Interface an address, you are defining the subnet to which it belongs. That\u0026rsquo;s what the dropdown box to the right is for. I picked \u0026ldquo;24\u0026rdquo; because I want 10.122.73.0 to be the subnet, with a mask of 255.255.255.0.\nThat\u0026rsquo;s all you need to specify: enable, static IPv4, address and mask. Hit \u0026ldquo;Save\u0026rdquo; and then \u0026ldquo;Apply\u0026rdquo;.\nNow your Interface and VLAN is set up and ready to go. You\u0026rsquo;ll see that new Interface name pop up all over the OPNSense UI whenever it\u0026rsquo;s an option. Most importantly, it will be an option in the Firewall --\u0026gt; Rules menu. Don\u0026rsquo;t forget that at this point, it has no rules except the default, \u0026ldquo;Deny Everything\u0026rdquo; rule, meaning that this subnet is 100% sequestered for outgoing traffic.\nSetting Up DHCP # The last step is to configure the OPNSense DHCP service to listen on your new Interface/VLAN.\nAt the time that I\u0026rsquo;m writing this, the older DHCP services are being phase out, and KEA is probably overkill for simple home networks. Which means that you should be using Dnsmasq DHCP. So that\u0026rsquo;s what I\u0026rsquo;ll show here.\nThe DHCP configuration is in the \u0026ldquo;Services\u0026rdquo; menu. The first thing you\u0026rsquo;ll need to do is some basic configuration for the service. Click on Services --\u0026gt; Dnsmasq DNS \u0026amp; DHCP to open up the submenu and then click on General. You\u0026rsquo;ll get a screen that will allow you to enable the service, and then select all of the Interfaces that you want it to work with. Save this and you\u0026rsquo;re ready to set up your subnets.\nSetting Up DHCP Options # Just as when you set up static IP on a device where you need to specify subnet and mask, gateway and DNS servers along with device IP address, DHCP has to provide the same information. The subnet and mask are handled automatically, but you need to configure the rest.\nIn OPNSense Dmasq the gateway and the DNS servers are treated as \u0026ldquo;options\u0026rdquo;. You\u0026rsquo;ll need to configure these for each Interface. There\u0026rsquo;s actually a fair number of options available, and you\u0026rsquo;ll have to pick the correct two.\nIn the DMasq screen in OPNSense there\u0026rsquo;s a tab (and a menu item) called \u0026ldquo;DHCP Options\u0026rdquo;. This will show a list of the options that you have configured for each Interface. There\u0026rsquo;s the usual \u0026ldquo;+\u0026rdquo; button at the bottom right. Click it and you\u0026rsquo;ll see a dialogue like this:\nPick the Interface for your VLAN and select \u0026ldquo;set\u0026rdquo; as the action. You need to do this twice. Once for \u0026ldquo;router [3]\u0026rdquo; as default gateway address, and once for \u0026ldquo;dns-server [6]\u0026rdquo;. It\u0026rsquo;s probably a good idea to specify a description so that you can see what they are at a glance from the main listing.\nThat\u0026rsquo;s It # At this point, you have your firewall configured to support your VLAN\u0026rsquo;s. It\u0026rsquo;s beyond the scope of this article to talk about firewall rules, but just remember that each of these subnets is automatically created in a fully sequestered mode. Nothing can get out, and nothing can get in. You\u0026rsquo;ll have to add the rules to allow devices on these subnets to communicate outside the subnet.\nDon\u0026rsquo;t Forget the Switch # Whatever switch port that LAN on your OPNSense firewall is plugged into needs to be configured to allow traffic tagged with every VLAN that you set up in the firewall to pass through. Otherwise, none of this will work.\nIgnoring VLAN\u0026rsquo;s # The one caveat to all of this is that once a subnet has been connected to a VLAN, nothing outside that VLAN can directly communicate with any address on that subnet that is on the VLAN.\nLet\u0026rsquo;s say that you have three devices on ports 1, 2 and 3 of a managed switch. Ports 1 and 2 are configured to use VLAN 77 for untagged packets while port three is left at the deault configuration, essentially meaning no VLAN processing. The devices are all configured statically to have IP addresses 10.10.10.10, 10.10.10.11 and 10.10.10.12 in order in ports 1, 2 and 3. In this scenario, the devices in ports 1 and 2 can communicate, but the device in port 3 cannot communicate with either one of them, even though it is on the same subnet.\nFurthermore, that device on port 3 can only communicate with devices that are on the same subnet and also untagged. This is because the gateway - presumably at 10.10.10.1 - is going to also be on VLAN 77 if everything is configured correctly. This means that the device on port 3 cannot communicate with the default gateway for its subnet, and therefore cannot communicate with any device on another subnet.\nAt the same time, the devices on ports 1 and 2 can communicate with any device on any other subnet reachable by the firewall, regardless of what VLAN they are on. This is because the traffic leaving the firewall Interface on the other subnet is going to be tagged with the correct VLAN id for that subnet.\nYou can see that it\u0026rsquo;s important that once you start using VLAN\u0026rsquo;s to then use them everywhere, especially if you are configuring devices on subnets that aren\u0026rsquo;t associated with VLAN1, the default \u0026ldquo;No VLAN\u0026rdquo; VLAN.\nConclusion # The first thing to remember is that network security behind your firewall is every bit as important as security between your firewall and the outside world.\nIt\u0026rsquo;s incredibly easy to hear about VLAN\u0026rsquo;s and how important they are to your network security, but it\u0026rsquo;s much harder to find out how they do that. I\u0026rsquo;m hoping that this article helps to clear that up. But to recap:\nThe core of network security is based on controlling TCP/IP traffic. That control is virtually always achieved via a firewall to regulate traffic between IP subnets. VLAN\u0026rsquo;s work inside your network switches by restricting whether or not data packets will be delivered to particular switch ports and each port on a managed switch is configured such that it will accept packets (incoming or outgoing) with particular VLAN \u0026ldquo;tags\u0026rdquo;. Each port can also be configured to assign a specific tag to incoming packets that have no tags on them.\nYour firewall is the component that connects VLAN tagging with TCP/IP subnets and TCP/IP traffic control, which, once again is the main way that you implement your security. Each VLAN effectively becomes synonymous with a TCP/IP subnet.\nYou use this technology to divide your internal network up into subnets that represent different \u0026ldquo;zones\u0026rdquo;. Those zones, in turn, hold devices that share a similar trust profile and they are isolated from other zones via your firewall rules.\n","date":"6 February 2026","externalUrl":null,"permalink":"/homelab/vlans/","section":"Homelabs","summary":"How to secure your home network from threats from inside the house.","title":"VLAN's and Subnets For Home Networks","type":"homelab"},{"content":" What\u0026rsquo;s JavaFX? # JavaFX is the modern API for creating interactive desktop applications in Java. While it doesn\u0026rsquo;t replace Swing, it represents a significant upgrade for the programmer as it uses reactive programming.\nIs it Hard to Learn? # It can be challenging. JavaFX has never been very popular, as desktop applications have largely gone out of style, so there simply isn\u0026rsquo;t a great wealth of resources out there to learn how to build applications using JavaFX. However, if you\u0026rsquo;ve had experience using frameworks like React or Jetpack Compose, which are also based on reactive principles, it should be easier for you to learn.\nDon\u0026rsquo;t use ScreenBuilder or FXML # Okay, so this is controversial and a lots of people disagree with me. I don\u0026rsquo;t use ScreenBuilder and FXML and I strongly recommend that beginners avoid it.\nWhat are these things?\n\u0026ldquo;ScreenBuilder\u0026rdquo; is the graphical, drag-and-drop, tool for creating screen layouts. The output from ScreenBuilder is a text file in an XML-like format called \u0026ldquo;FXML\u0026rdquo;. JavaFX contains objects and methods to load FXML files into your application and merge them with Java code.\nIt is much better to learn how to build your screen with code. It\u0026rsquo;s actually quite easy, and if you apply DRY (Don\u0026rsquo;t Repeat Yourself), you\u0026rsquo;ll quickly build up a library of builder and helper methods, as well as custom classes, that will strip about 90% of the boilerplate code out of your layout logic. And this is even easier if you\u0026rsquo;re using Kotlin! The resulting pure Java/Kotlin code is easier to understand and maintain than any FXML ever will be.\nReactive programming # From Wikipedia:\nReactive programming is a declarative programming paradigm concerned with data streams and the propagation of change\u0026hellip;.\nFor example, in an imperative programming setting, a := b + c would mean that a is being assigned the result of b + c in the instant the expression is evaluated, and later, the values of b and c can be changed with no effect on the value of a. On the other hand, in reactive programming, the value of a is automatically updated whenever the values of b or c change, without the program having to explicit re-execute the statement a := b + c to determine the presently assigned value of a.\nThis exactly describes how JavaFX is implemented, and how you should approach building JavaFX applications. Somehow, this seems to have been completely missed by the programming world in general, and you won\u0026rsquo;t find JavaFX included in the Wikipedia list of reactive frameworks, and no mention of the reactive nature of JavaFX is included in the Wikipedia page about JavaFX.\nReactive programming seems to be a challenge for many programmers.\nMuch of your GUI code is layout and configuration, and sprinkled in with that are code snippets that define the reactive elements. The layout code is executed immediately, but the reactive snippets are only triggered when the reactive data streams are modified.\nThe resulting program feels a bit like a Rube Goldberg machine in code, or like setting up dominoes to fall over.\nLearn More # If you want to learn how build desktop applications with JavaFX, this website has a lot of information that can help you get started. Check back often, as I plan to add a lot more content on this subject.\nThe Elements of JavaFX # First off, take a look at The Elements of JavaFX. This will give you an overview of all the things you\u0026rsquo;ll need to be familiar with in order to write good JavaFX applications. You don\u0026rsquo;t need to master them all at once before you start, but it\u0026rsquo;s a good idea to be at least aware of how all of theses things fit together.\nApplication Structure # Where do you start? What should a JavaFX application look like?\nCheck out my blog post: JavaFX Without FXML. This will give you step by step instructions for building a complete (but simple) application with data input, showing you some good practices to follow and how to keep everything clean and easy to read.\nNext, Using MVC with JavaFX, will show you how to add some structure to your application, using Model-View-Controller. MVC works really well with JavaFX, and is a natural fit to the Reactive nature of JavaFX.\n","date":"5 August 2025","externalUrl":null,"permalink":"/javafx/","section":"JavaFX","summary":"What’s JavaFX? # JavaFX is the modern API for creating interactive desktop applications in Java. While it doesn’t replace Swing, it represents a significant upgrade for the programmer as it uses reactive programming.\n","title":"JavaFX","type":"javafx"},{"content":"Most Reactive frameworks work by holding a data representation of the \u0026ldquo;State\u0026rdquo; of the GUI in some kind of structure, then monitoring that structure for changes and updating the GUI whenever they occur. Conversely, the GUI itself may update the \u0026ldquo;State\u0026rdquo; in response to actions by the user.\nIn JavaFX, the work of connecting the GUI to your \u0026ldquo;State\u0026rdquo; is done via an extensive library of \u0026ldquo;Observable\u0026rdquo; objects, along with classes designed to allow you to link \u0026ldquo;Observable\u0026rdquo; objects together. Virtually every configurable element of every screen component is implemented as Observable objects, which means that you can easily connect them directly to your data representation of \u0026ldquo;State\u0026rdquo;.\nThese libraries are huge, and that means that it takes some time to learn about them. However, you can start out doing some simple things right away, and then learn more advanced techniques as you go along.\nFinally, every application eventually needs actions. JavaFX has a library of classes designed to capture and handle actions, called Events. It also has classes called Listeners that allow you to turn changes in \u0026ldquo;State\u0026rdquo; into actions.\n","date":"5 August 2025","externalUrl":null,"permalink":"/javafx/elements/observables/","section":"JavaFX","summary":"Observables and their related classes are the core of the Reactive aspect of JavaFX, and it’s critical to understand how they work if you want to take advantage of the benefits of Reactive design.","title":"Observables, Bindings and Listeners","type":"javafx"},{"content":" The Elements of JavaFX You Need to Master # It seems obvious that if you are going to use JavaFX, then you\u0026rsquo;ll need to know how to deal with the widgets that you use to build a GUI. And there are lots of Nodes that you can use, and some of them are quite complicated.\nBut there is much more to JavaFX than just screen widgets.\nMore than anything else, you\u0026rsquo;ll need to understand how to represent your data as \u0026ldquo;Observable\u0026rdquo; objects that allow you to connect parts of your screen, and your application together.\nBeyond that, you\u0026rsquo;ll need to know how to happen actions that stem from user Button clicks, and data entry, and how to safely do those actions in a background thread when they might potentially block, or simply take a long time to run.\nThe Nodes # There are large amount of classes in JavaFX arranged in a hierarchy starting with a top level class called, Node. There are a number of layout classes that all work differently, such as BorderPane, ScrollPane, HBox, VBox, GridPane and StackPane which can all be embedded inside of each other to create exactly the layout you want.\nThere are classes to display images, text or text and images together. ListView, TreeView and TableView allow collections of data to be displayed on screen (and edited) in highly customizable ways.\nFor user input, you can choose from TextField, ComboBox, ChoiceBox, Spinner, Checkbox, RadioButton, Buttons and ToggleButtons.\nAll of these Nodes have a large number of methods and properties for controlling their presentation and how they act. Learning how to use them and configure them can be a daunting task. Luckily, most of the tutorials on the web seem to focus on this aspect of JavaFX, so help is easy to find.\nObservable Objects and Concepts # This is the Reactive part of JavaFX, and it\u0026rsquo;s critical to understand how this works.\nThere are three aspects to the observable nature of JavaFX. The first is a huge set of classes that wrap around values and implement the \u0026ldquo;Observer Design Pattern\u0026rdquo; into classes collectively called Properties. This enables you to do two important things: The first is to use Bindings to connect Properties to each other such that when value changes, the values in the connected Properties will change automatically. The second is to create ChangeListeners that watch these observable values and trigger code to run whenever they detect a change in them.\nThe key to using JavaFX to create Reactive applications is to establish a definitive place for the \u0026ldquo;State\u0026rdquo; of your GUI to reside, and then use Properties, Bindings and ChangeListeners to connect your GUI elements back to that \u0026ldquo;State\u0026rdquo; instead of copying the values in and out of it in your code.\nBut to do that, you\u0026rsquo;ll need to understand how these Properties, Bindings and ChangeListeners work.\nEvents and Actions # At its heart, JavaFX is \u0026ldquo;Event Driven Programming\u0026rdquo;, and even bindings probably boil down to event handling deep inside the JavaFX library. Events can be triggered by Property changes, through mouse move movements and clicks, by keyboard actions, and the actions on Nodes like Buttons and MenuItems. Events are captured in the code through \u0026ldquo;Handlers\u0026rdquo;, and each EventHandler specifies code that should be run when the event occurs.\nIt\u0026rsquo;s important to understand where actions and Events are more appropriate than State changes and Bindings. Something like a Button click is unmistakably an \u0026ldquo;Action\u0026rdquo;, and should probably be treated as such. It would be possible to configure a click handler on a Button to toggle a Property in State, which is then captured with a ChangeListener in an application logic class, but that\u0026rsquo;s probably the most complicated way to program the process.\nCSS # All of the JavaFX Nodes can be styled with cascading style sheets. These are largely compatible with web standards for CSS, but there are some differences to learn about. How you implement CSS styling largely becomes a matter of taste. You can override the standard styling for entire classes of Nodes, or you can create custom selectors and attach them to specific Nodes in your code. Or you can do both.\nGetting started with stylesheets is pretty easy. There are, as usual, some things you need to know before you get going but they\u0026rsquo;ve all been gathered together in this article:\nPseudo-Classes are the best way to represent changes in the State of your application the styling of your GUI. These are built into JavaFX, and allow you to create different sets of stylings that can be attached to your screen Nodes and configured to activate when different conditions apply. Pseudo-classes are used all over the standard JavaFX Nodes, if you\u0026rsquo;ve ever set a Button to disabled and watched it change on the screen, then you\u0026rsquo;ve seen Pseudo-classes in action.\nHandling the GUI Thread # Like virtually every GUI library available, it\u0026rsquo;s really, really bad to do long or blocking operations on the same thread that is maintaining the GUI. This handled by launching a background thread to do the otherwise blocking operation, and then triggering an event at its completion which will update the GUI. The GUI thread in JavaFX is called the \u0026ldquo;FX Application Thread\u0026rdquo;, or \u0026ldquo;FXAT\u0026rdquo;\nTip This is probably the number one issue that beginners in JavaFX struggle with.\nThe biggest implication of this is that you cannot program linearly. Try as hard as you like, but there\u0026rsquo;s no way that you can run anything that looks like a \u0026ldquo;Function\u0026rdquo; (ie. input some data and wait for an answer) triggered from the FXAT that uses a background thread. When your application behaves weirdly and seems laggy or hangs, come back to this paragraph and read it carefully because this is what you\u0026rsquo;re probably trying to do.\nJavaFX provides a handy class called \u0026ldquo;Task\u0026rdquo;, which supplies a runnable which you can pass to a Thread. When the code in the Task has been completed, it triggers a \u0026ldquo;Success\u0026rdquo; Event. You can supply an Event Handler which be invoked when the \u0026ldquo;Success\u0026rdquo; event is triggered. Just like any Event Handler, it will be run on the FXAT and can therefore update the GUI.\n","date":"5 August 2025","externalUrl":null,"permalink":"/javafx/elements/","section":"JavaFX","summary":"There are certain ideas that you simply need to understand in order to use JavaFX effectively.  This section organizes these elements into broad categories and contains many articles that will get you started.","title":"The Elements of JavaFX","type":"javafx"},{"content":"","date":"22 July 2025","externalUrl":null,"permalink":"/javafx/techniques/","section":"JavaFX","summary":"Beyond understanding the Nodes, Properties, EventHandlers other JavaFX components, there are some essential techniques that you need to master in order to create great JavaFX applications.","title":"JavaFX Techniques","type":"javafx"},{"content":"All of the JavaFX Nodes and Observables are not thread safe. Instead JavaFX provides a dedicated thread called the \u0026ldquo;FX Application Thread\u0026rdquo; or \u0026ldquo;FXAT\u0026rdquo; which needs to be used whenever you update any of these objects.\nThis is probably the subject that trips up beginners the most. Not that you have to use the FXAT for updates, but that you should never use the FXAT for long or blocking jobs. This can be easy to skip, because development and testing environments often do not have real domain setups that actually respond in the same time frames as live environments. The result is that everything works fine until you release your application, then it flops in production.\nThe good news is that JavaFX provides a tool to deal with all of this: Task. Essentially, Task is an abstract class that implements Runnable and Future\u0026lt;V\u0026gt;, which means that you can instantiate an anonymous inner class based on it and pass it to the constructor of Thread. Then you can call whatever code you\u0026rsquo;ve configured the Task to run through Thread.start() which, by definition, is running off the FXAT.\nOn top of that, Task contains all of the tools that you need to connect it back to the FXAT. You can monitor progress while the Task is running from the FXAT, and you can specify code to run on the FXAT when the Task completes.\n","date":"2 July 2025","externalUrl":null,"permalink":"/javafx/elements/fxat/","section":"JavaFX","summary":"One of the most important aspects of JavaFX that trips up most beginners is handling the FX Application Thread (or FXAT). It is absolutely critical that you do NOT run long or blocking jobs on the FXAT, as that will cause your application’s GUI to appear to be hung.\nThis section contains a number of articles that explain what the FXAT is, how to manage it and how to use Task to manage running your code off the FXAT, and then getting back on the FXAT when your job is done.\n","title":"The FX Application Thread","type":"javafx"},{"content":"","date":"10 June 2025","externalUrl":null,"permalink":"/categories/javafx/","section":"Categories","summary":"","title":"Javafx","type":"categories"},{"content":" The Articles: # ","date":"10 June 2025","externalUrl":null,"permalink":"/javafx/elements/nodes/tableview-listview/","section":"JavaFX","summary":"All about TableView and ListView","title":"ListView and TableView","type":"javafx"},{"content":"","date":"10 June 2025","externalUrl":null,"permalink":"/categories/nodes/","section":"Categories","summary":"","title":"Nodes","type":"categories"},{"content":"","date":"10 June 2025","externalUrl":null,"permalink":"/categories/tableview/","section":"Categories","summary":"","title":"Tableview","type":"categories"},{"content":" JavaFX nodes # There are large amount of classes in JavaFX arranged in a hierarchy starting with a top level class called, Node. There are a number of layout classes that all work differently, such as BorderPane, ScrollPane, HBox, VBox, GridPane and StackPane which can all be embedded inside of each other to create exactly the layout you want.\nThere are classes to display images, text or text and images together. ListView, TreeView and TableView allow collections of data to be displayed on screen (and edited) in highly customizable ways.\nFor user input, you can choose from TextField, ComboBox, ChoiceBox, Spinner, Checkbox, RadioButton, Buttons and ToggleButtons.\nAll of these Nodes have a large number of methods and properties for controlling their presentation and how they act. Learning how to use them and configure them can be a daunting task. Luckily, most of the tutorials on the web seem to focus on this aspect of JavaFX, so help is easy to find.\nThe Articles: # The following articles each deal with single Node type or a group of similar Nodes:\n","date":"10 June 2025","externalUrl":null,"permalink":"/javafx/elements/nodes/","section":"JavaFX","summary":"The building-blocks of a JavaFX GUI are the classes in the hierarchy that extends from Node. There’s a great variety of Node subclasses, each of which serves a different purpose. Some classes are designed to facilitate organizing the elements of your GUI on the screen, some are for user input, and others are for displaying information. Some others are designed to trigger actions in your application.","title":"The JavaFX Nodes","type":"javafx"},{"content":"","date":"15 December 2024","externalUrl":null,"permalink":"/javafx/elements/stylesheets/","section":"JavaFX","summary":"One of the things that makes JavaFX obviously different from Swing is that you can use Stylesheets in JavaFX.  However, this isn’t just a convenience.  It’s a critical element in creating layouts that behave dynamically and you really cannot write an effective JavaFX application without leveraging the power of Style Sheets.","title":"Stylesheets and PseudoClasses","type":"javafx"},{"content":" Kotlin The examples in this article are written in Kotlin. If you need some help understanding Kotlin, then refer to this article. Introduction # For me, this is the most exciting new feature added to JavaFX in version 23. Putting transitions into the style-sheet means it will be dead simple to have Nodes gently fade in and fade out, resize, or change colours without writing any code at all to control it.\nThe stated goal of this enhancement was to implement W3.org CSS Transitions in JavaFX. From what I can see, they have come pretty close to the specification listed there.\nWhy Use This? # From the JDK Issue for this feature:\nCSS Transitions is a universally supported web standard that allows developers to specify how a CSS property changes its value smoothly from one value to another.\nand a bit later:\nCSS Transitions makes it easy to define animated transitions and create rich and fluid user experiences. CSS Transitions also nicely complements control skins: while skins define the structure and semantics of a control, implicit transitions define its dynamic appearance.\nFirst off, since these are \u0026ldquo;transitions\u0026rdquo;, this is clearly aimed at elements of styling that change. Secondly, this feature only applies to styling that is defined in the style sheet. This means that styling that is applied programmatically will not transition unless you explicitly program the change inside some kind of Transition class.\nTo me, this means that this is a feature that works best with PseudoClasses. As a matter of fact, the example from the JDK Issue uses the hover PseudoClass:\n.button { -fx-opacity: 0.8; transition: -fx-opacity 1s; } .button:hover { -fx-opacity: 1.0; } The really, really nice thing about this is that you can define your PseudoClasses in your layout code, define conditions under which they\u0026rsquo;ll turn off and on, and then you can let the stylesheet take care of the work of making the PseudoClass styling changes transition smoothly from one state to another. That\u0026rsquo;s a big win because all of that transitioning code just clutters up your layout code - so good riddance to it!\nAn Example # Let\u0026rsquo;s explore this with a simple example. The base requirement is that we need a screen with an element that is styled with a PseudoClass and some way to turn the PseudoClass on and off:\nclass CssTransitionExample : Application() { private val activatedPC = PseudoClass.getPseudoClass(\u0026#34;activated\u0026#34;) val activated: BooleanProperty = SimpleBooleanProperty(false) override fun start(stage: Stage) { stage.scene = Scene(createContent(), 340.0, 200.0).apply { CssTransitionExample::class.java.getResource(\u0026#34;example.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = Label(\u0026#34;This is the Label\u0026#34;).apply { styleClass += \u0026#34;transition-label\u0026#34; activated.subscribe { newVal -\u0026gt; pseudoClassStateChanged(activatedPC, newVal) } } bottom = CheckBox(\u0026#34;Activate PseudoClass\u0026#34;).apply { selectedProperty().bindBidirectional(activated) } padding = Insets(40.0) } } fun main() = Application.launch(CssTransitionExample::class.java) That\u0026rsquo;s all we need. We\u0026rsquo;re using a Label as the styled Node with the PseudoClass, and a CheckBox to turn the PseudoClass on and off. It\u0026rsquo;s pretty simple.\nHere\u0026rsquo;s the really cool part. For the rest of this article, we aren\u0026rsquo;t going to touch this code at all\u0026hellip;\nWhich is point of this feature. The code just does the layout and the necessary mechanics of handling the PseudoClass. Once that\u0026rsquo;s set up, everything else is done via the stylesheet.\nUse a transition, don\u0026rsquo;t use a transition, style it this way or some other way\u0026hellip;none of that changes the layout code at all.\nLet\u0026rsquo;s look at the stylesheet without any transitions:\n.transition-label { -fx-font-size: 18px; -fx-font-weight: bold; -fx-text-fill: darkgreen; } .transition-label: activated { -fx-text-fill: red; } That\u0026rsquo;s our starting point. Big black letters, which change to red when activated. It looks like this:\nand:\nNow we want to turn the colour change into a transition. All we have to do, is to change the stylesheet like this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; transition: -fx-text-fill 5.0s; } .transition-label: activated { -fx-text-fill: red; } That\u0026rsquo;s it, we just added the line transition: -fx-text-fill 5.0s; and now the PseudoClass activation will trigger a transition that will take 5 seconds for it to change the text fill from darkgreen to red. Like this:\nI\u0026rsquo;ve changed the initial colour from black to darkgreen because it\u0026rsquo;s opposite red on the colour wheel, and makes the transition a little bit easier to see when it starts.\nSome Thoughs on Transitions # This is a technical arcticle, but\u0026hellip;\nIf you\u0026rsquo;re building applications to be used in a professional setting, as I did for decades, then there are some things you should keep in mind.\nTransitions and animations can make your application look slick and professional, but only if you employ them with restraint. Screen elements that suddenly jump from colour to colour, pop in and, and suddenly change there appearance often look clumsy, and you can improve this by using transitions. But your users should never really be aware that these things are happening.\nFrom my experience, users are consciously aware that they want functionality. More functionality. New functionality, Better functionality. If they think that you\u0026rsquo;re spending time doing \u0026ldquo;fancy\u0026rdquo; stuff, they get annoyed because the perception is that the time that it takes to develop new functionality is becoming longer because of this \u0026ldquo;fancy\u0026rdquo; fluff.\nOn the other hand, user confidence in your application is critical. So it does help if your application looks like it was written by programmers who know what they are doing and can craft a quality product. Transitions can help with that.\nThis new feature can go a long way towards that. How long does it take to add a single line to a stylesheet?\nThe 5 second transition in this example is ridiculous. It makes the transition starkly obvious and impossible to ignore. However, if we shorten the transition to 0.4 seconds, it changes everything:\nNow the transition makes the colour change look more professional. It\u0026rsquo;s just long enough that it isn\u0026rsquo;t a sharp jump from one colour to the other.\nDetails About CSS Transitions # That\u0026rsquo;s the basics, now let\u0026rsquo;s look at some of the details:\nWhat Can You Transition? # You can transition anything defined by a primitive type, and anything defined by a type that implements \u0026ldquo;Interpolatable\u0026rdquo;. Currently, the latter includes Color, Point2D and Point3D, but only Color is meaningfully used in stylesheets.\nI was curious about how Boolean would transition, since it\u0026rsquo;s a primitive type, but it doesn\u0026rsquo;t transition. I couldn\u0026rsquo;t find any meaningful uses of String in the CSS Reference Guide, either.\nUnfortunately, anything defined by \u0026lt;size\u0026gt; in the stylesheet isn\u0026rsquo;t able to transition, since it isn\u0026rsquo;t a primitive. So you cannot transition -fx-border-width, or -fx-border-insets or -fx-padding, or -fx-font-size, which is a shame. However, the issue notes indicate that at least some of this will be imlemented in a future enhancement.\nEssentially, you can transition anything in the stylesheet which is defined by \u0026lt;number\u0026gt; or \u0026lt;paint\u0026gt;.\nNotably, things that you can transition are -fx-opacity, -fx-rotate, -fx-scale-x, -fx-scale-y, -fx-translate-x, and -fx-translate-y. You can also transition anything defined by a single \u0026lt;paint\u0026gt;, which includes -fx-fill, -fx-stroke, and -fx-text-fill.\nNew Transitions in JFX 24 # Issue #83332895, included in JFX release 24 has support for the following Node Properties:\nInsets Background BackgroundFill BackgroundImage BackgroundPosition BackgroundSize Border BorderImage BorderStroke BorderWidths CornerRadii Stop Paint and all of its subclasses Margins (internal type) BorderImageSlices (internal type) At the time of writing this article, JFX 24 is only available in \u0026ldquo;early access\u0026rdquo;.\nTransitioning Multiple Attributes # What if you want to transition more than a single property? How do you do that?\nYou can only have a single transition definition per CSS selector, but you can specify more than one property for that transition.\nFor example, this won\u0026rsquo;t work:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; transition: -fx-text-fill 0.4s; transition: -fx-scale-x 2.5s; transition: -fx-scale-y 2.5s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } But this will:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: -fx-text-fill, -fx-scale-x, -fx-scale-y; transition-duration: 3.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } However, if you do want different times, you can do this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: -fx-text-fill, -fx-scale-x, -fx-scale-y; transition-duration: 3.0s, 10s, 10s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } Which will transition the -fx-text-fill property over 3s and the other two over 10s.\nThe transition-property attribute is taken as the master, and if you have extra values for the other attributes, it will ignore them. However, if the other lists are too short, it will repeat them. So this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: -fx-text-fill, -fx-scale-x, -fx-scale-y; transition-duration: 3.0s, 10s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } Will result in the -fx-text-fill property transitioning over 3s, the -fx-scale-x over 10s and then the -fx-scale-y over 3s as the list of durations is repeated to satisfy all of the properties specified.\nAlso, you cannot do this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition: -fx-text-fill, -fx-scale-x, -fx-scale-y 2.5s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } But you can specify\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition: -fx-text-fill 2.5s, -fx-scale-x 2.5s, -fx-scale-y 2.5s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } But you cannot do this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition: -fx-text-fill 2.5s, -fx-scale-x, -fx-scale-y; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } You must specify the duration for each property because the default is 0s.\nHowever, you can do this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition: all 5.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } Finally, because the default for transition-property is all, you can do this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition: 5.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } But you cannot do this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-duration: 5.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } While you can do this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: all; transition-duration: 5.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } Interpolators # Transitions do not have to move at a steady rate from beginning to end (which would be linear). The following types of interpolation:\nLinear Bezier Quadratic Easing Step SMIL 3.0 Easing The two \u0026ldquo;easing\u0026rdquo; types allow you to have the accelerate at the beggining and decelerate at the end, or both. With the bezier quadratic easing, you can vary the amount of acceleration at each end.\nThe \u0026ldquo;Step\u0026rdquo; function jumps through a number of values interpolated between the two end-point values. You can specify how many jumps you want to have.\nAll of these are well documented in the CSS Reference Guide. There are diagrams and examples.\nTo implement one of these, use the transition-timing-function tag in the stylesheet. Like this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: all; transition-timing-function: -fx-ease-both; transition-duration: 2.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } It\u0026rsquo;s extremely easy to change the interpolator, and you really have to try them out to understand how they differ.\nYou can also have a different interpolator for each property being transitioned:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: -fx-text-fill, -fx-scale-x, -fx-scale-y; transition-duration: 4s, 10s, 10s; transition-timing-function: -fx-ease-both, steps(12, jump-end), steps(12, jump-end); } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } Transition Delay # It is possible to delay the transition for any of the properties listed in transition-property. Like this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: -fx-text-fill, -fx-scale-x, -fx-scale-y; transition-duration: 0.4s, 10s, 10s; transition-delay: 5s, 0s, 0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } This will cause the -fx-text-fill property to transition in 0.4s after as 5s delay, while the scaling properties transition over a 10s period with no delay.\nSome Quirks # If you don\u0026rsquo;t include the starting values in the base styling, then the transition becomes a bit odd:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; transition: all 5.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } This transitions the scaling and the colour when you turn off the PseudoClass, but when turning on the PseudoClass the scaling jumps immediately, and then the colour transitions.\nYou can fix this by changing the stylesheet to this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition: all 5.0s; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } I suspect that this might be a bug. But it\u0026rsquo;s easy enough to work around.\nI tried to create multiple transitions for a Node by adding a second selector to the Label called growing-label and then modify the stylesheet to use it:\n.growing-label { -fx-scale-x: 1.0; -fx-scale-y: 1.0; transition-property: -fx-scale-x, -fx-scale-y; transition-duration: 26.0s; } .growing-label: activated { -fx-scale-x: 2.0; -fx-scale-y: 2.0; } .transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; transition-property: -fx-text-fill; transition-duration: 5.0s; } .transition-label: activated { -fx-text-fill: red; } But this didn\u0026rsquo;t work. In fact, it turned off the transition on the scaling and then did just the transition for the colour.\nWhat if you put the transition properties in the PseudoClass selector section? I tried this:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; } .transition-label: activated { -fx-text-fill: red; -fx-scale-x: 2.0; -fx-scale-y: 2.0; transition-property: all; transition-duration: 5.0s; } And the results were as I suspected. The Label transitioned to the 2x scaling and red, but snapped back to black and 1x scaling when the PseudoClass was turned off again:\nSomething That Won\u0026rsquo;t Transition: Defined Colours # A lot of CSS styling defined in Modena related to the standard PseudoClasses depends on changing the definitions of some pre-defined colours in the stylesheet. Changing the definition of -fx-color in the context of a PseudoClass can change the appearance of that Node without having to recreate a lot of complicated styling.\nSo it would be nice to able to transition a Node based on a change to a color definition. For instance, I hoped that the following would cause a transition to the background colour of the Label:\n.transition-label { -fx-font-size: 16px; -fx-font-weight: bold; -fx-text-fill: darkgreen; -fx-scale-x: 1.0; -fx-scale-y: 1.0; -fx-background-color: -fx-color; -fx-color: blue; transition-property: all; transition-duration: 2.0s; } .transition-label: activated { -fx-text-fill: red; -fx-color: green; -fx-scale-x: 2.0; -fx-scale-y: 2.0; } But it did not work.\nPersonally, I would really like to see the ability to transition named colours because that would open up a whole world of possibilities for animating standard Nodes with standard PseudoClasses. Take a look at these styles for PseudoClasses from Modena:\n.button:hover, .toggle-button:hover, .radio-button:hover \u0026gt; .radio, .check-box:hover \u0026gt; .box, .menu-button:hover, .split-menu-button \u0026gt; .label:hover, .split-menu-button \u0026gt; .arrow-button:hover, .slider .thumb:hover, .scroll-bar \u0026gt; .thumb:hover, .scroll-bar \u0026gt; .increment-button:hover, .scroll-bar \u0026gt; .decrement-button:hover, .choice-box:hover, .color-picker.split-button \u0026gt; .arrow-button:hover, .color-picker.split-button \u0026gt; .color-picker-label:hover, .combo-box-base:hover, .combo-box-base:editable \u0026gt; .arrow-button:hover, .spinner .increment-arrow-button:hover, .spinner .decrement-arrow-button:hover, .tab-pane \u0026gt; .tab-header-area \u0026gt; .control-buttons-tab \u0026gt; .container \u0026gt; .tab-down-button:hover { -fx-color: -fx-hover-base; } .button:armed, .toggle-button:armed, .radio-button:armed \u0026gt; .radio, .check-box:armed .box, .menu-button:armed, .split-menu-button:armed \u0026gt; .label, .split-menu-button \u0026gt; .arrow-button:pressed, .split-menu-button:showing \u0026gt; .arrow-button, .slider .thumb:pressed, .scroll-bar \u0026gt; .thumb:pressed, .scroll-bar \u0026gt; .increment-button:pressed, .scroll-bar \u0026gt; .decrement-button:pressed, .choice-box:showing, .combo-box-base:showing, .combo-box-base:editable:showing \u0026gt; .arrow-button, .spinner .increment-arrow-button:pressed, .spinner .decrement-arrow-button:pressed, .tab-pane \u0026gt; .tab-header-area \u0026gt; .control-buttons-tab \u0026gt; .container \u0026gt; .tab-down-button:pressed { -fx-color: -fx-pressed-base; } All of these elements that support hover and armed and pressed change their styling for those PseudoClasses by simply redefining -fx-color in those contexts. Imagine that if you wanted Button to transition on hover and armed, you could do this:\n.transition-button { transition-property -fx-color; transition-duration: 0.7s; } Then all you would have to do is:\nbutton.styleclass += \u0026#34;transition-button\u0026#34; And you\u0026rsquo;re done. But, unfortunately, we cannot do that.\nDealing With CSS Transitions Programmatically # There are circumstances in which you might want to cope with a CSS transition inside your layout code. To enable this, a new Event type has been added, TransitionEvent, which is fired when a CSS transition starts and ends. This means that you can detect a CSS transition and have your layout respond to it.\nWe\u0026rsquo;ll modify our layout such that the CheckBox is disabled while the CSS transition is running:\nclass CssTransitionExample : Application() { private val activatedPC = PseudoClass.getPseudoClass(\u0026#34;activated\u0026#34;) private val activated: BooleanProperty = SimpleBooleanProperty(false) private val transitionCounter: IntegerProperty = SimpleIntegerProperty(0) override fun start(stage: Stage) { stage.scene = Scene(createContent(), 340.0, 200.0).apply { CssTransitionExample::class.java.getResource(\u0026#34;example.css\u0026#34;)?.toString()?.let { stylesheets += it } } stage.show() } private fun createContent(): Region = BorderPane().apply { center = Label(\u0026#34;This is the Label\u0026#34;).apply { styleClass += \u0026#34;transition-label\u0026#34; activated.subscribe { newVal -\u0026gt; pseudoClassStateChanged(activatedPC, newVal) } this.addEventHandler(TransitionEvent.START) { evt -\u0026gt; transitionCounter.value += 1 println(\u0026#34;Started: ${evt.property}\u0026#34;) } this.addEventHandler(TransitionEvent.END) { transitionCounter.value -= 1 } } bottom = CheckBox(\u0026#34;Activate PseudoClass\u0026#34;).apply { selectedProperty().bindBidirectional(activated) disableProperty().bind(transitionCounter.greaterThan(0)) } padding = Insets(40.0) val fred = PropertyValueFactory\u0026lt;String, String\u0026gt;(\u0026#34;Abc\u0026#34;) } } fun main() = Application.launch(CssTransitionExample::class.java) I\u0026rsquo;ve also added some code to print some information about the Event to the console. This is interesting, because it shows 3 transitions running:\nStarted: ObjectProperty [bean: Label@44abad90[styleClass=label transition-label]\u0026#39;This is the Label\u0026#39;, name: textFill, value: 0x000000ff] Started: DoubleProperty [bean: Label@44abad90[styleClass=label transition-label]\u0026#39;This is the Label\u0026#39;, name: scaleX, value: 1.0] Started: DoubleProperty [bean: Label@44abad90[styleClass=label transition-label]\u0026#39;This is the Label\u0026#39;, name: scaleY, value: 1.0] Started: ObjectProperty [bean: Label@44abad90[styleClass=label transition-label]\u0026#39;This is the Label\u0026#39;, name: textFill, value: 0xff0000ff] Started: DoubleProperty [bean: Label@44abad90[styleClass=label transition-label]\u0026#39;This is the Label\u0026#39;, name: scaleX, value: 2.0] Started: DoubleProperty [bean: Label@44abad90[styleClass=label transition-label]\u0026#39;This is the Label\u0026#39;, name: scaleY, value: 2.0] The first three lines are when the PseudoClass is applied, and the last three are for when it is removed.\nThis is why you need to use the counter approach, instead of just enabling and disabling the Button directly in the EventHandlers. The Button is disabled the moment that the first transition starts, and sets the counter to \u0026ldquo;1\u0026rdquo;, then is enabled when the final transition completes and sets the counter back to \u0026ldquo;0\u0026rdquo;.\nThis kind of monitoring is less useful when your transitions are strictly stylistic and fast, but if you\u0026rsquo;re doing something that is more functional it could come in handy.\nConclusion # This new feature makes it dead easy to add transitions to a useful set of styling properties that you might want to modify when applying PseudoClasses. And it does it without requiring any changes to your layout code.\nAll of the examples in this article except for the last one that shows how to integrate CSS Transitions with code differ only in the stylesheets. All of the different techniques were demonstrated without changing anything in the layout code. How cool is that?\nThe issue notes clearly indicate that this new feature is still in its early stages and that we can expect enhancements in the future. Hopefully, this will include the ability to perform transitions on named colours.\n","date":"8 October 2024","externalUrl":null,"permalink":"/javafx/elements/css-transitions/","section":"JavaFX","summary":"JavaFX 23 delivers a new feature - CSS Transitions!  Let’s take a look at how this works and what you can do with it.","title":"CSS Transitions","type":"javafx"},{"content":"","date":"28 August 2024","externalUrl":null,"permalink":"/tags/javafx/","section":"Tags","summary":"","title":"Javafx","type":"tags"},{"content":"This series explains the JavaFX Observable framework, starting with the core interfaces and moving through properties, bindings, and practical usage.\nArticles # This series contains:\nIntroduction to the Observable classes Properties and Writable values Bindings and reactive expressions ","date":"28 August 2024","externalUrl":null,"permalink":"/javafx/elements/observable-classes/","section":"Series","summary":"This series explains the JavaFX Observable framework, starting with the core interfaces and moving through properties, bindings, and practical usage.\nArticles # This series contains:\n","title":"JavaFX Observable Classes","type":"series"},{"content":"","date":"28 August 2024","externalUrl":null,"permalink":"/tags/observable/","section":"Tags","summary":"","title":"Observable","type":"tags"},{"content":"","date":"28 August 2024","externalUrl":null,"permalink":"/tags/properties/","section":"Tags","summary":"","title":"Properties","type":"tags"},{"content":"","date":"28 August 2024","externalUrl":null,"permalink":"/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":" Introduction - Why Use a Framework? # Frameworks are all about organizing your application so that it will be easy to maintain, enhance and expand over time.\nIf your application is going to actually \u0026ldquo;do something\u0026rdquo; it will probably need to interact with the world outside your application. This might just be the local file system, but it might be a local database, or a remote database, another local application or REST API for some service anywhere on the Web. In any event, you need to organize your application so that you don\u0026rsquo;t have your screen layout muddled up with business logic and calls to external databases and services.\nThere are a number of frameworks to choose from, but they are all designed to keep your business logic out of your layouts, and your layouts out of your business logic. If they are done right, then your layouts and your business logic can connect without either one knowing anything at all about the other.\nThis article is about my own framework, \u0026ldquo;Model-View-Controller-Interactor\u0026rdquo;, which works in a manner very similar to the other framemworks that you\u0026rsquo;ll hear about, but is fine tuned to work with JavaFX when it\u0026rsquo;s used to create \u0026ldquo;Reactive\u0026rdquo; GUI applications.\nWhat\u0026rsquo;s Wrong With the Others? # This is a quick guide, so I\u0026rsquo;m not going to go into a deep description of the other frameworks or how they work. But I will explain why I feel they fall short when building Reactive applications with JavaFX:\nModel-View-Presenter (MVP)\nThe problem with MVP is that the View is stripped down to nothing more than layout and styling, leaving all other aspects of the GUI to be handled by the Presenter. This requires a level of coupling between the View and the Presenter that is so tight that they might as well be considered to be a single component. The Presenter then becomes a \u0026ldquo;god class\u0026rdquo;. I\u0026rsquo;m not sure that anybody uses MVP any more.\nModel-View-ViewModel (MVVM)\nMVVM looks like a much better fit for Reactive JavaFX because it specifically allows for binding between the Presentation Model and the View. However, the ViewModel isn\u0026rsquo;t allowed to see the Domain Objects and the Model isn\u0026rsquo;t allowed to see Presentation Model and the result is complications passing data between the ViewModel and the Model. The coupling is loose, but it becomes a problem itself because it can be very complicated to work around.\nModel-View-Controller (MVC)\nMVC solves many of the issues with MVVM because the Presentation Model is part of the Model. However, MVC specifically exposes the Model (and, therefore, the Presentation Model) to the View in a \u0026ldquo;read-only\u0026rdquo; mode. This means that Properties in the Model cannot be bound bidirectionally to the Properties of the Nodes in the View and updates must be passed through the Controller to the Model.\nWhat is Model-View-Controller-Interactor? # MVCI solves the problems of these other frameworks by taking MVC and splitting the Model into two parts: the Presentation Model (just called the \u0026ldquo;Model\u0026rdquo; in this framework) and the \u0026ldquo;Interactor\u0026rdquo;. This new Model is simple a POJO composed of JavaFX Observable objects and lists, and the other three components all have read and write access to it.\nThe result is something that is instantly simple and easy to integrate with Reactive JavaFX. Properties of the Nodes in the View can be bound in either direction to the elements of the Model. This means that the Model is truly a data representation of the \u0026ldquo;state\u0026rdquo; of the GUI at all times. At the other end of the framework, all of the business logic is contained and isolated within the Interactor, which also is the only component that can access external services, applications and databases.\nThis fulfils the purpose of the framework. Business logic, application connective tissue and GUI design are all isolated from each other and very loosely coupled. You\u0026rsquo;ll find that this structure is a natural fit for Reactive JavaFX and takes very little effort to implement.\nThe Components of MVCI # This diagram shows the components of MVCI in relation to each other and external elements:\nLet\u0026rsquo;s take a look at the four components of MVCI and see what they are how they work together.\nThe Model # The Model is just a bunch of data. But it\u0026rsquo;s data stored in JavaFX Observable objects (typically Properties), and ObservableLists. The intention is that the Interactor will treat the Observables simply as wrappers around data, ignoring their Observable nature (for the most part). At the other end, the View treats the Model as a collection of ObservableVales that it will treat only as Observable. This means that the View primarily interacts with the Model through Bindings, and perhaps Subscriptions or Listeners.\nThe Controller # The Controller is the connective tissue of the framework. It instantiates all the other components, and it handles anything involving threading (which becomes important when you need to deal with long or blocking processes off the FXAT). It also contains any links back to other MVCI frameworks in your application, usually through shared Model elements. Typically, you will initiate an MVCI framework by instantiating the Controller, and it will bootstrap itself through it\u0026rsquo;s constructor code.\nThe Interactor # Here\u0026rsquo;s where the business logic resides, and the calls to your external services, databases and applications. It\u0026rsquo;s also the only place that you\u0026rsquo;ll find \u0026ldquo;Domain Objects\u0026rdquo;. Domain Objects are the classes that hold the data that are returned from (and are sent to) your \u0026ldquo;Services\u0026rdquo; and they are completely foreign to your View and your Controller.\nThe View and the ViewBuilder # The View is a fully functioning JavaFX Node subclass that can be added to the SceneGraph. It handles all of the display, and any direct interaction with the user.\nThe preferred way to create a View is the \u0026ldquo;configuration\u0026rdquo; of an existing Node sub-class via a Builder. Happily, JavaFX has an interface called Builder\u0026lt;T\u0026gt; with a single method called build(). If you follow this approach, and it\u0026rsquo;s usually the correct one, then you won\u0026rsquo;t have a View class in your framework, but you\u0026rsquo;ll have a ViewBuilder which implements Builder\u0026lt;Region\u0026gt;.\nWhat Goes Where? # One of the nice things about MVCI is that all of the decisions about where to put your code are straight-forward and easy to resolve. Let\u0026rsquo;s look at some of the most important items:\nBusiness Logic # This is the question to ask first: \u0026ldquo;Is this business logic?\u0026rdquo; If your application isn\u0026rsquo;t a business application, then substitute some other term like, \u0026ldquo;game logic\u0026rdquo;. You get the idea.\nIf the answer is \u0026ldquo;YES\u0026rdquo;, then it goes in the Interactor. No exceptions.\nPresentation # Another question to ask is whether the code deals solely with how the data is presented to the user. That \u0026ldquo;solely\u0026rdquo; is interpreted very narrowly and if the answer is \u0026ldquo;YES\u0026rdquo;, then it goes in the View. Is something going to be displayed in a particular colour? That colour choice is probably solely presentation and belongs in the View. However, if that colour choice is based on something that smells like business logic, then it\u0026rsquo;s not solely presentation. You\u0026rsquo;ll need to split out the business logic, put that part in the Interactor and use the results of that to determine the actual colour to be used in the View.\nFor example, imagine you have a list of grocery items and you want veggies in one colour, and fruit in another, and dairy in another colour. If your list is just; bananas, carrots, apples, tomatoes and milk, then you\u0026rsquo;ll have a problem determining the colours in the View. What\u0026rsquo;s a \u0026ldquo;tomato\u0026rdquo;? Is it a fruit, a veggie or dairy? That\u0026rsquo;s a business logic decision, and cannot be made in the View. Update your Model such that the list also contains the type, and then populate it accordingly from the Interactor. Now you\u0026rsquo;ll get; bananas(fruit), carrots(veggie), apples(fruit), tomatoes(technically fruit, but used like a veggie), and milk(dairy). In your View, you look at the types and determine the colour based on that, because now that is solely a presentation decision.\nDomain Logic # It\u0026rsquo;s important to understand that \u0026ldquo;Business Logic\u0026rdquo;, as it is used above actually means, \u0026ldquo;business logic specific to this MVCI construct\u0026rdquo;. This does not include calling external API\u0026rsquo;s or parsing JSON data. Things like that belong in a layer below your Interactor that holds the \u0026ldquo;Domain\u0026rdquo;.\nLet\u0026rsquo;s take a look at the fruit and veggie example above. If your list of items is coming from a database somewhere, you are NOT going to put SQL statements or any equivalent in your Interactor. Create a GroceryListService that does the database access, then parses the resultant JSON data and creates a list of GroceryItem which probably has at least name and quantity. GroceryItem is your domain object, and that list is passed back to your Interactor.\nBut what about the type?\nLet\u0026rsquo;s say that you\u0026rsquo;ve found some web service like Chomp that can give you information about grocery items, (full disclosure, Chomp doesn\u0026rsquo;t seem to tell you what department stuff is in) and you are going to use it to determine fruit, veggie or dairy. The code that contains the API calls and the JSON parsing for that function also goes into a service. However, the code that loops through your list of grocery items, calls the service to find out what type of item it is, and then integrates the two together into the Model is business logic for this MVCI construct and goes in the Interactor.\nPresentation Data # The Model contains just \u0026ldquo;Presentation Data\u0026rdquo;. In other words, every element is something that is potentially used directly by both the View and the Interactor. Another way that you can think about it is that the \u0026ldquo;Presentation Data\u0026rdquo; represents the \u0026ldquo;State\u0026rdquo; of your entire MVCI construct. Because the View and the Model are connected to each other through Bindings, any relevant changes to your View are going to be mirrored in your Model and they are going to be immediately visible and available to the Interactor. At the same time, any changes to the Model made by the business logic in the Interactor are going to be instantly reflected in the View.\nAn Example # The best way to understand this is to see it in code. So we\u0026rsquo;ll build that grocery list application to see how it works\u0026hellip;\nThe Application # class MvciApplication : Application() { override fun start(stage: Stage) { stage.scene = Scene(Controller().getView(), 600.0, 400.0) stage.show() } } fun main() = Application.launch(MvciApplication::class.java) The most important take-away here is the way that the Controller is instantiated and then simply used to retrieve the View so that it can be placed in the Scene.\nThe perspective that you need to have is that the View, which is just some sort of Node subclass, is the important product of the Controller from a viewpoint outside the MVCI framework. This is the thing that you are going to stick into your GUI. In this case we\u0026rsquo;re putting in as the root element of a Scene, but it could become the child of some other layout class. It will still work.\nThe Controller # The rough skeleton for a Controller is always something like this:\nclass Controller { private val model= Model() private val interactor = Interactor(model) private val viewBuilder:Builder\u0026lt;Region\u0026gt; = ViewBuilder(model) fun getView() : Region = viewBuilder.build() } But we are also going to be doing some database lookups in our application, so we\u0026rsquo;ll need to use Task to handle the threading and call the Interactor methods to do the work:\nclass Controller { private val model = Model() private val interactor = Interactor(model) private val viewBuilder: Builder\u0026lt;Region\u0026gt; = ViewBuilder(model) { fetchList() } private fun fetchList() { Thread(object : Task\u0026lt;List\u0026lt;GroceryItem\u0026gt;\u0026gt;() { override fun call() = interactor.getList() }.apply { setOnSucceeded { _ -\u0026gt; interactor.updateModelAfterFetch(get()) } }).apply { isDaemon = true start() } } fun getView(): Region = viewBuilder.build() } The Model # Our Model is going to have three classes, one to hold the actual grocery items and another to hold (at least) a list of those items. The final class is an Enum to hold the departments:\nclass Model { val listName: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) val groceryList: ObservableList\u0026lt;GroceryModel\u0026gt; = FXCollections.observableArrayList() } class GroceryItem { val name :StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) val quantity: ObjectProperty\u0026lt;Int\u0026gt; = SimpleObjectProperty(0) val department: ObjectProperty\u0026lt;Department\u0026gt; = SimpleObjectProperty() } enum class Department { DAIRY, FRUITS, VEGGIES, MEATS, BAKERY; } The Enum class Department is more of a utility class than part of the Presentation Model, as it\u0026rsquo;s used in the Domain Objects as well.\nThe ViewBuilder # Our ViewBuilder is just a class that implemnents Builder\u0026lt;Region\u0026gt;. As is often the case with \u0026ldquo;top-level\u0026rdquo; Views, it\u0026rsquo;s best to return a BorderPane:\nclass ViewBuilder(private val model: Model, private val listFetcher: () -\u0026gt; Unit) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region = BorderPane().apply { center = createTable() bottom = createLookup() padding = Insets(15.0) } private fun createLookup(): Region = HBox(10.0).apply { children += Label(\u0026#34;List Name:\u0026#34;) children += TextField().apply { textProperty().bindBidirectional(model.listName) } children += Button(\u0026#34;Fetch\u0026#34;).apply { setOnAction { listFetcher.invoke() } } padding = Insets(5.0) } private fun createTable(): Node = TableView\u0026lt;GroceryModel\u0026gt;().apply { columns += TableColumn\u0026lt;GroceryModel, String\u0026gt;(\u0026#34;Item Name\u0026#34;).apply { setCellValueFactory { p -\u0026gt; p.value.name } } columns += TableColumn\u0026lt;GroceryModel, Department\u0026gt;(\u0026#34;Department\u0026#34;).apply { setCellValueFactory { p -\u0026gt; p.value.department } } columns += TableColumn\u0026lt;GroceryModel, Int\u0026gt;(\u0026#34;Quantity\u0026#34;).apply { setCellValueFactory { p -\u0026gt; p.value.quantity } } items = model.groceryList columnResizePolicy = TableView.CONSTRAINED_RESIZE_POLICY_LAST_COLUMN } } The View is just a BorderPane with a TableView in the centre and an HBox holding the \u0026ldquo;Lookup\u0026rdquo; controls in the bottom. You can see how the input TextField is bound to Model.listName bidirectionally, and the ObservableList held in Model.groceryList becomes the items in the TableView.\nWe\u0026rsquo;ll talk about this some more later, but note that the EventHandler on the Button invokes the Runnable passed from the Controller as listFetcher.\nThe Interactor # class Interactor(private val model: Model) { private val listService = GroceryListService() fun getList(): List\u0026lt;GroceryItem\u0026gt; { return if (model.listName.value.isEmpty()) { listService.fetchList(\u0026#34;DEFAULT\u0026#34;) } else { listService.fetchList(model.listName.value) } } fun updateModelAfterFetch(groceries: List\u0026lt;GroceryItem\u0026gt;) { model.groceryList.setAll(groceries.map { createGroceryModel(it) }) } private fun createGroceryModel(groceryItem: GroceryItem) = GroceryModel().apply { name.value = groceryItem.itemName department.value = groceryItem.theDepartment quantity.value = groceryItem.howMany } } Things to note about the Interactor:\nThe function getList() contains code to handle a blank list name.\nThis is because the manner that an empty list name is dealt with is something that is very specific to this application. It\u0026rsquo;s not defined at the service level. Some other application might want to return an empty list, or maybe generate an error warning.\nThe Interactor knows about GroceryItem, which is a \u0026ldquo;Domain Object\u0026rdquo;. There are no usages of GroceryItem in the Controller, Model or View.\nThe Interactor does not compose REST calls, or SQL statements. It simply asks the Service to get the information, without any knowledge of how it will do it.\nThe Interactor takes the Domain Objects from the Service, and converts them into Presentation Model objects, which are then put into the Model.\nThe Service Layer (Domain) Elements # We are just going to simulate the operation of a Service here, since we\u0026rsquo;re not going to create a database or access any remote web API\u0026rsquo;s to retrieve data:\nclass GroceryListService { fun fetchList(listName: String): List\u0026lt;GroceryItem\u0026gt; { Thread.sleep(2000) return listOf( GroceryItem(\u0026#34;Celery\u0026#34;, Department.VEGGIES, 10), GroceryItem(\u0026#34;Milk\u0026#34;, Department.DAIRY, 4), GroceryItem(\u0026#34;Grapes\u0026#34;, Department.FRUITS, 230) ) } } data class GroceryItem(val itemName: String, val theDepartment: Department, val howMany: Int) We\u0026rsquo;ve also defined our Domain Object, GroceryItem here. The most important point is that GroceryItem is NOT just a POJO representation of JSON data from some external source. It might actually be created that way, this definition of GroceryItem is related to whatever layour of our Service interacts with its client applications. The Service might be accessed by dozen of different screens in our application, and they may or may not expect to send or receive GroceryItem to or from the Service. But they all know how to deal with GroceryItem if they get it.\nSome More About this Example # This application will run. It looks like this:\nAll told, there\u0026rsquo;s about 100 lines of code in this example (I counted), and this is way more complete than any example you\u0026rsquo;ll find for MVC or MVVM out there on the web. Let\u0026rsquo;s look at what this does:\nIt actually runs. It handles background threading via Task It has an actual Service layer. The Service layer simulates external services completely hidden from the MVCI framework. It has some business logic. The MVCI framework part of this example is 100% operational.\nThe only simulated part is the Service layer which is external to the framework. This means that you can absolutely test this framework without connecting to an actual external database or REST API. Coupling and Dependencies in MVCI # This is quick guide, so we won\u0026rsquo;t have a lot of theory. However, the main point of any framework is to limit and control the coupling in your application. So it\u0026rsquo;s important to understand how MVCI does this, and where the depencies exist.\nMVCI is Almost a Closed System # The first thing that you need to understand is that the primary method of all coupling is via public methods (or public fields). Every time that you introduce a public method, you\u0026rsquo;ve tied yourself down just a little bit more. A class with no public methods cannot be coupled to (or depended on).\nIn MVCI the Model, the Interactor and the ViewBuilder are all private to the framework. There\u0026rsquo;s no way for any outside class to get a reference to any of these three classes. That means that there\u0026rsquo;s no way that any outside class can call any public methods in these three classes, making them effectively private to the framework.\nThere are exactly two truly public methods in MVCI, and they are both in the Controller. The first is the constructor for the Controller, which has to be public in order to create the framework in an application. It is possible to create dependencies here by specifying constructor parameters.\nThe second externally visible method is the Controller.getView() method, which returns the View, which is always just a vanilla JavaFX Region or Region subclass.\nThis is literally the bare minimum coupling that you can have and still have a functional framework.\nCoupling Through the Model # This is the key element of coupling in MVCI. Every other component can see the Model and depends on its visible structure.\nClearly, therefore, any visible changes to the Model have the potential to break some other component of the framework. At the same time, changes can often be made to the other components without affecting any other part as long as they continue to work with the Model without changing its structure.\nIn practice, this gives a fair degree of latitude. Internal changes can be made to the Model, as long as the public methods remain unchanged.\nCoupling Between the Controller and the ViewBuilder # The View with JavaFX is almost always a Region or a standard subclass of Region, and will only have the public methods of Region The result is that there\u0026rsquo;s no coupling introduced via the View itself.\nHowever, the ViewBuilder is custom but you can see that it is instantiated as Builder\u0026lt;Region\u0026gt;, which has only one public method, Builder.build().\nAt first glance, it would seem that the View needs to have dependencies on the Controller in order to invoke activity in the Interactor. To avoid this two techniques are used; \u0026ldquo;Dependency Inversion\u0026rdquo; (or \u0026ldquo;Inversion of Control\u0026rdquo;, IoC), and \u0026ldquo;Dependecy Injection\u0026rdquo;.\nThis is done via the constructor of the ViewBuilder. In the example it\u0026rsquo;s: ViewBuilder(private val model: Model, private val listFetcher: () -\u0026gt; Unit). It\u0026rsquo;s the second parameter that does the work, listFetcher : () -\u0026gt; Unit, which is the Kotlin equivalent of a Runnable, but we could have specified a Consumer or a BiConsumer here.\nThe important thing is that listFetcher is a general purpose functional interface that \u0026ldquo;inverts\u0026rdquo; the dependency. Now, instead of the ViewBuilder needing a reference to the Controller in order to call one of its public methods, the Controller needs to know what functional element it needs to pass to the ViewBuider.\nEssentially the ViewBuilder, by having this constructor parameter, is saying, \u0026ldquo;Anyone can instantiate me, but you need to give me this functional element in order to do so\u0026rdquo;. That functional element has to implement the specified interface, but there\u0026rsquo;s no knowledged passed on to the ViewBuilder about what it does or how it does it.\nAnd yes, the Controller has to supply something that responds meaningfully to the context in the View is going to invoke it. But, at the end of it all, the View/ViewBuilder knows zero about the Controller, and the Controller only knows that the ViewBuilder is a Builder\u0026lt;Region\u0026gt; and requires a particular functional element as a constructor parameter.\nCoupling Between the Controller and the Interactor # This is the biggest coupling after the Model itself.\nIn order to work, the Interactor needs to expose a number of public methods that can be called from the Controller. In the example we have Interactor.getList() and Interactor.updateModelAfterFetch().\nYou should note, however, that all of the coupling inside MVCI is outwards from the Controller. The Controller knows what parameters to pass to the ViewBuilder, and it knows which methods to call in the Interactor. Since the Controller instantiates the other elements, it also already has to know their types.\nOn the other side, neither the ViewBuilder nor the Interactor have any knowledge at all about even the existence of the Controller or each other.\nWhen you are building your own applications, you want to keep things this way. Try to stick to these \u0026ldquo;rules\u0026rdquo;:\nDon\u0026rsquo;t add public methods to the Controller, as they will become visible outside the framework and become points for coupling. Don\u0026rsquo;t pass references to the Controller to the ViewBuilder or the Interactor. Don\u0026rsquo;t pass references to each other between the Interactor and the ViewBuilder. Coupling Between the Interactor and the Services # The last piece of coupling is between the Interactor and the Services it will use. Once again, this coupling is unavoidable if you want you application to actually \u0026ldquo;do\u0026rdquo; something. But you can keep it strictly one-way. That is to say, the Interactor will have dependencies on its Services, but the Services won\u0026rsquo;t have any dependencies on the Interactor.\nThere are two main points of coupling:\nThe \u0026ldquo;Domain Objects\u0026rdquo;\nClearly the structure of the Domain Objects is a major dependency in this part of the application. However, it\u0026rsquo;s also the main way of controlling the coupling, as it isolates the Interactor from changes in the external services. Even if a database structure is radically altered, it remains the Services\u0026rsquo; responsibility to continue to return valid Domain Objects in the same structure as always.\nThe Services\u0026rsquo; APIs\nThe public methods of the Service are the other coupling at this level. There\u0026rsquo;s nothing that says that a Service has to be designed for use by multiple MVCI frameworks. You can have a Service specifically tailored to work with a specific framework, but you still want to isolate the inner workings of the Service from the MVCI framework.\nConclusion # I cannot stress how ridiculously simple it is to implement MVCI. Take these 15 lines of code:\nclass SkeletonModel {} class SkeletonInteractor(private var model: SkeletonModel){} class SkeletonViewBuilder(private var model: SkeletonModel): Builder\u0026lt;Region\u0026gt; { override fun build(): Region { TODO(\u0026#34;Not yet implemented\u0026#34;) } } class SkeletonController() { private val model = SkeletonModel() private val interactor = SkeletonInteractor(model) private val viewBuilder:Builder\u0026lt;Region\u0026gt; = SkeletonViewBuilder(model) fun getView() = viewBuilder.build() } This took me 3 minutes and 12 seconds to type in from scratch (yes, I timed it). If you put it together with this:\nclass SkeletonApp : Application() { override fun start(stage: Stage) { stage.scene = Scene(SkeletonController().getView()) stage.show() } } fun main() = Application.launch(SkeletonApp::class.java) It will run. Okay, it will crash on the TODO in SkeletonViewBuilder.build(), but it will compile and run.\nThis is how I start every screen that I build. From here I usually start building the layout. As I add components to the layout, then I add the data elements to the Model as I go. In this way I have a working layout at all times. Sometimes I\u0026rsquo;ll just hard code starting values in the constructors of the fields in the Model, sometimes I\u0026rsquo;ll initialize them in the init{} block in the Interactor.\nAt the beginning, I\u0026rsquo;m interested in the Interactor as a tool to test my GUI, so the methods I create there are designed to simulate business logic that generates data sets that challenge my layout. Later, I can address actual business logic without worrying too much about how it impacts the GUI.\nIn this way, MVCI doesn\u0026rsquo;t just organize my projects, it actually becomes a tool that accelerates and simplifies development.\nAll this stuff is just second nature to me now, and even if I\u0026rsquo;m doing something trivial I\u0026rsquo;ll start with those same 15 lines of code. It\u0026rsquo;s just that useful.\nHopefully, you\u0026rsquo;ll find it useful too.\n","date":"1 August 2024","externalUrl":null,"permalink":"/javafx/elements/mvci-quick/","section":"JavaFX","summary":"Looking for a framework that works with JavaFX as a Reactive platform?  Here’s how to get started with Model-View-Controller-Interactor (MVCI) without a lot of theory.","title":"JavaFX: Quick Guide to MVCI","type":"javafx"},{"content":" Why MVCI??? # So, yeah, why another framework?\nAll of the frameworks that you are familiar with, like MVC, MVP and MVVM are actually quite old. I\u0026rsquo;m not going to say that they predate Reactive programming, because I have feeling that this too is older than you might think. But they certainly do predate JavaFX, and the way that JavaFX implements Reactive programming.\nFirst of all, forget about Model-View-Presenter (MVP). It\u0026rsquo;s completely irrelevant to JavaFX unless you are attempting to use FXML in an extremely restrictive way.\nNext, Model-View-Controller (MVC) has a paradigm where the View is allowed to see changes in the Presentation Data (which is part of the Model), but all changes to that data from the View must go through the Controller. This is a big problem for Reactive JavaFX because it means that you cannot bidirectionally bind your Presentation Data to the screen Nodes since this would bypass the Controller for updates from the View.\nYou can just ignore this restriction if you want - there are no MVC police to arrest you, after all. But this is a pretty central concept to MVC, and if you just toss it away then you\u0026rsquo;re not really doing MVC any more.\nFinally, Model-View-ViewModel (MVVM). On the surface, this seems to be a better fit for Reactive JavaFX. After all, it is specifically designed to allow binding between the Presentation Data and the View. However, just like MVP, it tries to reduce the View to a simple layout, absorbing all of the View logic into the ViewModel. At the same time, it has a problem at the other end. The Model isn\u0026rsquo;t allowed to know about the implementation of the Presentation Data, and the ViewModel isn\u0026rsquo;t allowed to know about the implementation of the Domain Data. This means that you need an extensive interface between the ViewModel and the Model to transfer data between the two.\nNobody Really Understands MVC, MVP and MVVM # My experience is that very few people actually understand how to use them properly. And if they did, they probably wouldn\u0026rsquo;t want to use them.\nIn my opinion, the biggest issue is with them \u0026ldquo;Model\u0026rdquo;. It\u0026rsquo;s simply counter-intuitive.\nVirtually everyone - and this includes almost every on-line tutorial I\u0026rsquo;ve ever seen - treats \u0026ldquo;Model\u0026rdquo; as virtually synonymous with \u0026ldquo;Presentation Data\u0026rdquo;. Now, in MVP and MVC, the Model includes presentation data, but in every framework the Model encompasses \u0026ldquo;Domain Knowledge\u0026rdquo;.\nWhat is \u0026ldquo;Domain Knowledge\u0026rdquo;?\nDomain Knowledge is the context for your application, the stuff related to what it does. If this is an sales application, then it contains the information about customers, inventory, invoices and the databases that store that information. It contains knowledge about API calls, and contains shared logic that is used across different screens in your application. If this is a game, then it contains the game logic, access to database that contain saved data and API calls to remote systems if they are needed.\nNow, if you interpret \u0026ldquo;Model\u0026rdquo; as \u0026ldquo;Presentation Data\u0026rdquo;, where do you put the database and API calls? It ends up going in the Controller, or the ViewModel.\nI\u0026rsquo;ve seen this time and time again in online tutorials. Most often, the \u0026ldquo;Domain\u0026rdquo; part of the Model is just left out. The examples are too simple to have any \u0026ldquo;Domain\u0026rdquo;, so the Model just contains Presentation Data and the domain can be ignored.\nModel-View-Controller-Interactor # Enter MVCI, the framework that evolved as I struggled over years to build real systems while following MVC (or what I thought was MVC). I was left to figure the hard parts myself, and I fixed issues with my approach on at a time.\nThe result is a framework that meshes extremely well with JavaFX. It makes sense, and it leverages JavaFX\u0026rsquo;s Observables and Bindings in a way that feels natural:\nThe View is a complete GUI, not just a layout. The Interactor handles all of the Domain Knowledge, and can access the Presentation data. The Controller co-ordinates all of the actions and threading, and provides the link to the rest of your application\u0026rsquo;s GUI. The Model is strictly the Presentation Data, implemented as JavaFX Observables. I\u0026rsquo;ve used MVCI for years now, and I have yet to encounter an application that did not become significantly simpler and easier to maintain (or to write in the first place) by using MVCI.\n","date":"1 August 2024","externalUrl":null,"permalink":"/javafx/techniques/mvci/","section":"JavaFX","summary":"Model-View-Controller-Interactor (MVCI) is a framework I designed specifically to work with Reactive JavaFX.  It’s very similiar to MVC and MVVM, but has a few key differences that make it way better for JavaFX.  This section has articles that explain why MVC, MVVM and MVP fail, why MVCI is better, and how to start using MVCI to create clear and simple applications that are easy to understand and maintain.","title":"Model-View-Controller-Interactor","type":"javafx"},{"content":" Introduction # When my team and I began Java programming back in 2011, we went through the same experience that I\u0026rsquo;ve gone through with just about every new technology that I\u0026rsquo;ve learned on the job over my career. That is to say, we went from \u0026ldquo;Hello World\u0026rdquo;, to trying to write the most complicated stuff you could think of in just a few weeks.\nIn this case, while the team was composed of programmers who were experts in the technologies that we had been using to date, we had no Java experience and were attempting to extend a multi-user enterprise legacy application and deliver new functionality to our users as soon as possible. Back then, we were using Swing and learning how to deal with brand new (to us) Java concepts at the same time that we were trying to understand how to design GUI screens and integrate it with a complex legacy system.\nThis meant, of course, that our early work hellaciously bad. Really, really bad. We were fumbling around trying to learn how to work with a single-threaded GUI, while trying to figure out how to connect to our legacy database and handle performance issues related to that. We had heard a little bit about MVP, and we tried to implement its ideas without really understanding why we should even be doing that.\nBut we learned, and we got better.\nWhat If the Legacy System Goes Away? # While there was lots, and lots, and lots of stuff we did wrong, we did get one thing right. We decided right up front to confront the question of, \u0026ldquo;What happens if the legacy system goes away?\u0026rdquo;. Really, the question was about how our custom work could continue to function if the technology underlying the legacy system was to be replaced.\nWe dealt with this by thinking of our applications in slices. At one end was the user, and the code that interacted with the user. At the extreme other end was the legacy system and our Java code that directly interacted with the legacy system. We divided that legacy-facing end into two pieces, one slice we called the \u0026ldquo;Brokers\u0026rdquo; and the other we called the \u0026ldquo;DAO\u0026rsquo;s\u0026rdquo;. The idea was that the DAO classes would know about the files in the legacy system, and would know about the subroutines or the API routines that we had built in the legacy system. The Brokers would talk to the DAO\u0026rsquo;s and get data back, and the assemble them into \u0026ldquo;Business Objects\u0026rdquo; that we could pass on to the layers of the applicaitons that were closer to the users.\nWe still had an issue with the nature of the data that came back from the DAO\u0026rsquo;s. It was a proprietary structure in Java that was provided by the database vendor, and it handled the quirks of the database that was being used. We messed around for a while trying to come up with something more generic, but our Java skills at the time just weren\u0026rsquo;t up to the task. Finally, we just accepted that if we replaced the legacy system, then we\u0026rsquo;d have to scrap the whole DAO layer and do substantial work to the Broker layer to handle the data coming in a different format.\nIn the end, after more than a decade, the legacy system was replaced. However, the people in the corner offices with the two and three letter job titles decided, without consulting with the programmers, to toss everything - including our Java applications - and start from scratch with a new system. They never realized how much effort we had put in to build something that could still work even if the legacy system was replaced.\nBut it wasn\u0026rsquo;t a total loss. The divisions that we built in our architecture allowed us to deploy the Broker and DAO layers as separate libraries, and resuse them in over and over again for a large number of desktop applications. This saved us a huge amount of effort and helped to simplify our system design. And, believe me, we needed all the help we could get in that department.\nThroughout all of this we continued to move to a better and better understanding of how to build GUI applications with JavaFX. While we were saddled with a lot of early mistakes that we had to live with for a long time, we were able to make it all work because we dealt with that one question, \u0026ldquo;What happens if the legacy application goes away?\u0026rdquo;, right at the beginning.\nThat structure is what this article is about. It\u0026rsquo;s a look at what is probably the most common approach to handling external systems and integrating them with GUI applications. There\u0026rsquo;s no code in this article, it\u0026rsquo;s all about explaining what the parts do, and why they are connected together this way.\nOverall Application Structure # If you\u0026rsquo;ve read my articles about Model-View-Controller-Interactor (MVCI), then you have an idea about how to keep your business logic out of your layout code, and how to keep your layout code out of your business logic. But how do you put it all together to make a big, complicated application that does a lot of back-end stuff?\nLet\u0026rsquo;s start by takeing a 10,000\u0026rsquo; look at how an application goes together:\nAt the very top we have the user, who interacts with the View layer of our MVCI frameworks. At the bottom, we have a collection of databases, legacy systems, external systems, business partner systems and external resources.\nLet\u0026rsquo;s look at all of the parts, and what they do:\nThe Model-View-Controller-Interactor (MVCI) Frameworks # I\u0026rsquo;ve drawn this out using MVCI, but the rest of the structure works almost as well when you are using MVC, MVP or MVVM. In those frameworks, substitute the Model for the MVCI Interactor when thinking about how they connect down to the Broker/Service layer.\nIn the diagram, the MVCI parts look like this:\nIf you\u0026rsquo;ve read some of the other articles, you may have come upon this diagram, instead:\nThey are essentialy the same, but the second one shows the external interactions.\nIn a practical sense each one of the vertical MVCI constructs provides a specific set of features to the application. For instance, the one on the far left might be the main menu for the application and handle login and authentication, the next one might provide Customer search and lookup, the next one might provide Customer CRUD functions. Other ones might provide Order lookup, and then Order CRUD, inventory inquiry, and so on.\nWhen you look at this way, each feature in the application slices vertically from the View at the top right down to the external applications it might need to access at the bottom. If you were going to implement a new feature to, say, add a field onto a screen, then implementing it would involve the layout changes, adding the corresponding data to the Model, then adding it to a Domain Object and updating the Broker to populate and retrieve it. Finally, you\u0026rsquo;d need to add it to the DTO so that it can be passed to a DAO, which would then need to be updated to pack and unpack it, and then updates to the related database to store it.\nScope of a feature, then, is related to the width of that vertical slice, given that it always has to extend from the top right down to the bottom.\nThe MVCI framework itself is divided into 4 parts. Let\u0026rsquo;s take a very quick look at the parts:\nModel # Unlike in MVC, MVVM, or MVP, the Model in MVCI is simply a Presentation Model. This means that it holds data used by the View in a format best suited for the View. In JavaFX, this means that the data elements are JavaFX Observable types, which can be bound to Properties in the layout Nodes. Note that all of the other three components of MVCI (the Controller, the View and the Interactor) share a reference to the Model, so all of the components can access the data.\nIn practice, the View accesses the elements of the Model as Observable types via Binding. At the other end, the Interactor virtually ignores the Observable nature of these elements and access their values directly. In this way, the Model provides a data pipeline between the business logic and the GUI without either end knowing about the existence of the other.\nView # This is the piece that interacts with the User. This is NOT just the layout, it\u0026rsquo;s also the connection to the Presentation Model and the configuration of the Nodes included how they will act when activated. This means the definition of Events due to mouse clicks, keystrokes and anything else that can happen in the GUI.\nIn practice, the View will usually be an implementation of the JavaFX Region class, which is created and configured by a ViewBuilder class, which is just an implementation of the JavaFX Builder\u0026lt;Region\u0026gt; interface. So you won\u0026rsquo;t have a class called View in your MVCI implementation. Rather, you\u0026rsquo;ll usually have Controller.getView() method which delegates to Builder.build() method.\nController # The Controller is the central component of MVCI. It handles instantiation of all of the other components and provides the facility for the View to invoke business logic in the Interactor. The Controller is the ONLY component with public methods that can be invoked from outside the MVCI framework. Therefore, it is the component which allows MVCI frameworks to be connected together in a larger application.\nWhile the View and the Interactor share data freely via the Model, the Controller is the conduit for actions initiated in the View that need to invoke methods in the Interactor. Think about something like a \u0026ldquo;Save\u0026rdquo; Button. Clicking on it needs to invoke some kind of activity in the Interactor. It doesn\u0026rsquo;t need to transmit any data, because the Model is already up-to-date with the View (because of the Bindings), and accessible to the Interactor. The Controller provides some sort of functional element, like a Consumer or a Runnable to the View, and the View will invoke it when needed. This functional element will handle any background threading that\u0026rsquo;s require, and call a method in the Interactor.\nInteractor # This is the component that contains the business/application logic of the framework. It\u0026rsquo;s the only MVCI component which is allowed to deal with Domain Objects, and a large part of its job is to translate data between the Model and the Domain Objects in order to facilitate connections to a persistence layer.\nAlthough any of the frameworks is essentially a GUI oriented construct, the Interactor is the piece where you could, if you thought it had value, design without any knowledge of JavaFX. By this, I mean that you write it so that it would compile without any import javafx.* statements. You could write your Model with the fields designed as JavaFX Beans, and then only access the delegating getter and setter methods for the values. Then the Interactor wouldn\u0026rsquo;t even be aware that they were Observable types at all.\nIn practice I prefer to see all of the business logic an one place, and that includes establishing Binding relationships between elements of the Model. That usually means that some code in your Interactor will have to do some JavaFX work to create the Bindings.\nDomain Objects # Domain Objects represent fundamental groupings of application data and logic within the context of a \u0026ldquo;domain\u0026rdquo;. In a business application these would be called \u0026ldquo;Business Objects\u0026rdquo;, but \u0026ldquo;Domain Objects\u0026rdquo; is a more generalized term that encompasses any kind of application. Note that a Domain Object is not necessarily a POJO and is not restricted to containing only data elements. More about this later.\nAs an example, a Customer might be a Domain Object in an application. It could contain data elements including an account number, a name, address information and a list of recent purchases. Whatever makes sense within the context of the business (or, if you like, the domain). It\u0026rsquo;s also allowed to have whatever logic makes sense in its context. For instance, maybe we have a field for birth date. From birthDate we might have several functions that return age in years with rounding to the nearest year. This might not be of value if we need the age in years to determine eligibility to purchase alchohol, so we might need a function to calculate age rounded down. Maybe we need a function to calculate age in days, and we need to make sure that we take into account leap years. It\u0026rsquo;s fine to have these functions in Customer, as well as lots of other logic as required.\nBrokers and Services # I\u0026rsquo;m not sure if anyone else uses the term \u0026ldquo;Broker\u0026rdquo;, but I see it as a sub-type of \u0026ldquo;Service\u0026rdquo; that\u0026rsquo;s primarily concerned with creating or saving Domain Objects to/from a persistence layer. \u0026ldquo;Services\u0026rdquo; also deal in Domain Objects, but have a broader functionality that can include just about all of the business/application logic that spans the entire application. If you\u0026rsquo;re vigorously appling DRY (Don\u0026rsquo;t Repeat Yourself), then any time that you find yourself writing the same logic in two different Interactors, you should push that logic down into Broker/Service layer and expose it as a public method.\nBrokers all understand how to use one or more Data Access Objects (DAO\u0026rsquo;s) and understand the format of the data that they need to transfer to and from those DAO\u0026rsquo;s. It\u0026rsquo;s entirely possible that a Broker will need to connect to two or more DOA\u0026rsquo;s in order to complete a request. For instance, in our Customer example in the previous section, that recent purchase information may come from a sales application or database, while the rest may come from a CRM system. So the CustomerBroker would need to access methods in both the CrmDao and the SalesDao, which is fine.\nBrokers also understand the structure of the Data Transfer Objects (DTO\u0026rsquo;s) that will be transfered to and from the DOA layer. The use the DTO\u0026rsquo;s to assemble Domain Objects that are the building blocks of application logic.\nSo, generally speaking, a Broker will get a request to retrieve some kind of data from external services and it will know which DOA\u0026rsquo;s to contact and how to call their public methods in order to get the data that it needs. It will then assemble this data into Domain Objects which it will return to the requesting Interactor (or other Broker, or whatever). Conversely, it would receive a Domain Object from an Interactor in a request to save the data, which it would then transate into calls to DOA methods to perform the save.\nData Transfer Objects (DTO`s) # DTO\u0026rsquo;s are used\u0026hellip;wait for it\u0026hellip;to transfer data between the the DAO\u0026rsquo;s and the Brokers/Services. They represent a contract, if you like, between those two end points. The Broker asks for information, and the structure of the DTO defines what data elements will be delivered and how they will be structured.\nUnlike Domain Objects, Data Transfer Objects are pure data, essentially POJO\u0026rsquo;s. In Java, you could use Record classes and in Kotlin you can use Data classes.\nBeyond that, it\u0026rsquo;s probably practical to set up DTO\u0026rsquo;s so that they are easily serializeable. Then your DAO can simply use standard libraries to convert JSON data from a REST API into a DTO object.\nData Access Objects (DAO\u0026rsquo;s) # DAO\u0026rsquo;s are the objects that actually connect to the various databases, applications, websites and file systems that the applications need to run. They can be used to provide persistence, but these external systems can provide virtually any service you can visualize.\nIn the diagram, I\u0026rsquo;ve shown each DOA connecting to a single external service because that\u0026rsquo;s probably the most common real-life situation. However, there\u0026rsquo;s no real rule about this. If it makes more sense to have a single DOA connect to two services - perhaps because the presence of multiple services is irrelevant to the business logic at the Broker level - then build it that way.\nDOA\u0026rsquo;s know everything they need to about the services that they connect to. If they are connecting to a database, then they are aware of the tables and fields in the database and how to construct queries to retrieve data from the database. They contain logic to deal with connection limitations, authentication, caching and performance optimization. The are also aware of the structure of the data that they will receive or send to/from the service.\nDesign Decisions # Within this overall structure, there\u0026rsquo;s lots and lots of room for a team to argue about how to best implement any specific aspect of the application design. Lots of places to make bad decisions - and good ones.\nHiding and Revealing # Pretty much all of this application structure is intended to serve one purpose - limit and control coupling throughout the application.\nToo much coupling is deadly to an application. Imagine having to refactor multiple Views because some external business partner upgraded their database! It can happen. Coupling is the single most important factor in how complicated it becomes to make a change or enhancement to your application.\nThe best way to understand coupling is to think of it in terms of hiding and sharing, responsibility and delegation. Write every class so that it jealously guards it secrets, and desparately clings to every bit of power and responsibility that it has a right to. Shared responsibility may be great for human teams and teamwork, but it\u0026rsquo;s 100% wrong in an application design.\nIf you look at the application structure, you\u0026rsquo;ll see that every single layer is designed to insulate the layers above and below it from the layers on the other side - to the extent that they are not even aware of the existence of any of other elements. And\u0026hellip;if they aren\u0026rsquo;t even aware of them, then they cannot be coupled to them.\nYou\u0026rsquo;ll also notice that virtually all of the functional coupling is downwards. That is to say, that all of the elements contain references only to the elements immediately below them, and can therefore only call methods in the elements below them. For instance, no DOA can call Broker/Service method, and no Brokers can call Interactor methods. At the other end, the View calls Controller methods, but exposes no methods to the Controller. As a subclass of Region the View exposes Region methods to whatever layout element contains in in the GUI, but cannot call any methods of that layout element.\nThe DOA\u0026rsquo;s are designed to isolate the Broker/Service layer from the external services. There\u0026rsquo;s simply no coupling between the external services and the Brokers because they aren\u0026rsquo;t even aware of each other.\nThe Data Transfer Objects remove coupling between the data structures from the external services and the Brokers. The DTO\u0026rsquo;s are just Java Data or Record classes, and they have been sanitized from any connection to the original external structure. They represent the contract between the Broker and the DAO, \u0026ldquo;You ask for information, this is what I\u0026rsquo;ll send you, and I\u0026rsquo;ll send it to you in this format, even if the external source is completely changed\u0026rdquo;.\nPutting Code in the Right Place # Another key component to limiting coupling is to put your code in the right place. Code that\u0026rsquo;s in the wrong place automatically increases coupling because it inevitably means you have to reach into some other place to get information that you need.\nThere are two techniques that you can use to help ensure that you put your code in the right place\u0026hellip;\nDon\u0026rsquo;t Repeat Yourself # DRY is probably the most important rule, but it also so easy to break. Personally, I don\u0026rsquo;t think you can \u0026ldquo;over-apply\u0026rdquo; DRY, even though I have seen arguments to the contrary. Repeated code can also hide on you, especially if it\u0026rsquo;s written at different times by different programmers.\nUsing DRY results in putting code in the right place because it tends to ensure that your logic is unique to the place in your system where the data is located. As an example, let\u0026rsquo;s say that you found the same logic in two different Interactors. Clearly, that logic is not unique to the data present in either Interactor. There\u0026rsquo;s a very good chance that you can move that logic into a Broker/Service class and expose it as a public method. There\u0026rsquo;s also a very good chance that the logic centres around the data that is \u0026ldquo;native\u0026rdquo; to that Broker or Service.\nAvoid \u0026ldquo;Feature Envy\u0026rdquo; # \u0026ldquo;Feature Envy\u0026rdquo; is when one class contains logic that really should be a feature, or a public method, in another class.\nA good indicator of this is when you have a block of code in a class that primarily uses several fields or methods of just a different single class. In that case, the logic probably should be incorporated into that other class. Any local data used in the original block of code can be passed as a parameter.\nThere\u0026rsquo;s a number of benefits to removing feature envy\u0026hellip;\nFirst, as a public method testing is much, much easier than as a block of code incorporated inside another method, which might even be private.\nSecondly, left in the first class it clutters up the logic which is actually unique to that class. This makes it much harder to understand and maintain that code.\nThirdly, it\u0026rsquo;s almost a prerequite to violating DRY.\nFinally, and most importantly, it increases coupling. Now you have a block of code which is dependent on the implementation of several fields and methods of some other class. If any of those change, then it could break your logic. And the programmer who wants to change any of those implementations has to be aware of how they are used throughout the application. If the logic is incorporated into that other class and exposed as a public method, then any programmer than changes the internals of that class is also responsible for ensuring that they don\u0026rsquo;t break that public method.\nStatic Methods # One thing that eventually will happen, is that you look at Customer and you\u0026rsquo;re shocked to see how much code it\u0026rsquo;s grown to hold. You\u0026rsquo;ll feel that your class has become bloated and unwieldy. And you might be right.\nThen you\u0026rsquo;ll look at CustomerBroker and you\u0026rsquo;ll say to yourself, \u0026ldquo;This is a good place to put methods that manipulate Customer\u0026rdquo;. And you\u0026rsquo;ll start moving stuff out of Customer and start delgating to static methods in CustomerBroker, passing them Customer.this. Things like Customer.canBuyAlchohol() will delegate to CustomerBroker.canBuyAlchohol(Customer customer).\nAnd this will reduce the bloat in your domain objects.\nOr will it?\nHonestly, when you create a static method in a class which is only called by other class, especially just one method in that other class, all you\u0026rsquo;ve really done is spread your class across two classes. There\u0026rsquo;s no practical advantage to it. Even if you cheat.\nCheat?\nSo you make your delagate methods not static. But it\u0026rsquo;s still effectively static because it doesn\u0026rsquo;t use or update the state of the Broker class. So it could easily be static if you wanted - but now you have to pass an instance of the Broker to the Domain Object when it\u0026rsquo;s created. Or you bypass the delegating function in the Domain Object to pass the Domain Object directly to the delegate method in the Broker from the Interactor. Now you\u0026rsquo;ve made it worse because you\u0026rsquo;ve increased the coupling between the Interactor and the Broker.\nConclusion # The truth is that any large application that does real work is going to have ample complications to keep you busy for a long time. It will seem like none of the design decisions are simple, and everything has some twist that makes life more difficult.\nHaving a solid idea about how your application architecture will help to reduce the coupling through the application can make a lot of these decisions much easier.\nThe nice thing about MVCI with this architecture is that it just works. MVCI provides exactly the things you need to write a Reactive JavaFX GUI connected to business logic. The tiered structure below it isolates everything else so that it doesn\u0026rsquo;t become a tangled mess of logical spaghetti.\n","date":"25 May 2024","externalUrl":null,"permalink":"/javafx/techniques/mvci/applications/","section":"JavaFX","summary":"Understanding the Model-View-Controller-Interactor framework is one thing, but putting it all together to create a large and complicated application means that you’ll need to know how to integrate it with external services.","title":"Application Structure With MVCI","type":"javafx"},{"content":"","date":"30 April 2024","externalUrl":null,"permalink":"/javafx/techniques/style/","section":"JavaFX","summary":"Articles that deal with how to code properly, especially in a JavaFX context","title":"Coding Style","type":"javafx"},{"content":" Introduction # After literally a decade writing JavaFX applications, it can be hard to remember how I thought things were supposed to be done when I started out. Thankfully, there seems to be a nearly endless supply of beginners having trouble, posting their code and giving us a glimpse into their thought processes.\nIn this article we are going to look at some beginner code that really highlights how much more complicated JavaFX is when you don\u0026rsquo;t know a lot about it.\nEven after getting this code to compile and run, it\u0026rsquo;s still not clear to me exactly what it\u0026rsquo;s supposed to do. In general terms, it displays a screen with a title and three Buttons, one of which is supposed to hide some of the Buttons while displaying a Label\\TextField\\Button combination to allow some user entry. This data entry display also contains a \u0026ldquo;Back\u0026rdquo; Button to go back to the original, three Button display.\nWhile the general idea of the application is easy to figure out, details of how the screens are supposed to look are a bit murky because the approach taken, and the fact that it wouldn\u0026rsquo;t even compile in its original state. I\u0026rsquo;ve taken a \u0026ldquo;best try\u0026rdquo; approach when refactoring it to come up with something that seems to hold to the original spirit, if not the fine details of the intended layout.\nMy version is in Kotlin. It\u0026rsquo;s pretty small, so you shouldn\u0026rsquo;t have much trouble following it and the main point of this article is to highlight general concepts and JavaFX techniques - none of which are language dependent.\nThe Code # public class opening extends Application { // first scene (buttons Label gameTitle = new Label(\u0026#34;Basil\u0026#39;s Cat Game :3\u0026#34;); // new game buttons TextField putNameHere = new TextField(); Label putNameTxt = new Label(\u0026#34;Name:\u0026#34;); Button nameOkButton = new Button(\u0026#34;OK\u0026#34;); VBox titleVBox = new VBox(); HBox titleHBox = new HBox(); GridPane titleGroup = new GridPane(); Button backBtn = new Button(\u0026#34;BACK\u0026#34;); VBox title = new VBox(); Button exitBtn = new Button(); Group titleGroupSec = new Group(); boolean buttonClicked = false; @Override public void start(Stage primaryStage) throws Exception { // needs buttons to select what to do and also a text field to store a name gameTitle.setFont(Font.font(STYLESHEET_CASPIAN, 40)); Button playBtn = new Button(\u0026#34;NEW\u0026#34;); playBtn.setFont(Font.font(30)); playBtn.setBackground(Background.EMPTY); Button loadBtn = new Button(\u0026#34;LOAD\u0026#34;); loadBtn.setFont(Font.font(30)); loadBtn.setBackground(Background.EMPTY); Button exitBtn = new Button(\u0026#34;EXIT\u0026#34;); exitBtn.setFont(Font.font(30)); exitBtn.setBackground(Background.EMPTY); Button exitBtnSec = new Button(\u0026#34;EXIT\u0026#34;); exitBtnSec.setFont(Font.font(30)); exitBtnSec.setBackground(Background.EMPTY); VBox btnVBox = new VBox(); btnVBox.getChildren().add(0, gameTitle); btnVBox.getChildren().add(1, playBtn); btnVBox.getChildren().add(2, loadBtn); btnVBox.getChildren().add(3, exitBtn); btnVBox.setAlignment(Pos.TOP_CENTER); btnVBox.setSpacing(20); titleGroup.getChildren().remove(titleHBox); titleGroup.getChildren().remove(titleVBox); titleGroup.getChildren().addLast(btnVBox); titleGroup.setAlignment(Pos.TOP_CENTER); titleGroup.setPadding(new Insets(50)); /** * maybe should change to say \u0026#34;NEW\u0026#34;? anyways, make them pick a name (move name * feature to here \u0026amp; show a quick text based tutorial that does some waiting * liek im sayin it to the player :3 * * make them roll for a cat nd name the cat. save the cat and it\u0026#39;s features to a * file that is specific to this player. * * make it so they can access a list to see player info (name) and cat info(how * many cats, names, breeds, traits, colors) */ playBtn.setOnMouseClicked(playBtnClickedEvent -\u0026gt; { btnVBox.getChildren().remove(loadBtn); btnVBox.getChildren().remove(exitBtn); btnVBox.getChildren().remove(playBtn); exitBtn.setAlignment(Pos.TOP_LEFT); gameTitle.setFont(Font.font(STYLESHEET_CASPIAN, 40)); putNameTxt.setFont(Font.font(STYLESHEET_CASPIAN, 20)); putNameHere.setEditable(true); nameOkButton.setFont(Font.font(10)); title.getChildren().add(putNameTxt); title.getChildren().addLast(exitBtn); title.getChildren().addFirst(gameTitle); title.setSpacing(10); title.setAlignment(Pos.TOP_CENTER); titleHBox.getChildren().addFirst(putNameHere); titleHBox.getChildren().addLast(nameOkButton); titleHBox.setPadding(new Insets(100)); titleHBox.setAlignment(Pos.CENTER); titleGroup.getChildren().addFirst(backBtn); titleGroup.getChildren().add(title); titleGroup.getChildren().addLast(titleHBox); }); /** * here, u will probably have to implement something more complex than a file * writer if you decide to use photos or graphics to make ur kitties :P * * call the names of players (only players that have pressed the PLAY button \u0026amp; * gotten a cat.. show the amt of cats they have next to their name :P) * * if they click on the button that has their name/amt of cats then make open * that save file in the stage */ loadBtn.setOnMouseClicked(loadBtnClickedEvent -\u0026gt; { }); exitBtn.setOnMouseClicked(exitBtnClickedEvent -\u0026gt; { primaryStage.close(); }); Scene scene = new Scene(titleGroup, 500, 500); primaryStage.setScene(scene); primaryStage.setTitle(\u0026#34;love u \u0026lt;3\u0026#34;); primaryStage.show(); } public static void main(String[] args) { launch(args); } } The Fundamentals # Breaking the Single Responsibility Principle (SRP) # SRP and DRY (see below) are the two foundational principles for writing good code. Yet beginners violate this all the time.\nEvery block of code should be directly responsible for one thing, and one thing only. Every method should be directly responsible for one, and one thing only. Every class should be directly responsible for one thing, and one thing only. Do you see a pattern here?\nIn this program we have one class that does everything, and one 130 line method that does almost everything. This is a clear indication that SRP has been violated.\nBreaking \u0026ldquo;Don\u0026rsquo;t Repeat Yourself\u0026rdquo; (DRY) # Take a look at this snippet from the code:\nButton playBtn = new Button(\u0026#34;NEW\u0026#34;); playBtn.setFont(Font.font(30)); playBtn.setBackground(Background.EMPTY); Button loadBtn = new Button(\u0026#34;LOAD\u0026#34;); loadBtn.setFont(Font.font(30)); loadBtn.setBackground(Background.EMPTY); Button exitBtn = new Button(\u0026#34;EXIT\u0026#34;); exitBtn.setFont(Font.font(30)); exitBtn.setBackground(Background.EMPTY); Here we have the same three lines of code repeated three times. The only differences are in the variable that is created and the text in the Button.\nWhy is this bad? First, it clutters things up. There\u0026rsquo;s now 9 lines of code that you have to read carefully to determine that all three Buttons are essentially configured the same. Secondly, if you want to change something, you now have to do it 3 times.\nFinally, it\u0026rsquo;s error prone. One of the most common places for making silly mistakes that way more time to track down than you\u0026rsquo;d expect comes from repeated blocks of code. Take a look at this:\nButton playBtn = new Button(\u0026#34;NEW\u0026#34;); playBtn.setFont(Font.font(30)); playBtn.setBackground(Background.EMPTY); Button loadBtn = new Button(\u0026#34;LOAD\u0026#34;); loadBtn.setFont(Font.font(30)); playBtn.setBackground(Background.EMPTY); Button exitBtn = new Button(\u0026#34;EXIT\u0026#34;); exitBtn.setFont(Font.font(30)); exitBtn.setBackground(Background.EMPTY); It\u0026rsquo;s almost the same block of code, but it has an error in it. The kind of error that you get from cutting and pasting repeatedly.\nYou should make it a habit to never cut and paste from your own code. Maybe from StackOverflow, but cutting and pasting from your own code almost guarantees that you\u0026rsquo;re breaking DRY, and you should just stop doing that.\nViolating YAGNI and Clinginess # Programmers always seem to want to over-engineer and future-proof everything. Yet the truth is that most of the time, \u0026ldquo;You Ain\u0026rsquo;t Gonna Need It\u0026rdquo; (YAGNI). And then, once they\u0026rsquo;ve built something they don\u0026rsquo;t need they won\u0026rsquo;t use the Delete key on it - clinginess.\nIn this example we have both titleGroupSec and buttonClicked which are instantiated as fields, but never used. In the end this just clutters things up and causes confusion.\nExcessive Coupling # Coupling is the thing that will really complicate your project. Both SRP and DRY are huge helpers in reducing coupling, which is another good reason to apply them.\nThe main tool in reducing coupling is to give every element in you application the smallest possible scope. Define variables inside of blocks of code when you can, and use local variables instead of fields whenever possible.\nBad Names # Yes, naming things well is hard. Get over that and just do it.\nAvoid the temptation to be cute or clever with your names. We all go through that phase. I know that I did, and I\u0026rsquo;m pretty sure that none of my coworkers appreciated it.\nIn this code we have:\ntitleHBox.getChildren().addFirst(putNameHere); What is putNameHere? You really cannot tell from this code. It\u0026rsquo;s a Node subclass or it couldn\u0026rsquo;t be put into an HBox, but other than that, we don\u0026rsquo;t know. Yes, you can put the cursor over it in any modern IDE and it will tell you what it is. But you still can\u0026rsquo;t just read the code an know. I can appreciate that putNameHere is cute, but it\u0026rsquo;s not very clear and, therefore, not a very good name.\nI\u0026rsquo;m not against the idea of putting an indicator of the Node type in the name, as in titleHBox in the snippet. Just be sure to change the name if you change the Node type. We also have this in the code:\nGridPane titleGroup = new GridPane(); Presumably, titleGroup was originally defined as a Group and later changed to a GridPane. Looking at the code where it was used, I had no idea it was a GridPane and this had me confused for a while.\nJavaFX Principles # Now we move on to JavaFX principles that you should try to follow, and how you can get into trouble if you don\u0026rsquo;t.\nChanging Layouts # I have written thousands and thousands of lines of JavaFX layout code, and I can hardly remember ever writing any code to change the layout of a screen while it was running. But beginners love to do this. I guess it just seems like the natural way to do things.\nIn this particular example, the desire to change the layout \u0026ldquo;on the fly\u0026rdquo; leads to virtually all of the other problems with the code.\nIn particular, this is not a thing:\ntitleHBox.getChildren().addFirst(putNameHere); titleHBox.getChildren().addLast(nameOkButton); This just created a bunch of red squiggly lines in my IDE. I think that these methods, addFirst() and addLast() are for LinkedLists, which the ObservableLists from Pane.children are not.\nHowever, the need to put stuff in at the beginning of a list of children is really only something you\u0026rsquo;ll need if you\u0026rsquo;re messing around with a running layout. There\u0026rsquo;s also lots of calls to List.remove(), which does work, but shouldn\u0026rsquo;t ever be needed.\nPart of the problem is that in order to do all of this swapping around, you need to have references to the various Nodes that you\u0026rsquo;re going to be adding and removing on the fly. And, since you\u0026rsquo;re going to be doing all of the swapping from different places in the layout, these references need to be global throughout all of the layout code. This creates a large amount of coupling. A really large amount.\nIn this code, we have two places where the swapping would occur. One is in the action for playBtn, and the other would be in the backBtn action. However, there is no code included for the backBtn action, probably because putting everything back was a daunting task that the programmer couldn\u0026rsquo;t bring themselves to face yet. Both these action handlers would have to have access to references to the containing layout class and virtually all of the children.\nNot Keeping the Layout Understandable # You should be able to look at a piece of layout code and get an idea in your head about how it\u0026rsquo;s going to look when it\u0026rsquo;s running. If you can\u0026rsquo;t, then something has gone seriously wrong.\nIn general, following SRP and DRY is going to force you to break your layout into chunks, and put those chunks together in a manner that makes it clear how the layout should look.\nIn this example we can see a Button instantiated and configured in one section of code, added to a layout 20 lines later, and then getting its action defined 50 lines after that. This means that you have to read through almost all of the layout code to understand how an element is configured and added to the layout.\nNot Using the Correct Layout Classes # Perhaps this rule should just be called \u0026ldquo;Don\u0026rsquo;t Abuse AnchorPane and GridPane\u0026rdquo;. AnchorPane is the most abused layout class, perhaps because it\u0026rsquo;s alphabetically first in the choices in SceneBuilder and beginners think it\u0026rsquo;s some kind of default. Then they never use it as an AnchorPane and use setLayoutX() and setLayoutY() (by dragging stuff in SceneBuilder) to position the children.\nAnd for some reason, beginners think that everything else needs to be a GridPane. I don\u0026rsquo;t know why.\nHere we have titleGroup which is actually a GridPane but its not used like a GridPane`. Children are dumped in, but never assigned to rows or columns. I was a little tempted to see what that actually does, but then I decided I didn\u0026rsquo;t need to know. I do know that when I ran the original code, it looked a little confused.\nNot Using the Correct Event Types # This code uses the MouseClickedEvent for all of the Button actions. This will work for mouse clicks, but won\u0026rsquo;t work for keyboard access. So if the user tabs around the screen to put the focus on a Button and hits Enter, it\u0026rsquo;s not going to work.\nThe New Version # The new version is in Kotlin. Let\u0026rsquo;s take a look at it first:\nclass Opening : Application() { private val isShowingNewGame: BooleanProperty = SimpleBooleanProperty(false) override fun start(primaryStage: Stage) { primaryStage.scene = Scene(createContent(), 500.0, 500.0).apply { Opening::class.java.getResource(\u0026#34;beginners.css\u0026#34;)?.toString()?.let { stylesheets += it } } primaryStage.title = \u0026#34;love u \u0026lt;3\u0026#34; primaryStage.show() } private fun createContent(): Region = BorderPane().apply { top = HBox(Label(\u0026#34;Basil\u0026#39;s Cat Game :3\u0026#34;) styleWith \u0026#34;title-label\u0026#34;).centred() bottom = HBox(createButton(\u0026#34;EXIT\u0026#34;) { Platform.exit() }).centred() center = StackPane(buttonBox() showWhen isShowingNewGame.not(), newGameBox() showWhen isShowingNewGame) } private fun buttonBox() = VBox(20.0, createButton(\u0026#34;NEW\u0026#34;) { isShowingNewGame.value = true }, createButton(\u0026#34;LOAD\u0026#34;) {}).centred() private fun newGameBox(): Region = BorderPane().apply { center = VBox( HBox(6.0, Label(\u0026#34;Name:\u0026#34;) styleWith \u0026#34;prompt-label\u0026#34;, TextField(), Button(\u0026#34;OK\u0026#34;)).centred() ).centred() top = Button(\u0026#34;Back\u0026#34;).apply { setOnAction { isShowingNewGame.value = false } } styleWith \u0026#34;transparent-button\u0026#34; } private fun createButton(title: String, action: EventHandler\u0026lt;ActionEvent\u0026gt;) = Button(title).apply { onAction = action styleWith(\u0026#34;transparent-button\u0026#34;) } } infix fun \u0026lt;T : Node\u0026gt; T.styleWith(selector: String): T = apply { styleClass += selector } infix fun \u0026lt;T : Node\u0026gt; T.showWhen(observed: ObservableBooleanValue): T = apply { visibleProperty().bind(observed) managedProperty().bind(observed) } fun HBox.centred() = apply { alignment = Pos.CENTER } fun VBox.centred() = apply { alignment = Pos.CENTER } fun main() { Application.launch(Opening::class.java) You\u0026rsquo;ll see that pretty much all of it is still inside Opening which extends Application. It was left this way because the application really doesn\u0026rsquo;t \u0026ldquo;do\u0026rdquo; anything, so there was no need to create a framework for the application. It would be easy, though, to replace createContent() with OpeningController.getView() and introduce an MVCI framework.\nApplication.start() # Application.start() should only deal directly with the mechanics of Stage and Scene. If you put anything else in here, you\u0026rsquo;re doing it wrong. The original code had everything in Application.start(), which was an extreme violation of SRP:\noverride fun start(primaryStage: Stage) { primaryStage.scene = Scene(createContent(), 500.0, 500.0).apply { Opening::class.java.getResource(\u0026#34;beginners.css\u0026#34;)?.toString()?.let { stylesheets += it } } primaryStage.title = \u0026#34;love u \u0026lt;3\u0026#34; primaryStage.show() } This is short and sweet. We create the Scene and delegate the creation of the layout to createContent(). Then we attach the StyleSheet, and put it into the Stage. Then we configure the title and put the Stage on the screen.\nThat\u0026rsquo;s it. That\u0026rsquo;s all this method should ever do.\nThe createContent() Method # The entire layout comprises about 20 lines of code across 4 methods. Let\u0026rsquo;s look at the top level method, createContent():\nprivate fun createContent(): Region = BorderPane().apply { top = HBox(Label(\u0026#34;Basil\u0026#39;s Cat Game :3\u0026#34;) styleWith \u0026#34;title-label\u0026#34;).centred() bottom = HBox(createButton(\u0026#34;EXIT\u0026#34;) { Platform.exit() }).centred() center = StackPane(buttonBox() showWhen isShowingNewGame.not(), newGameBox() showWhen isShowingNewGame) } It\u0026rsquo;s just 5 lines of code, so it can\u0026rsquo;t be hard to understand. You can immediately see that this is going to return a BorderPane and that the top region is populated with a styled Label popped into an HBox so that it can be centred. There\u0026rsquo;s a bit of Kotlin magic here, the HBox.centred() extension function and the Node.styleWith() extension function, both of which we\u0026rsquo;ll look at in a bit. But for now, it\u0026rsquo;s pretty clear what they do.\nThe bottom region gets an exit Button created through a builder/factory method and put in an HBox so that it too can be centred.\nThe center region gets a StackPane with two \u0026ldquo;boxes\u0026rdquo; created through builder methods and then modified with something called showWhen. We\u0026rsquo;ll look at showWhen later, but those builder methods now\u0026hellip;\nThe buttonBox() Method # private fun buttonBox() = VBox(20.0, createButton(\u0026#34;NEW\u0026#34;) { isShowingNewGame.value = true }, createButton(\u0026#34;LOAD\u0026#34;) {}).centred() This is technically one line of code, but it\u0026rsquo;s been formatted out to 2 for readability. It\u0026rsquo;s a VBox with spacing of 20 pixels between the children. The children are two Buttons created through that same builder/factory method we saw used in createContent(). The contents of the VBox are centred. That\u0026rsquo;s all\u0026hellip;pretty simple.\nThe newGameBox() Method # private fun newGameBox(): Region = BorderPane().apply { center = VBox( HBox(6.0, Label(\u0026#34;Name:\u0026#34;) styleWith \u0026#34;prompt-label\u0026#34;, TextField(), Button(\u0026#34;OK\u0026#34;)).centred() ).centred() top = Button(\u0026#34;Back\u0026#34;).apply { setOnAction { isShowingNewGame.value = false } } styleWith \u0026#34;transparent-button\u0026#34; } This is another BorderPane because we want to have the \u0026ldquo;Back\u0026rdquo; Button in the top left corner, and the HBox holding the input stuff centred. There\u0026rsquo;s no problem putting BorderPane inside another BorderPane or any other container class. Also, there\u0026rsquo;s nothing \u0026ldquo;heavyweight\u0026rdquo; about BorderPane that would have you want to avoid it.\nThe center region has a VBox holding an HBox both of which are centred. This puts the contents of the HBox right in the middle of the region. Inside the HBox we have a styled Label that says, \u0026ldquo;Name:\u0026rdquo;, a TextField and a Button labeled \u0026ldquo;OK\u0026rdquo;. Neither the Button nor the TextField do anything simply because they didn\u0026rsquo;t in the original code.\nThe \u0026ldquo;Back\u0026rdquo; Button has an action that sets the value of isShowingNewGame to false. isShowingNewGame is the only field in the application now, and it\u0026rsquo;s a BooleanProperty. Clicking \u0026ldquo;Back\u0026rdquo; sets it to false.\nThe Button Builder/Factory Method # private fun createButton(title: String, action: EventHandler\u0026lt;ActionEvent\u0026gt;) = Button(title).apply { onAction = action styleWith(\u0026#34;transparent-button\u0026#34;) } This is an application of DRY to the original code. All the Buttons were configured similarly, so this method does the repeated stuff. Additionally, we have a parameter that accepts the EventHandler that\u0026rsquo;s attached to the Button for its OnAction Event. We\u0026rsquo;re not setting the font or the background programmatically any more, either, now were using StyleSheets via Node.styleWith().\nKotlin Extension Methods # That\u0026rsquo;s it for the layout code. However, there are those mysterious Kotlin extension functions that were used all over the place. Let\u0026rsquo;s look at them:\ninfix fun \u0026lt;T : Node\u0026gt; T.styleWith(selector: String): T = apply { styleClass += selector } infix fun \u0026lt;T : Node\u0026gt; T.showWhen(observed: ObservableBooleanValue): T = apply { visibleProperty().bind(observed) managedProperty().bind(observed) } fun HBox.centred() = apply { alignment = Pos.CENTER } fun VBox.centred() = apply { alignment = Pos.CENTER } In Kotlin, you can add a method to an existing class without extending it using \u0026ldquo;Extension Functions\u0026rdquo;. These evaluated statically, so they\u0026rsquo;re not really breaking any JVM rules, but they can save a lot of boilerplate with JavaFX. You might also notice that these methods are declared outside of Opening and not part of any class at all. This is something that you can do in Kotlin, and these are generally referred to as \u0026ldquo;top level functions\u0026rdquo;.\nLet\u0026rsquo;s look at HBox.centred(). It uses a \u0026ldquo;Scope Function\u0026rdquo; called apply{} which is defined as a member function of Object and passes the object it\u0026rsquo;s called on to the body of apply{} as this. Then the function returns the object itself. Just like in Java, when this can be inferred, it can be left out. In this case it shortens this.setAlignment(Pos.CENTER) to alignment = Pos.CENTER where setAlignment() is also replaced with the field accessor version.\nUnfortunately, alignment is defined at the HBox and VBox level, so we need an extension function for each one. Each one will return the HBox or VBox it\u0026rsquo;s called on, acting like a decorator.\nFor the styleWith there are a couple extra things. First, styleClass is defined at Node, but we want our decorator to return the actual class of the object it\u0026rsquo;s called on. So, if we call Label.styleWith() we want a Label back, not a Node. So we have to define it generically as a type T that\u0026rsquo;s a subclass of Node. The styleClass value is a List and we use the List.plusAssign() method to add a new selector to it, through +=.\nFinally, we have a modifier, infix. Using this means that we can skip the . and the (). You can define any function that has one passed parameter this way if you want. This allows Label(\u0026quot;Hello\u0026quot;) styleWith \u0026quot;some-selector\u0026quot;. Once again, it acts a decorator, returning the original object.\nThe showWhen() Function # This is another infix decorator function, one that simply binds the Visible and Managed Properties of the Node to some ObservableBooleanValue.\nThis is called in two ways, one with isShowingNewGame as the parameter, and one with isShowingNewGame.not() as the parameter. In other words, each the two Nodes will only be visible when the other one is not.\nOriginally I had the calls to this method as part of the builder methods buttonBox() and newGameBox(), but then I thought it made more sense as part of the layout code since it was related more to how the entire layout functions. Having the two calls sitting side-by-side in the code also made it easier to see how it was being used:\ncenter = StackPane(buttonBox() showWhen isShowingNewGame.not(), newGameBox() showWhen isShowingNewGame) How Does This Fix The Issues? # Single Responsibility Principle # Now we don\u0026rsquo;t have any methods that are larger than 5 lines long. It\u0026rsquo;s pretty hard to cram too much responsibility into 5 lines or less.\nOne place where we do get close is with the definition of the top section of the main BorderPane and the center section of the BorderPane defined in newGameBox(). In both these cases we\u0026rsquo;re configuring multiple elements at one time. In the second case it\u0026rsquo;s a VBox holding an HBox holding a Label, TextField and Button.\nIn practice I find that there\u0026rsquo;s a certain \u0026ldquo;threshold of triviality\u0026rdquo; that can be applied to these situations. Even if you\u0026rsquo;re technically breaking SRP, are the second, third and fourth responsibilities so trivial, individually and collectively, that they can be left in with the primary responsibility? The only trade-off with following SRP to the letter is that you need to drill down into the delegate methods to see what they do. So it\u0026rsquo;s never going to hurt much to split out and delegate the extra responsibilities, but you might lose some readability.\nIn the case of the center section defined in newGameBox(), it\u0026rsquo;s only trivial because neither the TextField nor the Button do anything. The moment that you start to add configuration in for these elements, it will breach the \u0026ldquo;threshold of triviality\u0026rdquo; and needs to be split out into its own method.\nDon\u0026rsquo;t Repeat Yourself # Nothing is repeated here. Between the extension functions and the redesign itself, all of the repetitious code has been excised from this application.\nYAGNI # In all honesty, the TextField and the \u0026ldquo;OK\u0026rdquo; Button feel a bit like YAGNI, simply because they don\u0026rsquo;t do anything. In truth, though, you are going to need them, and they\u0026rsquo;re more like unfinished features than anything else. That being said, half implemented features are almost as bad as YAGNI violations. Features should be so small that there\u0026rsquo;s never any possibility of implementing only half of one.\nIn this particular case, it seems like making that \u0026ldquo;OK\u0026rdquo; Button do something is going to require some application logic and access to a persistence layer. That will mean adding a framework, which will mean some refactoring and redesign. That should have been done before the Button was added to the screen.\nExcessive Coupling # The entire application now has only one variable defined, and that is the final BooleanProperty field, isShowingNewGame. It is very, very difficult to have coupling without variables to reference. In fact, isShowingNewGame was only introduced to act as a central coupling point for the functionality of the Buttons and layout swapping - eliminating the need to instantiate those Buttons or layouts as variables in order to reference them from other layout elements.\nBad Names # Almost no variables means almost no variables to name, too! What we do have, however, are methods that need good names. I feel like the extension functions, styleWith(), showWhen() and centred() have good names, although perhaps styledWith() and shownWhen() - changing the tense, might be better.\nMaybe createButton() might be more descriptive as createBigButton() or createMainButton(), but with an application this small it hardly matters. If the application grew and more Button types were added, then you\u0026rsquo;d definitely need to change this name.\nThe same goes for the other methods. Perhaps buttonBox() is fine for now, but maybe menuBox() would be more descriptive if the application grows. The main method, createContent() will become build() if this is changed over to a framework, and this code is all moved into a Builder\u0026lt;Region\u0026gt;.\nChanging Layouts # This layout is now static, but it behaves dynamically in response to the value in isShowingNewGame. There no longer is any attempt to change the running layout.\nUnderstandable Layout # It\u0026rsquo;s 20 lines of code. It\u0026rsquo;s a BorderPane and you can see at a glance that only the top, center and bottom sections are used. You can see right away that the centre is a StackPane with two layouts that alternate in visibility. You can drill down into those two layouts and understand them easily.\nConclusion # It\u0026rsquo;s pretty easy to look at the final code and say, \u0026ldquo;Yeah, but that\u0026rsquo;s a pretty trivial example. It\u0026rsquo;s only 20 lines of layout code. You can\u0026rsquo;t do that with something complex.\u0026rdquo; But yet\u0026hellip;the original code was over 100 lines long, and was non-trivial enough that the author got stuck trying to figure out how to make it work.\nHopefully, you can also see how Kotlin makes life so much better with JavaFX.\nOne really big rule when writing layout code is NOT to reference Nodes from other Nodes, especially when those Nodes aren\u0026rsquo;t closely related in the layout. Especially, especially in the case where they are so unrelated in the layout that you need to instantiate one of them as a field, so that it\u0026rsquo;s global to the layout. Instead, create a Property as a field, or in your Model, and then reference that Property from the Nodes that need to interact (but now indirectly).\n","date":"30 April 2024","externalUrl":null,"permalink":"/javafx/techniques/codingstyle/beginner-mistakes-2/2024-04-15-more-beginner-mistakes/","section":"JavaFX","summary":"Introduction # After literally a decade writing JavaFX applications, it can be hard to remember how I thought things were supposed to be done when I started out. Thankfully, there seems to be a nearly endless supply of beginners having trouble, posting their code and giving us a glimpse into their thought processes.\n","title":"More Beginner Mistakes","type":"javafx"},{"content":" Introduction # It\u0026rsquo;s 2024, and people are starting to get the idea that you really need to use a build engine like Maven or Gradle with your Java projects. It will handle your dependencies, and do all the organization work that you need with your project.\nFiddling around downloading SDK\u0026rsquo;s or JDK\u0026rsquo;s and JavaFX jars and libraries and their JavaDocs is not something you should be spending any time at all on. It\u0026rsquo;s an absolute waste of time. Time that you could be using to write code. These are things that Gradle and Maven can do for you, and with virtually no effort at all on your part.\nHowever, I still regularly see people having issues with this error:\nError: JavaFX runtime components are missing, and are required to run this application\nWhat this error says is that the Java runtime needs some VM options set to tell it which JavaFX components to load. What this error means is that you\u0026rsquo;re still not using a build system that will take care of this minutia for you!\nLet\u0026rsquo;s look at how to get started using a build engine with a good IDE to get a project up and running with minimal effort.\nWhy Intellij IDEA? # Because it just works. It works without fuss or needing a lot of additional setup. Plus it works best with Kotlin, since Kotlin comes from Intellij too.\nI started out, more than a decade ago, using NetBeans and then we moved to Eclipse. Eclipse was okay, but back then it was still fairly glitchy and primitive. My team wasted way too much time dealing with Eclipse problems. After a few years, a new hire suggested that we try Intellij IDEA, so we did, and never looked back. Back when I had corporate overlords, the company paid for the \u0026ldquo;Pro\u0026rdquo; version license, but now I just use the \u0026ldquo;Community Edition\u0026rdquo; since I have to pay my own bills. There\u0026rsquo;s very little missing in CE. The \u0026ldquo;duplicate code\u0026rdquo; finding feature was nice in Pro, especially if you\u0026rsquo;re struggling to stick to DRY, and it was also nice to have the colours pop up in the CSS editor. But there\u0026rsquo;s nothing I cannot do without.\nYes, I\u0026rsquo;d rather use a open-source IDE if I could, and I\u0026rsquo;m a little disappointed in myself for sticking with a proprietary IDE. But nothing works as well as Intellij IDEA for me. A year or so ago, I looked into getting Eclipse to run Gradle for a project I was working on, and I was instantly transported back to when I was using Eclipse as a daily drive; spending way too much time setting up the tools and not enough time programming.\nI had one experience with VSCode, and that was editing this website. My editor of choice, Atom, was abandoned and it looked like VSCode was the best option to replace it. I found it to be nothing less than an annoying nightmare, and my production of new material dropped off to practically zero for months. Then I found Pulsar, a fork of Atom that was new and actively maintained and VSCode is out of my life. If you are using VSCode because you think it\u0026rsquo;s awesome, or because your school or company says you have to, then you have my pity. If you are using VSCode because you don\u0026rsquo;t know any better, then go and download Intellij IDEA CE today.\nWhy Gradle? # I started out with ANT, which does its job but is very mysterious. When it became time to move on the choice was Maven or Gradle. To me, Maven seemed a bit much like ANT in spirit, it\u0026rsquo;s run with a configuration file (the \u0026ldquo;POM\u0026rdquo; file) that\u0026rsquo;s something like XML. Gradle seemed to me to be the up and coming next generation of build systems, so why bother learning and using the older stuff like Maven? So Gradle it was.\nGradle has its haters, and at least some of the gripes about it are legitimate, in my opinion. What I like about it is that it\u0026rsquo;s configured via something that feels like code. I\u0026rsquo;m a programmer, and I like working with code. I don\u0026rsquo;t like working with XML or YAML or other file formats. So Gradle works for me.\nAnd I never learned much about Maven, since I really never use it. I can read a POM file and get the gist of what it\u0026rsquo;s doing, and see what libraries it loads, but that\u0026rsquo;s about it.\nFunctionally, you can probably do a few more things in Gradle than you can in Maven, but I\u0026rsquo;m sure that the Maven fans will say that\u0026rsquo;s rubbish.\nGradle and Intellij IDEA Together # This article is about using Gradle and Intellij together because that\u0026rsquo;s what I know, and that\u0026rsquo;s what I use, and that\u0026rsquo;s what I can tell you about. I think it can work for just about anybody. Both tools are free, and they work together really well.\nOne of the nice things about Gradle is that it does just about EVERYTHING for you. It will download a JDK for you, whatever version you want. It will download JavaFX for you, any version you want. It will download your libraries for you from Maven Central. It will run your tests. It will configure and run your application for you. It will build your jar files for you. It will even upload your library to Maven Central if you want.\nYou literally just tell it, \u0026ldquo;I want this, and I want that, and I want this version of this other thing\u0026rdquo;, and it sets it all up for you.\nAnd Intellij knows about Gradle and works with Gradle. It shares configuration information with Gradle and gives you options based on your Gradle setup.\nLet\u0026rsquo;s get started by using the Intellij \u0026ldquo;New Project Wizard\u0026rdquo;\u0026hellip;\nUsing the Wizard # Intellij has a pretty good wizard for starting a new project that will set up a Gradle project for you with a \u0026ldquo;Hello World\u0026rdquo; application. It has some issues, but we\u0026rsquo;ll look at those and how to fix them. The entire process shouldn\u0026rsquo;t take more than a few seconds to complete.\nYou can start up a project in a few seconds, and then test run it to make sure it works, and then you\u0026rsquo;re good to go. Let\u0026rsquo;s see how to do this:\nFrom the Menu # We\u0026rsquo;re going to start off with the \u0026ldquo;File\u0026rdquo; menu in Intellij Idea, select \u0026ldquo;New\u0026rdquo; and then \u0026ldquo;Project\u0026hellip;\u0026rdquo;:\nThis will bring up the wizard dialogue box, that we\u0026rsquo;ll use to create the project:\nThe main things here:\nProject Type\nWe\u0026rsquo;re going to be building a simple JavaFX application, so that\u0026rsquo;s what has been selected here. If you\u0026rsquo;re not doing JavaFX, then pick something else, but this article is particularly focused on JavaFX, so that\u0026rsquo;s what we\u0026rsquo;ll select.\nName\nPick a name for your project. You can start with upper or lowercase, it doesn\u0026rsquo;t matter.\nLocation\nGenerally I like to have all of my projects branching off some directory that makes sense. Not that what you specify here is the parent directory. Intellij will create a new directory for your project as indicated.\nApplication Language\nYour choice. I usually pick Kotlin, but you\u0026rsquo;ll probably want Java. Nobody will pick Groovy.\nBuild System\nClick on \u0026ldquo;Gradle\u0026rdquo;.\nGroup\nThis is the starting point for all the packages in your project. Something like \u0026ldquo;com.example\u0026rdquo;. This will be put together with \u0026ldquo;artifact\u0026rdquo; to create the base package for the application. In this case, it will be \u0026ldquo;ca.pragmaticcoding.demo1\u0026rdquo;.\nSelect your JDK\nMy screenshot utility won\u0026rsquo;t let me capture open combo boxes, unfortunately. Pick the JDK you want to write your application in.\nWhen you click \u0026ldquo;Next\u0026rdquo;, for a JavaFX project you\u0026rsquo;ll get this screen:\nThese are a bunch of libraries that are commonly used with JavaFX. I\u0026rsquo;ve picked Ikonli so that we can see what it will do.\nThen click on \u0026ldquo;Create\u0026rdquo; and you\u0026rsquo;ll get the dialogue about where to open your new project:\nWorking With the New Project # Whatever you pick, you\u0026rsquo;ll end up with a project screen that looks like this:\nThe red arrow is pointing to the most important thing on the screen right now, the Gradle menu icon. Go ahead and click on it:\nThis is the Gradle menu. I\u0026rsquo;ve expanded the \u0026ldquo;Tasks\u0026rdquo; submenu, so that you can see the options. Click on \u0026ldquo;build\u0026rdquo; to see the options in the \u0026ldquo;Build\u0026rdquo; submenu:\nYou can go ahead and double click on \u0026ldquo;build\u0026rdquo; to run the first build on this project. You should get something like this:\nUh oh! There\u0026rsquo;s a lot of red, but no build. What\u0026rsquo;s happening?\nUnsupported Gradle JVM\nYour build is currently cnfigured to use Java 21.0.2 and Gradle 8.2\nThis message just means that Gradle 8.2 doesn\u0026rsquo;t support Java 21. We can either upgrade Gradle, or change to Java 19 or earlier. Let\u0026rsquo;s change to Gradle 8.5, which does support Java 21.\nTo do this, we need to edit /gradle/wrapper/gradle-wrapper.properties. From this:\ndistributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\\://services.gradle.org/distributions/gradle-8.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists To this:\ndistributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists It\u0026rsquo;s just a one character change, from gradle-8.2-bin.zip to Gradle-8.5-bin.zip.\nOne valid gripe against Gradle is that it\u0026rsquo;s still an evolving tool, and that new versions often \u0026ldquo;break\u0026rdquo; older usages. Moving from Gradle 8.2 to 8.5 involves such changes, and there are things that you might see in examples with Gradle 8.2 that won\u0026rsquo;t run in Gradle 8.5. But for our straight-forward usage here, it\u0026rsquo;s not going to cause a problem.\nOnce you change gradle-wrapper.properties your going to see this symbol on your screen:\nThat\u0026rsquo;s the Gradle elephant logo with a \u0026ldquo;refresh\u0026rdquo; circle on it. This means that something has changed in your Gradle setup which requires a rebuild of the Gradle environment. In this case, it\u0026rsquo;s because we changed the properties file. You should click on that symbol to trigger the rebuild. Then you\u0026rsquo;ll get this:\nWith the progress bar indicating what\u0026rsquo;s going on. In this case, it needs to download the new Gradle version from the repository, and then load some stuff. You\u0026rsquo;ll need to wait for it to complete.\nNow we can try the build again. This time it will work, and we\u0026rsquo;ll get a /build directory in our project that will hold all the classes and other things generated during the build.\nAt this point, everything in good to go and we can try to run the program. The \u0026ldquo;run\u0026rdquo; option is on the \u0026ldquo;application\u0026rdquo; submenu in the Gradle \u0026ldquo;Tasks\u0026rdquo; menu.\nAnd when we double-click that, we get the application:\nAnd we\u0026rsquo;re there! We have a running application.\nOne last thing to note is that the integration between Intellij and Gradle works quite well, and the \u0026ldquo;Run\u0026rdquo; section on the main menu at the top of the screen always have the latest Gradle task that you ran. So you can close up the Gradle menu now, and just use the \u0026ldquo;Play\u0026rdquo; button to run the application from the Gradle \u0026ldquo;Tasks/application/run\u0026rdquo; option.\nTurning it into a Proper JavaFX Application # The biggest issue with this wizard is that it creates an FXML based application. We\u0026rsquo;ll need to get rid of that rubbish.\nRemoving the FXML # The code looks like this:\npublic class HelloApplication extends Application { @Override public void start(Stage stage) throws IOException { FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource(\u0026#34;hello-view.fxml\u0026#34;)); Scene scene = new Scene(fxmlLoader.load(), 320, 240); stage.setTitle(\u0026#34;Hello!\u0026#34;); stage.setScene(scene); stage.show(); } public static void main(String[] args) { launch(); } } and FXML Controller:\npublic class HelloController { @FXML private Label welcomeText; @FXML protected void onHelloButtonClick() { welcomeText.setText(\u0026#34;Welcome to JavaFX Application!\u0026#34;); } } And, of course, there\u0026rsquo;s an FXML file, but it has nothing of interest in it, and we can just delete it, along with HelloController.\nHere\u0026rsquo;s code that does exactly the same as the FXML version:\npublic class HelloApplication extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) { stage.setTitle(\u0026#34;Hello!\u0026#34;); stage.setScene(new Scene(createContent(), 320, 240)); stage.show(); } private Region createContent() { VBox results = new VBox(20); Label label = new Label(); Button button = new Button(\u0026#34;Hello!\u0026#34;); button.setOnAction(evt -\u0026gt; label.setText(\u0026#34;Welcome to JavaFX Application!\u0026#34;)); results.getChildren().addAll(label, button); results.setAlignment(Pos.CENTER); return results; } } That\u0026rsquo;s fine, if all you want is something as simple as a \u0026ldquo;Hello World\u0026rdquo; application. But if you want to do much more, you\u0026rsquo;ll need to add a framework\u0026hellip;\nUsing a Framework # You can use any framework you like, but for me MVCI (Model-View-Controller-Interactor) works best. It\u0026rsquo;s incredibly simple to set up and it\u0026rsquo;s nonintrusive even when the application barely calls for a framework.\nWith MVCI we have first, a Controller:\npackage ca.pragmaticcoding.demo1.hello; public class HelloController { private final ViewBuilder viewBuilder; private final Interactor interactor; public HelloController() { Model model = new Model(); interactor = new Interactor(model); viewBuilder = new ViewBuilder(model); interactor.initializeMessage(); } public Region getView() { return viewBuilder.build(); } } And then a Model which is strictly the \u0026ldquo;Presentation Data\u0026rdquo;. You can see this is just a POJO with a JavaFX Property as its field:\npackage ca.pragmaticcoding.demo1.hello; class Model { private final StringProperty message = new SimpleStringProperty(\u0026#34;\u0026#34;); public String getMessage() { return message.get(); } public void setMessage(String message) { this.message.set(message); } public StringProperty messageProperty() { return message; } } And an Interactor, which holds the application logic:\npackage ca.pragmaticcoding.demo1.hello; class Interactor { private final Model model; Interactor(Model model) { this.model = model; } void initializeMessage() { switch (new Random().nextInt(3)) { case 0: { model.setMessage(\u0026#34;Hello From JavaFX\u0026#34;); } case 1: { model.setMessage(\u0026#34;This is a message\u0026#34;); } case 2: { model.setMessage(\u0026#34;Wow! This works!\u0026#34;); } } } } Yes, this is pretty lame \u0026ldquo;application logic\u0026rdquo;, but it is enough for you to get the idea. The message that will be displayed is dependent on some kind of logic, even if it is just a random number.\nAnd a ViewBuilder, which, as its name implies, builds the View:\npackage ca.pragmaticcoding.demo1.hello; class ViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final Model model; ViewBuilder(Model model) { this.model = model; } @Override public Region build() { VBox results = new VBox(20); Label label = new Label(); label.textProperty().bind(model.messageProperty()); label.setVisible(false); Button button = new Button(\u0026#34;Hello!\u0026#34;); button.setOnAction(evt -\u0026gt; label.setVisible(true)); results.getChildren().addAll(label, button); results.setAlignment(Pos.CENTER); return results; } } You\u0026rsquo;ll notice that build() here is very much the same as createContent() from the original example. The only change is that the Label's Text Property is now bound to the Model Property and the Button now just makes the Label visible.\nFinally, we have a new version of our Application, which has been renamed to DemoApplication.\nWhy rename it?\nGenerally speaking, when you create a framework it is to perform a purpose. If you have an application with a whole mess of functions (or functional areas), then you\u0026rsquo;ll have a framework for each one. In this case, the framework is just saying \u0026ldquo;Hello\u0026rdquo; in some way. So that is the \u0026ldquo;Hello\u0026rdquo; framework.\nMy practice is to put all 4 components of a framework into their own package. Then all of the classes are package-private except for the Controller, which is public. The only public methods in the package are the Controller\u0026rsquo;s constructor, and the getView() method. All the other methods are package-private at most. This is going to include the constructors, and the methods in the Interactor that are called by the Controller.\nI made a point of leaving in the package line in all of the code samples above, even though I\u0026rsquo;ve stripped out the imports. You can see that they are all in ca.pragmaticcoding.demo1.hello.\nThe DemoApplication stays in ca.pragmaticcoding.demo1:\npackage ca.pragmaticcoding.demo1; public class DemoApplication extends Application { public static void main(String[] args) { launch(); } @Override public void start(Stage stage) { stage.setTitle(\u0026#34;Hello!\u0026#34;); stage.setScene(new Scene(new HelloController().getView(), 320, 240)); stage.show(); } } There\u0026rsquo;s not much in it now, which is the way it should be.\nNow, let\u0026rsquo;s run it\u0026hellip;\nOops! There\u0026rsquo;s an error:\nError: Could not find or load main class ca.pragmaticcoding.demo1.HelloApplication in module ca.pragmaticcoding.demo1\nFAILURE: Build failed with an exception.\nIt looks like it\u0026rsquo;s still looking for HelloApplication. Where is that configured?\nIt\u0026rsquo;s in a file called build.gradle. We\u0026rsquo;ll have a closer look at that in a bit, but for now we just need to change this section:\napplication { mainModule = \u0026#39;ca.pragmaticcoding.demo1\u0026#39; mainClass = \u0026#39;ca.pragmaticcoding.demo1.HelloApplication\u0026#39; } to:\napplication { mainModule = \u0026#39;ca.pragmaticcoding.demo1\u0026#39; mainClass = \u0026#39;ca.pragmaticcoding.demo1.DemoApplication\u0026#39; } Now we can try running it\u0026hellip;\nThis is after clicking the Button.\nOne Last Change # Very often, an application will have some sort of a window wrapper around it. Then the other frameworks will occupy a Region in that main window. Often it\u0026rsquo;s the whole window. But sometimes you\u0026rsquo;ll have a main menu for the application or a status bar at the bottom. That kind of thing.\nYou want to put that stuff in its own package for organization, along with the Application subclass.\nSo let\u0026rsquo;s do that, we\u0026rsquo;ll create a new package: ca.pragmaticcoding.demo1.main and we\u0026rsquo;ll put DemoApplication in it. Then we\u0026rsquo;ll change build.gradle to point to it:\napplication { mainModule = \u0026#39;ca.pragmaticcoding.demo1\u0026#39; mainClass = \u0026#39;ca.pragmaticcoding.demo1.main.DemoApplication\u0026#39; } Now we\u0026rsquo;ll run it.\nMore problems. This time Intellij brings up a file with errors, module-info.java:\nmodule ca.pragmaticcoding.demo1 { requires javafx.controls; requires javafx.fxml; requires org.kordamp.ikonli.javafx; opens ca.pragmaticcoding.demo1 to javafx.fxml; exports ca.pragmaticcoding.demo1; exports ca.pragmaticcoding.demo1.main; opens ca.pragmaticcoding.demo1.main to javafx.fxml; } And the error messages are:\n/home/dave/JavaProgramming/demo1/src/main/java/module-info.java:8: error: package is empty or does not exist: ca.pragmaticcoding.demo1 exports ca.pragmaticcoding.demo1;\n/home/dave/JavaProgramming/demo1/src/main/java/module-info.java:7: warning: package is empty or does not exist: ca.pragmaticcoding.demo1 opens ca.pragmaticcoding.demo1 to javafx.fxml;\nIt also complains about our module demo1 ending in a digit, but not as an actual error.\nWhat is module-info.java? Basically it\u0026rsquo;s a file that tells Java how to control access between various modules used in your application, including libraries. It\u0026rsquo;s worth a whole article in itself, but for now we\u0026rsquo;ll just concentrate on making it work.\nWhat\u0026rsquo;s surprising about this is that module-info.java even knows about ca.pragmaticcoding.demo1.main. This means that Intellij must have updated it for us automatically when we moved DemoApplication into it. It doesn\u0026rsquo;t do this for Kotlin code, so this is new to me.\nThe big problem is that ca.pragmaticcoding.demo1 doesn\u0026rsquo;t have any classes in it any more, now that we\u0026rsquo;ve moved DemoApplication out of it. This causes issue with module-info.java, and we\u0026rsquo;ll need to clean it out. Also, there\u0026rsquo;s still some junk left behind from when the application used FXML, so we can clear it out, too:\nmodule ca.pragmaticcoding.demo1 { requires javafx.controls; requires org.kordamp.ikonli.javafx; exports ca.pragmaticcoding.demo1.main; } Now it runs, but it still complains about the trailing \u0026ldquo;1\u0026rdquo; in \u0026ldquo;demo1\u0026rdquo;. We can leave that for another day.\nUnderstanding the build.gradle File # At this point, we\u0026rsquo;ve achieved any possible goals you might have for getting a quick start with Intellij, Gradle and JavaFX. We have a working application that compiles and runs, is organized into packages nicely and has the beginning of an MVCI framework implemented.\nGoing forward, you might need to add some libraries or change a few things about your project. You\u0026rsquo;re going to need to know just a little bit about that build.gradle file.\nIntroducing build.gradle # First, let\u0026rsquo;s take a look at the entire file:\nplugins { id \u0026#39;java\u0026#39; id \u0026#39;application\u0026#39; id \u0026#39;org.javamodularity.moduleplugin\u0026#39; version \u0026#39;1.8.12\u0026#39; id \u0026#39;org.openjfx.javafxplugin\u0026#39; version \u0026#39;0.0.13\u0026#39; id \u0026#39;org.beryx.jlink\u0026#39; version \u0026#39;2.25.0\u0026#39; } group \u0026#39;ca.pragmaticcoding\u0026#39; version \u0026#39;1.0-SNAPSHOT\u0026#39; repositories { mavenCentral() } ext { junitVersion = \u0026#39;5.10.0\u0026#39; } sourceCompatibility = \u0026#39;21\u0026#39; targetCompatibility = \u0026#39;21\u0026#39; tasks.withType(JavaCompile) { options.encoding = \u0026#39;UTF-8\u0026#39; } application { mainModule = \u0026#39;ca.pragmaticcoding.demo1\u0026#39; mainClass = \u0026#39;ca.pragmaticcoding.demo1.main.DemoApplication\u0026#39; } javafx { version = \u0026#39;21\u0026#39; modules = [\u0026#39;javafx.controls\u0026#39;, \u0026#39;javafx.fxml\u0026#39;] } dependencies { implementation(\u0026#39;org.kordamp.ikonli:ikonli-javafx:12.3.1\u0026#39;) testImplementation(\u0026#34;org.junit.jupiter:junit-jupiter-api:${junitVersion}\u0026#34;) testRuntimeOnly(\u0026#34;org.junit.jupiter:junit-jupiter-engine:${junitVersion}\u0026#34;) } test { useJUnitPlatform() } jlink { imageZip = project.file(\u0026#34;${buildDir}/distributions/app-${javafx.platform.classifier}.zip\u0026#34;) options = [\u0026#39;--strip-debug\u0026#39;, \u0026#39;--compress\u0026#39;, \u0026#39;2\u0026#39;, \u0026#39;--no-header-files\u0026#39;, \u0026#39;--no-man-pages\u0026#39;] launcher { name = \u0026#39;app\u0026#39; } } jlinkZip { group = \u0026#39;distribution\u0026#39; } This is written in a DSL (Domain Specific Language) based on Groovy. Groovy is a superset of Java, so technically all Java code is actually Groovy code as well. So, you could just write Java here, but I\u0026rsquo;ve never seen it done.\nMost people seem to be able to cope with build.gradle without really knowing anything about Groovy. You can too.\nThe best way to view this file is like a custom class file, but without the constructor. All the parts that look like sections are methods, and the others are like field declarations. That may not be technically correct, but it should help you cope with build.gradle.\nPlugins # The first section defines the \u0026ldquo;plugins\u0026rdquo;.\nplugins { id \u0026#39;java\u0026#39; id \u0026#39;application\u0026#39; id \u0026#39;org.javamodularity.moduleplugin\u0026#39; version \u0026#39;1.8.12\u0026#39; id \u0026#39;org.openjfx.javafxplugin\u0026#39; version \u0026#39;0.0.13\u0026#39; id \u0026#39;org.beryx.jlink\u0026#39; version \u0026#39;2.25.0\u0026#39; } What\u0026rsquo;s a \u0026ldquo;plug-in\u0026rdquo;? Basically it\u0026rsquo;s a module that gets included into your Gradle system to perform some function. You can see how this section is organized. We have 5 plug-ins here:\njava\nThis is the basic java compiler component. application\nJava code can be a library or something else, but if you need to be able to run as an application, this plug-in supplies that functionality. moduleplugin\nThis is the plug-in that handles the new modularized project stuff. javafxplugin\nThis is the plug-in that loads in the JavaFX library. jlink\nYou probably won\u0026rsquo;t need this unless you are looking to create a jar file, and this plug-in handles a lot of that. Really, the most important part about this is that JavaFX isn\u0026rsquo;t just a dependency, it\u0026rsquo;s configured and handled for you through a plug-in. This is why you don\u0026rsquo;t need those VM parameters to run a JavaFX application from Gradle.\nWhat you need to know about plug-ins is that each one (potentially) will bring along its own tasks and configuration methods, many of which you can access and customize in your build.gradle file. This is also where the menu items in the Gradle menu come from. Now you can see that the \u0026ldquo;run\u0026rdquo; option in the \u0026ldquo;application\u0026rdquo; sub-menu is a task of the \u0026ldquo;application\u0026rdquo; plug-in.\nGeneral Configuration # Let\u0026rsquo;s look at the next few sections:\ngroup \u0026#39;ca.pragmaticcoding\u0026#39; version \u0026#39;1.0-SNAPSHOT\u0026#39; repositories { mavenCentral() } ext { junitVersion = \u0026#39;5.10.0\u0026#39; } sourceCompatibility = \u0026#39;21\u0026#39; targetCompatibility = \u0026#39;21\u0026#39; The first two items group and version are going to control jar creation naming. The repository section tells Gradle where to go to find libraries listed in the dependencies section. You can add more repositories here if you need to. Even your own local repositories.\nThe ext section is interesting because this is where you can just create variables for your own use.\nThe next two items tell the compiler what versions of Java you want to have your application compile to.\nThe application Section # We\u0026rsquo;ve already looked at this a bit:\napplication { mainModule = \u0026#39;ca.pragmaticcoding.demo1\u0026#39; mainClass = \u0026#39;ca.pragmaticcoding.demo1.main.DemoApplication\u0026#39; } As you know, our application is modular, and this tells the application plug-in what the \u0026ldquo;main\u0026rdquo; module in our application is. Then it tells it where to find the main() method.\nThe javafx Section # Here\u0026rsquo;s where you specify which JavaFX version you want to use, and the different packages that you want - the equivalent to the VM options when you\u0026rsquo;re running directly from the IDE.\njavafx { version = \u0026#39;21\u0026#39; modules = [\u0026#39;javafx.controls\u0026#39;, \u0026#39;javafx.fxml\u0026#39;] } Dependencies # This is the piece that you are most likely to play around with:\ndependencies { implementation(\u0026#39;org.kordamp.ikonli:ikonli-javafx:12.3.1\u0026#39;) testImplementation(\u0026#34;org.junit.jupiter:junit-jupiter-api:${junitVersion}\u0026#34;) testRuntimeOnly(\u0026#34;org.junit.jupiter:junit-jupiter-engine:${junitVersion}\u0026#34;) } Gradle has the idea of \u0026ldquo;run configurations\u0026rdquo;. These are the contexts under which your code is going to run, and each context could require different libraries. There\u0026rsquo;s a lot to this subject, but if you are just running and testing an application you\u0026rsquo;re probably going to be dealing with implementation and testImplementation.\nGenerally, if you need a library to run and use your application, you\u0026rsquo;ll specify implementation. This will also have it available for testing purposes, too. If you just need it for testing, like a mocking library, you\u0026rsquo;ll need to use testImplementation or maybe testRuntimeOnly. The documentation for virtually any of these libraries will tell you which context to use.\nYou might remember that I specified \u0026ldquo;Ikonli\u0026rdquo; as an extra library to add to the project, way, way back in the wizard. This is how that shows up in the project.\nIf you are adding a library from Maven Central, their website will give you the correct syntax for a variety of build systems, Gradle and Maven included. You can just copy and paste from there:\nNothing could be easier.\nLast Gradle Tips # Here\u0026rsquo;s a few more pointers about Gradle to help you get started\u0026hellip;\nThe Settings File # There\u0026rsquo;s one last Gradle setup file that you should be aware of, and that\u0026rsquo;s a file in the root of your project called settings.gradle:\nrootProject.name = \u0026#34;demo1\u0026#34; Simple projects will just have the one line, like this. More complex projects which have \u0026ldquo;sub-projects\u0026rdquo; will have a bit more in here. However, if you do want to change the name of your project, you\u0026rsquo;ll have to remember to change it here.\nAdding A Test Environment # Intellij is aware of Gradle when adding new directories. If you go to your /src directory, bring up the menu and select New -\u0026gt; Directory you\u0026rsquo;ll get a dialogue box with some help:\nYou can see that it has options for the correct placement of the java source and the resources for the \u0026ldquo;test\u0026rdquo; configuration. It\u0026rsquo;s also aware if you are configured for a different language, like Kotlin or Groovy.\nYou can select both the java and the resources options and it will create both at once for you.\nRebuilds and Indexing # When you open up a Gradle based project, it usually has to do some preliminary setup stuff to get going. Before it\u0026rsquo;s done this, Intellij probably won\u0026rsquo;t recognize your code as Java and do any syntax highlighting or recognize included libraries. This means you may have to wait a few moments before you can begin working. You should see some activity listed in the status bar at the bottom of the screen.\nIf it doesn\u0026rsquo;t, you can manually kick it off, even if you don\u0026rsquo;t have the Gradle elephant logo floating on the screen. Open the Gradle menu and click on the \u0026ldquo;Reload All Gradle Projects\u0026rdquo; icon:\nThis will usually sort out any issues with your Gradle setup.\nUsing Clean # Gradle is pretty good about keeping track of which tasks and subtasks have already been completed, and which need to be rerun. For instance, if you run \u0026ldquo;application -\u0026gt; run\u0026rdquo; for the first time, it will do a whole bunch of steps, but if you run it again right away it will know that it doesn\u0026rsquo;t need to recompile and it will just skip most of the steps and go straight to running the application.\nThere are some circumstances where it can get lost, or maybe you\u0026rsquo;ve gummed it up by messing around behind the scenes. In that case, you can run \u0026ldquo;build -\u0026gt; clean\u0026rdquo; and it will erase everything in the /build directory, essentially wiping out any previously completed steps. Then the next time you run any kind of build, it will start over from scratch.\nFor the most part, you only need \u0026ldquo;build -\u0026gt; clean\u0026rdquo; when something goes wrong. You\u0026rsquo;ll drive yourself crazy waiting if you clean and build every time. You don\u0026rsquo;t need to do that.\nGradle DSL Language # As far as I can tell, Groovy is becoming a dead language. It hasn\u0026rsquo;t caught on.\nFor Gradle, the Kotlin based DSL is now the intended standard. However, the Intellij wizard (at least at the time I wrote this tutorial) still generates a Groovy DSL build.gradle file. It\u0026rsquo;s easy enough to change it over, but you probably won\u0026rsquo;t need to do that for simple projects.\nIt\u0026rsquo;s getting better, but much of the older documentation for some plug-ins still use Groovy examples. So using the Kotlin DSL can be a little challenging as you have to translate all of the example code. More and more of the documentation is now including both Kotlin and Groovy example code.\nIn the long-term, the Kotlin DSL will be the way to go.\nBootstraping a Gradle Project # Technically, all you need is a build.gradle and a setup.gradle file in a directory and Intellij can start up a project for you. Additionally, you might want to have a gradle.properties file in the Gradle wrapper directory if you want to control which version of Gradle you want to use. If you have source code and resources, put it in /src/main/... in the appropriate directories.\nAll you have to do is right click on the build.gradle file in your file manager, select \u0026ldquo;Open with\u0026hellip;\u0026rdquo; and pick Intellij IDEA and it will bootstrap the project and get it going.\nApparently, there\u0026rsquo;s an Intellij Gradle plug-in that adds some more functionality to this process, but I haven\u0026rsquo;t found any need for it so far.\nConclusion # The process listed here gets you past that first, critical, hurdle in starting a JavaFX project. Going from nothing to seeing \u0026ldquo;Hello World\u0026rdquo; on the screen. Now you can do some real coding.\nThere\u0026rsquo;s a lot of screenshots and reading here, but the process is ridiculously simple in practice, once you\u0026rsquo;ve done it a few times. You can go from nothing to a project with a framework in less than 5 minutes with some experience. The most important thing to remember is to get it running before you change any application code. After that, you can be sure that if it stops working it\u0026rsquo;s because you broke it.\nGradle is crazy powerful, and super complex when you get into it. And eventually, you\u0026rsquo;ll need to get into it. However, you can go a long way with just basic information.\nIf you want to know more, the documentation is here.\nThere\u0026rsquo;s also documentation for the Java Plugin and the Application Plugin.\n","date":"22 April 2024","externalUrl":null,"permalink":"/javafx/gradle-intellij/","section":"Programming","summary":"A quick guide get started with Gradle in the Intellij IDEA IDE.","title":"Getting Started with Gradle in Intellij Idea","type":"programming"},{"content":"I have about 35 years of experience building applications for a living.\n","date":"22 April 2024","externalUrl":null,"permalink":"/programming/","section":"Programming","summary":"I have about 35 years of experience building applications for a living.\n","title":"Programming","type":"programming"},{"content":" Introduction # I\u0026rsquo;m not a big fan of the way that the JavaFX JavaDocs are written, especially the introductory sections. My biggest complaint is that they are relatively opaque to a reader with little understanding of the specific topic. Quite often, I\u0026rsquo;ll go back once I have a fuller understanding of a concept and I\u0026rsquo;ll see that the introductory discussion actually directly addressed whatever issue I was having, just not in a way where I had any hope of picking up on it at the beginning.\nLet\u0026rsquo;s start by looking at the code that was included in the introductory discussion for PseudoClass:\npublic boolean isMagic() { return magic.get(); } public BooleanProperty magicProperty() { return magic; } public BooleanProperty magic = new BooleanPropertyBase(false) { @Override protected void invalidated() { pseudoClassStateChanged(MAGIC_PSEUDO_CLASS, get()); } @Override public Object getBean() { return MyControl.this; } @Override public String getName() { return \u0026#34;magic\u0026#34;; } } private static final PseudoClass MAGIC_PSEUDO_CLASS = PseudoClass.getPseudoClass(\u0026#34;xyzzy\u0026#34;); Right off the bat, this is probably the most complicated way that you could implement a new PseudoClass, and it\u0026rsquo;s written under the unspoken assumption that this code would be inside some custom Node class that implements the PseudoClass. The only way that you can determine that last assumption is because the call to pseudoClassStateChanged() has an implied this and that pseudoClassStateChanged() is a member method of Node. So this has to refer to Node which means that this anonymous inner class has to be defined inside a custom class that extends Node.\nBut, putting that aside, the really interesting thing here is this part:\n@Override protected void invalidated() { pseudoClassStateChanged(MAGIC_PSEUDO_CLASS, get()); } Woa! What\u0026rsquo;s this?\nWell, it\u0026rsquo;s in the JavaDocs:\nQuote The method invalidated() can be overridden to receive invalidation notifications. This is the preferred option in Objects defining the property, because it requires less memory. The default implementation is empty.\nThat doesn\u0026rsquo;t help much, to be honest. The memory stuff is confusing too.\nThe other thing to notice is that this is in BooleanPropertyBase. It\u0026rsquo;s also in IntegerPropertyBase, ObjectPropertyBase, and, I assume, all the ...PropertyBase classes. But what are these classes?\nTo understand this, let\u0026rsquo;s first look at SimpleBooleanProperty, the one we\u0026rsquo;re all familiar with\u0026hellip;\nSimpleBooleanProperty # Here\u0026rsquo;s the source code for SimpleBooleanProperty:\npublic class SimpleBooleanProperty extends BooleanPropertyBase { private static final Object DEFAULT_BEAN = null; private static final String DEFAULT_NAME = \u0026#34;\u0026#34;; private final Object bean; private final String name; public Object getBean() { return this.bean; } public String getName() { return this.name; } public SimpleBooleanProperty() { this(DEFAULT_BEAN, \u0026#34;\u0026#34;); } public SimpleBooleanProperty(boolean var1) { this(DEFAULT_BEAN, \u0026#34;\u0026#34;, var1); } public SimpleBooleanProperty(Object var1, String var2) { this.bean = var1; this.name = var2 == null ? \u0026#34;\u0026#34; : var2; } public SimpleBooleanProperty(Object var1, String var2, boolean var3) { super(var3); this.bean = var1; this.name = var2 == null ? \u0026#34;\u0026#34; : var2; } } Really what we have here is 4 different constructors, all designed to handle various cases of defining, or not defining, the Java Bean, the name and the initial value of the Property. Other than that, it doesn\u0026rsquo;t do much, and it delegates any initialization of the boolean value to its parent, BooleanPropertyBase.\nIf you look at the abstract BooleanPropertyBase class, you\u0026rsquo;ll find that it does 100% of the actual Property stuff you need, but none of the Java Bean handling at all. Those functions are left to whatever concrete class extends from it. So SimpleBooleanProperty only introduces enough code to support the Java Bean functions getName() and getBean() required by the interface ReadOnlyProperty that is implemented by BooleanProperty.\nI\u0026rsquo;ll admit that the Java Bean stuff in Properties mystifies me a little bit. I\u0026rsquo;ve always assumed that it was used for serialization and de-serialization, but I\u0026rsquo;ve yet to see a real case for serializing JavaFX properties. I\u0026rsquo;ve certainly never seen any project where the Java Bean values were actually used for JavaFX Properties.\nBut still, this is what SimpleBooleanProperty (and all the other \u0026ldquo;Simple\u0026hellip;Property\u0026rdquo; classes) add to BooleanPropertyBase, and therefore, BooleanProperty.\nAnd that means that there\u0026rsquo;s nothing wonderful or mysterious or essential about the Simple...Property classes at all. They just add some simple stuff to ...PropertyBase. There\u0026rsquo;s no inherent advantage to extending Simple...Property classes instead than ditching them and extending directly from ...PropertyBase - as the example from the PseudoClass JavaDocs does.\nCreating a Custom PseudoClassProperty # Let\u0026rsquo;s look at creating a custom Property to handle PseudoClass changes that we can bind to other Properties. We\u0026rsquo;ll build it as an extension of BooleanPropertyBase. It\u0026rsquo;s going to be a BooleanProperty because that\u0026rsquo;s what we need for PseudoClasses, they\u0026rsquo;re either off or on, true or false.\nWe\u0026rsquo;ll call our new class: PseudoClassProperty.\nThe only things that we are obligated to handle, because they are defined in the interfaces but not implemented in BooleanPropertyBase or any class it inherits from, are getBean() and getName(). Remember, we\u0026rsquo;re not obligated to use any structure or particular values under the hood, we just have to implement these two methods.\nIt turns out that there are two pieces of information we\u0026rsquo;ll need to supply when we instantiate our PseudoClassProperty: The Node to which it is associated, and the PseudoClass that it implements. It\u0026rsquo;s not a stretch to view the Node as a good candidate for the Bean, since it kind of \u0026ldquo;owns\u0026rdquo; the Property. The PseudoClass also has a name, which would work as the PseudoClassProperty name as well. Maybe, if the Node also has an id defined, we would want to combine it with the PseudoClass name to became the Property name.\nThat being said, I don\u0026rsquo;t actually see any practical use to calling either getBean() or getName() in the real world. So this exercise is a little bit like \u0026ldquo;going through the motions\u0026rdquo;, but at least we\u0026rsquo;ll have return values that are sensible.\nThis solves all of our constructor issues. We only need one kind of constructor since we always need to supply the Node and the PseudoClass, and we can let Kotlin handle a default value for our initial value of the Property, making it optional. In truth, any instance of PseudoClassProperty would almost inevitably be bound to some other Property right away, and the initialValue parameter could probably just be taken out of the constructor. But with Kolin, it doesn\u0026rsquo;t hurt to leave it in (Yeah! Kotlin!!!)\nThis gives us this:\nclass PseudoClassProperty(private val node: Node, private val pseudoClass: PseudoClass, initialValue: Boolean = false) : BooleanPropertyBase(initialValue) { override fun getBean() = node override fun getName(): String = pseudoClass.pseudoClassName override fun invalidated() { node.pseudoClassStateChanged(pseudoClass, value) } } This is geunuinely, generically useful.\nThere is virtually no way to implement a PseudoClass that\u0026rsquo;s going to be linked to a Property that isn\u0026rsquo;t going to execute node.pseudoClassStateChanged(pseudoClass, value) in some way. It\u0026rsquo;s total \u0026ldquo;boilerplate\u0026rdquo;, and code that you want out of your layout code.\nThere\u0026rsquo;s a little bit of added Kotlin trickery that can be used here to:\ninfix fun Node.addPseudoClass(pseudoClass: PseudoClass) = PseudoClassProperty(this, pseudoClass) What does this do? It adds a function to virtually every JavaFX component that connects a PseudoClassProperty to it. Now we can do this:\nprivate fun createSampleRegion() = HBox(10.0).apply { children += Label(\u0026#34;Some Text\u0026#34;).apply { addPseudoClass(somePseudoClass).bind(model.someBooleanProperty) } } The Action Property # The whole point of PseudoClassProperty is that it connects something that doesn\u0026rsquo;t know about Observables into something that does know about Observables through an \u0026ldquo;action\u0026rdquo;. In this case, it\u0026rsquo;s the PseudoClass state change that now responds to a state change in an ObservableBooleanValue. Generally speaking this is an \u0026ldquo;adaptor\u0026rdquo;.\nIt\u0026rsquo;s what I\u0026rsquo;m calling an \u0026ldquo;Action Property\u0026rdquo;.\nAnd it\u0026rsquo;s possible because of ...PropertyBase.invalidated(). But what is that?\n\u0026hellip;PropertyBase.invalidated() # Let\u0026rsquo;s look at the source code for BooleanPropertyBase. We find this:\nprivate void markInvalid() { if (this.valid) { this.valid = false; this.invalidated(); this.fireValueChangedEvent(); } } protected void invalidated() { } public void set(boolean var1) { if (!this.isBound()) { if (this.value != var1) { this.value = var1; this.markInvalid(); } } else { String var10002 = this.getBean() != null \u0026amp;\u0026amp; this.getName() != null ? this.getBean().getClass().getSimpleName() + \u0026#34;.\u0026#34; + this.getName() + \u0026#34; : \u0026#34; : \u0026#34;\u0026#34;; throw new RuntimeException(var10002 + \u0026#34;A bound value cannot be set.\u0026#34;); } } First off, we see that, just as described in the JavaDocs, invalidated() is empty. So we\u0026rsquo;ll never need to call super.invalidated().\nMore importantly: Since it\u0026rsquo;s empty, that means that it\u0026rsquo;s only reason for existance is to be overriden in extending classes! Keep that in mind.\nWe can also see that invalidated() is called by markInvalid(). Looking a little bit closer, we can see that markInvalid() is called by the public method set() when the value actually changes. There are a few other places that markInvalid() is called from which have to do with handling when it is bound to another Property. But you should be able to get the idea from set().\nBack in days long gone by, this is something that we would have called a \u0026ldquo;hook\u0026rdquo;. It\u0026rsquo;s an empty method that is called in certain circumstances with the expectation that you can, if you want, supply some code to run there in an extending class. Its sole purpose is to provide that facility.\nAdvantages of invalidated() Over addListener(InvalidationListener) # Let\u0026rsquo;s be fair. There\u0026rsquo;s nothing you can do with overriding invalidated() that you can\u0026rsquo;t do with addListener(). There are no other protected methods or fields all the way up the hierarchy that you could only access through invalidated(). So no special status is awarded to code running in invalidated().\nThe difference is boilerplate code in your layout.\nAnimations # Another place in JavaFX where you simple cannot avoid actions is Animations and Transitions. These need to be launched by imperative code, and there\u0026rsquo;s no way around it. However, you can use an Action Property to run a Transition. Then you have a bindable Property that will automatically run the Transition when whatever it is bound to changes.\nLet\u0026rsquo;s take a look at an example that runs a FadeTransition whenever the visibility of a Node is changed:\nclass FadeActionProperty(private val node: Node, private val duration: Duration) : BooleanPropertyBase() { override fun getBean() = node override fun getName() = \u0026#34;Fade Action Property\u0026#34; private val transition = FadeTransition(duration, node) override fun invalidated() { if (value) node.isVisible = true transition.interpolator = Interpolator.EASE_IN transition.toValue = if (value) 1.0 else 0.0 transition.setOnFinished { node.isVisible = value } transition.playFromStart() } } infix fun Node.addFade(duration: Duration): FadeActionProperty = FadeActionProperty(this, duration) fun \u0026lt;T : Node\u0026gt; T.bindFade(duration: Duration, boundTo: ObservableBooleanValue): T = apply { addFade(duration).bind(boundTo) } class ActionPropertyViewBuilder1() : Builder\u0026lt;Region\u0026gt; { private val nodeVisible: BooleanProperty = SimpleBooleanProperty(true) override fun build(): Region = VBox(10.0).apply { children += VBox( 10.0, Label(\u0026#34;Line 1\u0026#34;), Label(\u0026#34;Line 2\u0026#34;).bindFade(Duration.millis(700.0), nodeVisible), Label(\u0026#34;Line 3\u0026#34;), ) children += ToggleButton(\u0026#34;Visible\u0026#34;).apply { nodeVisible.bind(selectedProperty()) isSelected = true } minWidth = 300.0 } padWith 20.0 } class ActionPropertyApplication1 : Application() { override fun start(stage: Stage) { with(stage) { scene = Scene(ActionPropertyViewBuilder1().build()) show() } } } fun main() { Application.launch(ActionPropertyApplication1::class.java) } At the top of this we have our new Action Property, FadeActionProperty. Then we have a couple of convenience extension functions to make this easier to use in a layout. The FadeTransition will have a toValue of either 0.0 or 1.0 depending on whether we are fading in or out - determined by the new value of the FadeActionProperty.\nOur layout is just three Labels and a ToggleButton in a VBox. We instantiate our FadeActionProperty and attach it to the second Label, bound to another BooleanProperty called nodeVisible. Then nodeVisible is bound to the Selected Property of the ToggleButton. So now, when the ToggleButton is clicked, nodeVisible toggles along with it, and each toggle triggers the FadeTransition.\nOur FadeActionProperty needs to be associated with a Node, which gives us our Bean, and then it\u0026rsquo;s just been named \u0026ldquo;Fade Action Property\u0026rdquo; to satisfy the interface requirements. When the value is invalidated, we set up a FadeTransition to either fade in or fade out by setting FadeTransition.toValue and then we run it.\nBefore we start the transition, the Node has to be visible. There\u0026rsquo;s no point in fading otherwise. Then, when the transition ends, we set the Node's Visible Property to the correct value.\nOne Issue With this Approach # There\u0026rsquo;s one aspect of this implementation that I do not like, but unfortunately there\u0026rsquo;s no way around it that I can find.\nIt\u0026rsquo;s that the setup of the fade transition has to be done externally to the Node that\u0026rsquo;s affected. What this means is that you cannot just create a Node who\u0026rsquo;s fade behaviour is strictly an internal feature that\u0026rsquo;s not exposed to the outside world. Imagine that you wanted to create some layout component who\u0026rsquo;s behaviour was to fade in and fade out, that\u0026rsquo;s just the way that it would respond to a change in its Visible Property. In this example case, the layout would simply bind the Visible Property of the Label to the Selected Property of the ToggleButton and the FadeActionProperty would be bound to the VisibleProperty. The layout would have no idea that the fade Transition would happen, it would just be part of the Label's behaviour.\nThe problem is the first line in the invalidated() method in the FadeActionProperty:\nif (value) node.isVisible = true If the new value of the Property is false, then the Node disappears intstantly, so you have to force it back to true. This means that you\u0026rsquo;ll have to suppress acting on the subsequent invalidation of the FadeActionProperty, but that can be dealt with. The problem occurs if node.visibleProperty() is bound to another ObseverableValue, because that line of code will fail and generate an exception because a bound value cannot be set.\nAt a minimum, you would need to be able to unbind the Visible Property, force it to true, run the FadeTransition and then rebind it to whatever it was bound to before. And this is where you get stuck. You can see if the Property is bound:\npublic boolean isBound() { return this.observable != null; } So, the value it\u0026rsquo;s bound to is called observable but you cannot see observable itself:\nprivate ObservableBooleanValue observable = null; And there\u0026rsquo;s no getter for observable.\nThis means that you can\u0026rsquo;t do something like this:\nval boundTo = node.visibleProperty().observable node.visibleProperty.unbind() node.isVisible = true . . . node.visibleProperty().bind(boundTo) Another alternative might be to extend the Node class and then override Node.visibleProperty() so that you could insert your own proxy property to track the bindings. But Node.visibleProperty() is final. So you cannot do that either.\nOn the upside, while investigating this I did find that Node's Visible Property is actually an Action Property itself. Take a look:\nthis.visible = new StyleableBooleanProperty(true) { boolean oldValue = true; protected void invalidated() { if (this.oldValue != this.get()) { NodeHelper.markDirty(Node.this, DirtyBits.NODE_VISIBLE); NodeHelper.geomChanged(Node.this); Node.this.updateTreeVisible(false); if (Node.this.getParent() != null) { Node.this.getParent().childVisibilityChanged(Node.this); } this.oldValue = this.get(); } } public CssMetaData getCssMetaData() { return Node.StyleableProperties.VISIBILITY; } public Object getBean() { return Node.this; } public String getName() { return \u0026#34;visible\u0026#34;; } }; The invalidated() method does all the action work associated with removing the Node from the Scene.\nEncapsulation # Another use for this technique is to encapsulate some functionality with the Property itself, allowing the Property to become a complete functional unit without additional coding in your layout code.\nHere\u0026rsquo;s a very simple example where a property counts the number of times that it has been changed:\nclass EncapsulatingProperty(initialValue: Boolean) : BooleanPropertyBase(initialValue) { override fun getBean(): Any = Object() override fun getName() = \u0026#34;Encapsulating Property\u0026#34; var counter = 0 override fun invalidated() { println(\u0026#34;Running invalidated()\u0026#34;) counter++ } } fun main() { val testProperty = EncapsulatingProperty(true) println(\u0026#34;Counter: ${testProperty.counter} value: ${testProperty.value}\u0026#34;) testProperty.value = true println(\u0026#34;Counter: ${testProperty.counter} value: ${testProperty.value}\u0026#34;) testProperty.value = false println(\u0026#34;Counter: ${testProperty.counter} value: ${testProperty.value}\u0026#34;) testProperty.value = true println(\u0026#34;Counter: ${testProperty.counter} value: ${testProperty.value}\u0026#34;) testProperty.value = false println(\u0026#34;Counter: ${testProperty.counter} value: ${testProperty.value}\u0026#34;) } If you run this, you\u0026rsquo;ll get the following output:\nCounter: 0 value: true Counter: 0 value: true Running invalidated() Counter: 1 value: false Running invalidated() Counter: 2 value: true Running invalidated() Counter: 3 value: false This is a very trivial example, but it does show how you can use extension of ...PropertyBase to build custom Properties that do special things, and how invalidated() fits into that.\nConclusion # Action Properties can be used to bundle together changes in State and imperative code that needs to be triggered whenever those changes occur.\nObviously, there\u0026rsquo;s nothing that you can do with invalidated() that you cannot do with ObsevableValue.addListener() because there\u0026rsquo;s nothing special about the running environment of invalidated() that would give it some advantage. What it does do is to allow you to get irrelevant details out of your layout code.\nThis is a very important point. Layout code should, as much as possible, contain only code that\u0026rsquo;s specific to that particular layout. So any method that you can find that allows you to move generic and boilerplate code out of your layouts should be leveraged whenever you can.\nAnother important idea that you should get from this article is that there is nothing special about Simple...Property classes. The only functionality that they add is to implement two useless Java Bean methods that you\u0026rsquo;re never going to call anyway. You should feel free to ditch Simple...Property whenever you like and extend directly from ...PropertyBase to create custom Property classes that do exactly what you want, encapsulating the functionality that you want to keep out of your layout code.\n","date":"15 April 2024","externalUrl":null,"permalink":"/javafx/action-properties/","section":"JavaFX","summary":"There a little known, but very useful function call in most Properties.  This can be overridden to turn a Property into a class that transforms a value change into an action.","title":"Action Properties","type":"javafx"},{"content":" Introduction # A while back, there was a question posed on StackOverflow.com that asked about how to run parallel processing to speed up the rendering of a Mandelbrot set in a JavaFX application. Presumably, the core problem was the the rendering was taking too long and the OP was looking for a way to speed it up.\nThe complete code was posted in the question.\nIt turns out that the primary problem was probably fairly easy to fix by moving a single line of code, but it was also possible to improve the performance by introducing a Parallel Stream into the calculation.\nHowever\u0026hellip;\nBeginner Coding Problems # The original Java code was fairly typical of beginner JavaFX code. In other words, a bit of a mess.\nFirst, about that:\nPersonally, I don\u0026rsquo;t feel any great emotional attachment to any code I write, and I tend to view any critisism of my code as a commentary on the code or, at most, on my coding ability at the time that I wrote it. Coding is a skill, and as such we can all hope to improve with practice. Beginners, by definition, make beginner mistakes and it is to be expected that much of their code will be, \u0026ldquo;a bit of a mess\u0026rdquo;. None of the comments in this article are about the coder, but just about the code.\nWith that said, let\u0026rsquo;s look at the kind of problems you\u0026rsquo;ll find in beginners\u0026rsquo; code:\nViolations of \u0026ldquo;Don\u0026rsquo;t Repeat Yourself\u0026rdquo; (DRY) This is something that sneaks into JavaFX fairly easily if you don\u0026rsquo;t watch out. There\u0026rsquo;s lots of boilerplate in JavaFX, and you can find yourself doing the same thing over and over. Violations of the \u0026ldquo;Single Responsibility Principle\u0026rdquo; (SRP) Not so much a JavaFX issue, but something that beginners just don\u0026rsquo;t seem to follow. In this case, it may have help covered up the original problem. Not Using Proper Layouts JavaFX has a full set of useful layout classes to make it easy to design screen layouts that make sense. Beginners seem to like to ignore them and use AnchorPane everywhere and the set the X \u0026amp; Y offsets of all the Nodes inside them. I\u0026rsquo;m baffled by this. Not Handling Threading Properly Everyone \u0026ldquo;knows\u0026rdquo; that you don\u0026rsquo;t run long tasks on the FXAT, yet all beginners do this time and time again. Unaware of Useful Classes This is super common. Even experienced JavaFX programmers are constantly learning of useful classes that they never knew existed. In many cases, these classes make it simple to do things that would otherwise be complex or tedious. The JavaFX toolbox is huge, and it takes a while to learn your way around it. Bad Naming Once again, this is super common. Naming things is hard. At this point I still don\u0026rsquo;t understand why the original Java code calls the TextField that controls the maximum iterations, typeIter. The \u0026ldquo;Iter\u0026rdquo; part I get, but \u0026ldquo;type\u0026rdquo;? Kotlin # Nowadays, I really prefer to do as much as possible in Kotlin. It\u0026rsquo;s really a great mix with JavaFX, as it works \u0026ldquo;out of the box\u0026rdquo; and has lots of tools to handle the boilerplate that you can get bogged down in.\nFor this project, I took the Java code and let IntelliJ Idea convert it to Kotlin for me. This gives OK results, but the code does look more like Java code written in Kotlin than Kotlin code. So some work was needed to clean it up a little bit.\nThe final Kotlin code was about 300 lines, compared to the original Java code at nearly 500 lines. A lot of this is just Kotlin goodness, but there was also a lot of refactoring which reduced the code size considerably. In general, less code is better code, and I think that holds here.\nFeature Changes # During the course of this cleanup, some of the functionality of the application was changed:\nRemoval of the \u0026ldquo;Save\u0026rdquo; Function Even still as Java, I had problems with this feature as it wouldn\u0026rsquo;t compile and resolve a dependency related to SwingFXUtils. At first I just wanted to get it run so I could see it in action, and I didn\u0026rsquo;t care about saving, so I just commented it out. Later, I still didn\u0026rsquo;t care about saving so I removed it. Rendering as a Background Task Even at its fastest, the rendering could take a couple of seconds to complete. This meant that it had to be implemented on a background thread via Task. Handling Clicks and Keystrokes While Rendering It became clear that the user was very likely to click or press keys while the rendering was in progress. Not to mention resizing the window which generated multiple events that would trigger rendering. A mechanism was developed to store up the changes and then run them automatically when the rendering was complete, then triggering another rendering. Parallel Processing for Rendering Even with the initial problem corrected, it could still take up to 5 seconds to complete a rendering if the resolution and zoom were sufficiently high. Parallel streaming was implemented to improve the performance by a factor of about 4 times. Status Display Elements were added to the display to show the progress of the current render, number of clicks and keystrokes pending, and the current zoom level. Proper Handling of the Keystrokes This allows the TextField and Button that control the resolution to be on-screen at all times. The Code # The code for both the Java and Kotlin versions can be found here. I had to move it out because this article was just getting too big with it included.\nAlternatively, you can get the complete source code on GitHub, here.\nNotes on the Refactoring # General Notes # As seems normal when comments are used, the comments in the Java code were either really obvious or meaningless to me. When my reaction to a comment in code is \u0026ldquo;Duh?\u0026rdquo;, or \u0026ldquo;What?\u0026rdquo; I usually just delete them and never think about them again. Otherwise they just take up valuable screen real estate. There was one place where I would have liked to have seen a comment explaining why something was done, but, of course, there weren\u0026rsquo;t any there.\nI was surprised to see that the entire application was just one class. I\u0026rsquo;ve left the Kotlin code the same way because this is really an exercise in correcting issues, not in re-designing the application. This should help make it easier to compare the two versions. For this reason, I\u0026rsquo;ve left the structure of the class largely intact, which is why the menu code is way down at the bottom far away from all of the other layout code.\nOverall Structure # In the Java code, all of the layout was performed in Application.start(), with method calls to configure particular elements. Generally speaking, the start() method should be about establishing the Stage and the Scene, and everything should be delegated. In this case, the layout was moved to createContent(), leaving just 3 lines in the start() method. Really, this is just application of SRP, but it does add to the readability:\noverride fun start(stage: Stage) { stage.scene = Scene(createContent(), width, height) stage.title = \u0026#34;Mandelbrot Set\u0026#34; stage.show() } Having much more than this in your start() method is an anti-pattern.\nThe Layout # The Java code used Group as its base \u0026ldquo;layout\u0026rdquo; class!\nGroup group = new Group(canvas); Scene scene = new Scene(group, width, height); Group is basically a non-layout layout class. It puts the Nodes in it on the screen but, does nothing else to help position or configure them. I can count the number of times I\u0026rsquo;ve used Group over the past 10 years without even taking my mittens off (I\u0026rsquo;m in Canada). Group has its uses, but it shouldn\u0026rsquo;t be used this way.\nIn the Kotlin code, I used a BorderPane as the base layout. You can see how it works here:\nThe basic code looked like this:\nprivate fun createContent() = BorderPane().apply { top = mainMenuBar() center = canvas bottom = createBottom() . . . } The createBottom() function looks like this:\nprivate fun createBottom() = HBox(6.0).apply { children += listOf(Label(\u0026#34;Iterations: \u0026#34;), iterationsBox(), ProgressBar().apply { progressProperty().bind(setProgress) prefWidth = 200.0 }, Label(\u0026#34; Pending: \u0026#34;), Label().boundLabel(Bindings.createStringBinding({ pendingSets.value.toString() }, pendingSets)), Label(\u0026#34; Zoom:\u0026#34;), Label().boundLabel(Bindings.createStringBinding({ zoom.value.roundToInt().toString() }, zoom))) padding = Insets(6.0) } There\u0026rsquo;s now a lot more going on the bottom of the screen than there was before. We have a ProgressBar so that we can see how the rendering is going, an indicator for how many keystrokes \u0026amp; mouse clicks still need to be processed, and an indicator for how much we\u0026rsquo;ve zoomed in. There are two builders used, one for the TextField/Button combination, and one for the bound Labels.\nI was tempted to write a builder for the ProgressBar, but I thought it wasn\u0026rsquo;t too disruptive leaving it in-line, and I thought it was instructive to show how you can do this stuff in Kotlin.\nLet\u0026rsquo;s look at the builder for the iterations control:\nfun iterationsBox() = HBox(6.0).apply { val iterationsTextField = TextField() val textFormatter: TextFormatter\u0026lt;Int\u0026gt; = TextFormatter(IntegerStringConverter()) iterationsTextField.textFormatter = textFormatter val refreshButton = Button(\u0026#34;Refresh\u0026#34;).apply { onAction = EventHandler { canvas.requestFocus() mandelbrotSet { maximumIterations = textFormatter.value?.toDouble() ?: 50.0 } } defaultButtonProperty().bind(iterationsTextField.focusedProperty()) } children += listOf(iterationsTextField, refreshButton) } This uses TextFormatter with the standard JavaFX IntegerStringConverter to ensure that only whole numbers can be entered into the TextField, which is the right way to do this. Also note that all of the contents are local to the apply{} scope, and everything is self-contained, including the actions for the Button.\nNow, contrast it with the original Java code:\npublic void textFieldIter(Group group, TextField typeIter, Button button) { typeIter.setLayoutY(200); button.setLayoutX(92); button.setLayoutY(33); button.setPrefWidth(78); typeIter.setLayoutX(7); typeIter.setLayoutY(33); typeIter.setPrefWidth(78); onlyNumbers(typeIter); typeIter.setVisible(false); button.setVisible(false); group.getChildren().add(typeIter); group.getChildren().add(button); } public void onlyNumbers(TextField text) { text.textProperty().addListener((observable, oldNum, newNum) -\u0026gt; { if (!newNum.matches(\u0026#34;\\\\d*\u0026#34;)) text.setText(newNum.replaceAll(\u0026#34;[^\\\\d]\u0026#34;, \u0026#34;\u0026#34;)); }); } This doesn\u0026rsquo;t do anything with the action for the Button, either. That\u0026rsquo;s tucked away in the Menu code. Also note onlyNumbers(), which works, but isn\u0026rsquo;t the right way to go about this. The TextFormatter route also handles the conversion to Int as well. Imagine how much more code would be needed to add in a ProgressBar and 4 more Labels.\nThe Menus # The original Java code can only be described as a mess. It\u0026rsquo;s 150 lines of code with bad naming, repeated violations of DRY and an intense amount of coupling. Beginners seem to have particular difficulty with menus in JavaFX. On the upside, this code avoids using a dispatch event handler:\npublic void mainMenuBar(Stage stage, Group group, TextField typeHeight, TextField typeWidth, TextField typeIter, Button button1, Button button2) { MenuBar menubar = new MenuBar(); menu1(menubar, group); menu2(menubar); menu3(stage, menubar, typeHeight, typeWidth, typeIter, button1, button2); } public void menu1(MenuBar menubar, Group group) { Menu menu1 = new Menu(\u0026#34;Color\u0026#34;); menubar.getMenus().add(menu1); group.getChildren().add(menubar); menubar.setPrefWidth(176); CheckMenuItem m1i1 = new CheckMenuItem(\u0026#34;Light\u0026#34;); CheckMenuItem m1i2 = new CheckMenuItem(\u0026#34;Dark\u0026#34;); CheckMenuItem m1i3 = new CheckMenuItem(\u0026#34;Colorful\u0026#34;); CheckMenuItem m1i4 = new CheckMenuItem(\u0026#34;Solid White\u0026#34;); m1i1.setOnAction(e -\u0026gt; { if (m1i1.isSelected()) { colorLight(); m1i2.setSelected(false); m1i3.setSelected(false); m1i4.setSelected(false); } }); m1i2.setOnAction(e -\u0026gt; { if (m1i2.isSelected()) { colorDark(); m1i1.setSelected(false); m1i3.setSelected(false); m1i4.setSelected(false); } }); m1i3.setOnAction(e -\u0026gt; { if (m1i3.isSelected()) { colorHue(); m1i1.setSelected(false); m1i2.setSelected(false); m1i4.setSelected(false);As JavaFX code, this application had a lot of problems that are typical of programmers just starting out with } }); m1i4.setOnAction(e -\u0026gt; { if (m1i4.isSelected()) { colorWhite(); m1i1.setSelected(false); m1i2.setSelected(false); m1i3.setSelected(false); } }); m1i1.setAccelerator(KeyCombination.keyCombination(\u0026#34;1\u0026#34;)); m1i2.setAccelerator(KeyCombination.keyCombination(\u0026#34;2\u0026#34;)); m1i3.setAccelerator(KeyCombination.keyCombination(\u0026#34;3\u0026#34;)); m1i4.setAccelerator(KeyCombination.keyCombination(\u0026#34;4\u0026#34;)); menu1.getItems().addAll(m1i1, m1i2, m1i3, m1i4); } public void menu2(MenuBar menubar) { Menu menu2 = new Menu(\u0026#34;View\u0026#34;); menubar.getMenus().add(menu2); MenuItem m2i1 = new MenuItem(\u0026#34;Zoom in\u0026#34;); MenuItem m2i2 = new MenuItem(\u0026#34;Zoom out\u0026#34;); MenuItem m2i3 = new MenuItem(\u0026#34;Reset\u0026#34;); MenuItem m2i4 = new MenuItem(\u0026#34;Move Up\u0026#34;); MenuItem m2i5 = new MenuItem(\u0026#34;Move Down\u0026#34;); MenuItem m2i6 = new MenuItem(\u0026#34;Move Left\u0026#34;); MenuItem m2i7 = new MenuItem(\u0026#34;Move Right\u0026#34;); m2i1.setAccelerator(KeyCombination.keyCombination(\u0026#34;Plus\u0026#34;)); m2i2.setAccelerator(KeyCombination.keyCombination(\u0026#34;Minus\u0026#34;)); m2i3.setAccelerator(KeyCombination.keyCombination(\u0026#34;Space\u0026#34;)); m2i4.setAccelerator(KeyCombination.keyCombination(\u0026#34;UP\u0026#34;)); m2i5.setAccelerator(KeyCombination.keyCombination(\u0026#34;DOWN\u0026#34;)); m2i6.setAccelerator(KeyCombination.keyCombination(\u0026#34;LEFT\u0026#34;)); m2i7.setAccelerator(KeyCombination.keyCombination(\u0026#34;RIGHT\u0026#34;)); m2i1.setOnAction(t -\u0026gt; zoomIn()); m2i2.setOnAction(t -\u0026gt; zoomOut()); m2i3.setOnAction(t -\u0026gt; reset()); m2i4.setOnAction(t -\u0026gt; up(100)); m2i5.setOnAction(t -\u0026gt; down(100)); //menubar location m2i6.setOnAction(t -\u0026gt; left(100)); m2i7.setOnAction(t -\u0026gt; right(100)); menu2.getItems().addAll(m2i1, m2i2, m2i3, new SeparatorMenuItem(), m2i4, m2i5, m2i6, m2i7); } public void menu3(Stage stage, MenuBar menubar, TextField typeHeight, TextField typeWidth, TextField typeIter, Button button1, Button button2) { Menu menu3 = new Menu(\u0026#34;Image\u0026#34;); menubar.getMenus().add(menu3); MenuItem m3i1 = new MenuItem(\u0026#34;Set Iterations\u0026#34;); MenuItem m3i2 = new MenuItem(\u0026#34;Save Image\u0026#34;); menu3.getItems().addAll(m3i1, m3i2); m3i2.setOnAction(e -\u0026gt; { typeHeight.setVisible(true); typeWidth.setVisible(true); button1.setVisible(true); button2.setVisible(false); typeIter.setVisible(false); }); button1.setOnAction(e -\u0026gt; { //setting image resolution and saving canvas.widthProperty().unbind(); canvas.heightProperty().unbind(); canvas.setWidth(Integer.parseInt(typeHeight.getText())); canvas.setHeight(Integer.parseInt(typeWidth.getText())); MandelbrotSet(); saveImage(stage, actualImage); canvas.widthProperty().bind(stage.widthProperty()); canvas.heightProperty().bind(stage.heightProperty()); MandelbrotSet(); typeHeight.setVisible(false); typeWidth.setVisible(false); button1.setVisible(false); }); m3i1.setAccelerator(KeyCombination.keyCombination(\u0026#34;CTRL + C\u0026#34;)); m3i1.setOnAction(e -\u0026gt; { typeIter.setVisible(true); button2.setVisible(true); typeHeight.setVisible(false); typeWidth.setVisible(false); button1.setVisible(false); }); button2.setOnAction(e -\u0026gt; { typeIter.setVisible(false); button2.setVisible(false); maximumIterations = Double.parseDouble(typeIter.getText()); MandelbrotSet(); }); m3i2.setAccelerator(KeyCombination.keyCombination(\u0026#34;CTRL + V\u0026#34;)); } I have to say this is the only place where I would have liked to see some comments, because I really couldn\u0026rsquo;t fathom why menu3 existed. All it seems to do is to hide and display the TextFields and Buttons. And why are the Button actions defined here?\nIt turns out that having those elements on the screen all of the time \u0026ldquo;steals\u0026rdquo; the keystrokes from the Scene that are used to zoom in and out, and to move the image around. Hiding them makes the keystroke controls work. I didn\u0026rsquo;t see that this was the problem until I\u0026rsquo;d refactored a lot of this, and ditched menu3 in the process. We\u0026rsquo;ll look at the keystroke controls in a bit.\nA very big problem with this code is that all of the builder methods are void and they pass the containing class (MenuBar) down as a parameter. This introduces coupling. Also. the MenuItem names, like \u0026ldquo;m1i2\u0026rdquo;, are meaningless and couple back to the Menus. So if you removed a menu item, or added a new one, or changed the order, they\u0026rsquo;d all be misleading.\nFinally, CheckMenuItem is the wrong class to use. JavaFX has a class called RadioMenuItem that ensures that handles all of the deselection of items automatically.\nLet\u0026rsquo;s look at the Kotlin code:\nprivate fun mainMenuBar() = MenuBar().apply { prefWidth = 176.0 menus.addAll(menu1(), menu2()) } @Suppress(\u0026#34;UNCHECKED_CAST\u0026#34;) private fun menu1() = Menu(\u0026#34;Color\u0026#34;).also { ToggleGroup().apply { toggles += listOf(makeColourMenuItem(\u0026#34;Light\u0026#34;, \u0026#34;1\u0026#34;) { mandelbrotSet { newSettings(246.0, maximumIterations, 0.9, 60, 0, 60) } }, makeColourMenuItem(\u0026#34;Dark\u0026#34;, \u0026#34;2\u0026#34;) { mandelbrotSet { newSettings(0.0, 0.0, maximumIterations, 15, 15, 15) } }, makeColourMenuItem(\u0026#34;Colourful\u0026#34;, \u0026#34;3\u0026#34;) { mandelbrotSet { newSettings(300.0, 1.0, 1.0, 35, 0, 35) } }, makeColourMenuItem(\u0026#34;Solid White\u0026#34;, \u0026#34;4\u0026#34;) { mandelbrotSet { newSettings(0.0, 0.0, 1.0, 0, 0, 0) } }) it.items += toggles as List\u0026lt;MenuItem\u0026gt; } } private fun makeColourMenuItem(name: String, accel: String, func: () -\u0026gt; Unit) = RadioMenuItem(name).apply { selectedProperty().addListener { ob: Observable -\u0026gt; if ((ob as ObservableBooleanValue).value) func.invoke() } accelerator = KeyCombination.keyCombination(accel) } private fun menu2() = Menu(\u0026#34;View\u0026#34;).apply { items += listOf(makeMenuItem(\u0026#34;Zoom In\u0026#34;, \u0026#34;Plus\u0026#34;) { zoomIn() }, makeMenuItem(\u0026#34;Zoom Out\u0026#34;, \u0026#34;Minus\u0026#34;) { zoomOut() }, makeMenuItem(\u0026#34;Reset\u0026#34;, \u0026#34;Space\u0026#34;) { reset() }, makeMenuItem(\u0026#34;Move Up\u0026#34;, \u0026#34;UP\u0026#34;) { up(100) }, makeMenuItem(\u0026#34;Move Down\u0026#34;, \u0026#34;DOWN\u0026#34;) { down(100) }, makeMenuItem(\u0026#34;Move Left\u0026#34;, \u0026#34;LEFT\u0026#34;) { left(100) }, makeMenuItem(\u0026#34;Move Right\u0026#34;, \u0026#34;RIGHT\u0026#34;) { right(100) }) } private fun makeMenuItem(name: String, accel: String, eventHandler: EventHandler\u0026lt;ActionEvent\u0026gt;) = MenuItem(name).apply { accelerator = KeyCombination.keyCombination(accel) onAction = eventHandler } Here we only have 35 lines of code, and it\u0026rsquo;s a lot easier to read. Firstly, DRY has been followed by creating builder methods for the two kinds of MenuItems. Now we have no repeated code.\nThe RadioMenuItems need to be placed into a ToggleGroup so that they\u0026rsquo;ll automatically deselect.\nChanging Settings # It bears looking at the newSettings method. Originally, we had this:\npublic void colorLight() { hue = 246.0; saturation = maximumIterations; brightness = 0.9; R = 60; G = 0; B = 60; MandelbrotSet(); } public void colorDark() { hue = 0; saturation = 0; brightness = maximumIterations; R = 15; G = 15; B = 15; MandelbrotSet(); } public void colorHue() { hue = 300.0; saturation = 1.0; brightness = 1.0; R = 35; G = 0; B = 35; MandelbrotSet(); } public void colorWhite() { hue = 0.0; saturation = 0.0; brightness = 1.0; R = 0; G = 0; B = 0; MandelbrotSet(); } Is this a violation of DRY???\nIt\u0026rsquo;s not really duplicated code, is it? Some lines are duplicated in some of the methods, and the call to MandelbrotSet() is duplicated in each one.\nBut you can paramaterize the values, and have a single method that does the work:\nprivate fun colorLight() { mandelbrotSet { newSettings(246.0, maximumIterations, 0.9, 60, 0, 60) } } private fun colorDark() { mandelbrotSet { newSettings(0.0, 0.0, maximumIterations, 15, 15, 15) } } private fun colorHue() { mandelbrotSet { newSettings(300.0, 1.0, 1.0, 35, 0, 35) } } private fun colorWhite() { mandelbrotSet { newSettings(0.0, 0.0, 1.0, 0, 0, 0) } } private fun newSettings(newHue: Double, newSat: Double, newBright: Double, newRed: Int, newGreen: Int, newBlue: Int) { hue = newHue saturation = newSat brightness = newBright red = newRed green = newGreen blue = newBlue } This felt like an improvement.\nHowever, I now had 4 one-line methods that were each called from one place. Ordinarily, methods like that serve one purpose: The name of the method tells us what that single line of code does.\nIn this case, the methods were called from MenuItem builders that already had the MenuItem name in them. So it was clear what they did. So I refactored out these methods, and just put them in-line in the builder call for the MenuItems.\nChanging Settings # All over the application there was code like this:\npublic void zoomIn() { zoom /= 0.7; MandelbrotSet(); } Some sort of change to a global control variable, and then a call to MandelbrotSet() to re-render the image. This two-step approach, repeated all over the place just felt wrong.\nI changed the signature of MandelbrotSet() such that it accepted a Runnable (actually the Kotlin equivalent), and then MandelbrotSet() would be responsible for running it. So these calls then changed to things like:\nprivate fun zoomIn() { mandelbrotSet { zoom.value /= 0.4 } } In Kotlin, you can drop the \u0026ldquo;()\u0026rdquo; when the only parameter is a lambda.\nIn the case of zoomIn(), it was called from a couple of places, so it stayed as a function. A few others were refactored out. However, since the main purpose of this was to be passed around as a parameter, it made more sense to instantiate them as variables:\nprivate val zoomIn: () -\u0026gt; Unit = { mandelbrotSet { zoom.value /= 0.4 } } private val zoomOut: () -\u0026gt; Unit = { mandelbrotSet { zoom.value *= 0.4 } } private val up: (Int) -\u0026gt; Unit = { mandelbrotSet { yPos -= height / zoom.value * it } } private val down: (Int) -\u0026gt; Unit = { mandelbrotSet { yPos += height / zoom.value * it } } private val left: (Int) -\u0026gt; Unit = { mandelbrotSet { xPos -= width / zoom.value * it } } private val right: (Int) -\u0026gt; Unit = { mandelbrotSet { xPos += width / zoom.value * it } } private val reset: () -\u0026gt; Unit = { mandelbrotSet { zoom.value = 250.0 xPos = -470.0 yPos = 30.0 } } This is both more concise and more \u0026ldquo;Kotlin-like\u0026rdquo; in my opinion.\nWhile this gives more concise code, the big functional difference here is that MandelbrotSet() is now executing the parameter change control. This turns out to be a very important factor when moving the rendering to a background thread and handling multiple clicks and keypresses\u0026hellip;\nThe Rendering Routine # Let\u0026rsquo;s look at the core of the processing first:\nprivate fun iterationChecker(cr: Double, ci: Double): Int { var iterationsOfZ = 0 var zr = 0.0 var zi = 0.0 while (iterationsOfZ \u0026lt; maximumIterations \u0026amp;\u0026amp; ((zr.pow(2) + zi.pow(2)) \u0026lt; 4)) { val oldZr = zr zr = zr * zr - zi * zi + cr zi = 2 * (oldZr * zi) + ci iterationsOfZ++ } return iterationsOfZ } private fun performMandelbrot(progressConsumer: (workDone: Long, maxWork: Long) -\u0026gt; Unit) = WritableImage(canvas.width.toInt(), canvas.height.toInt()).apply { println(\u0026#34;Starting mandelbrot\u0026#34;) val centerY = width / 2.0 val centerX = height / 2.0 val counter = AtomicLong(0) (0 until width.roundToInt()).toList().parallelStream().forEach { x -\u0026gt; progressConsumer.invoke(counter.incrementAndGet(), width.roundToLong()) for (y: Int in 0 until height.roundToInt()) { val cr = xPos / width + (x - centerY) / zoom.value val ci = yPos / height + (y - centerX) / zoom.value pixelWriter.setColor(x, y, determineColour(iterationChecker(cr, ci))) } } println(\u0026#34;Done\u0026#34;) } private fun determineColour(iterations: Int): Color { if (iterations.toDouble() == maximumIterations) { //inside the set return Color.rgb(red, green, blue) } if (brightness == 0.9) { //white background return Color.hsb(hue, iterations / maximumIterations, brightness) } if (hue == 300.0) { //colorful background return Color.hsb(hue * iterations / maximumIterations, saturation, brightness) } if (hue == 0.0 \u0026amp;\u0026amp; saturation == 0.0 \u0026amp;\u0026amp; brightness == 1.0) { return Color.hsb(hue, saturation, brightness) } return Color.hsb(hue, saturation, iterations / brightness) } Really, only a few things have been changed here.\nThe name was changed MandelbrotSet() has been changed to performMandelBrot(). The mandelbrotSet() now controls the Task setup. This method now returns a WritableImage. Colour determination was moved to determinColour The Intellij Idea conversion to Kotlin did a horrible job on the loops, converting the for() loops into manual loops. This made the whole thing kind of confused. It helped to move this code out of the loop, and helped with DRY. Parallel Processing was introduced The for() loop for x was replaced with a parallelStream() over a list of x values. The new mandelbrotSet() method looks like this:\n{: data-executable=\u0026ldquo;true\u0026rdquo; data-theme=\u0026ldquo;darcula\u0026rdquo;}\nprivate var taskRunning = false private val preProcesses = mutableListOf\u0026lt;() -\u0026gt; Unit\u0026gt;() private val pendingSets: IntegerProperty = SimpleIntegerProperty(0) private fun mandelbrotSet(preProcessor: () -\u0026gt; Unit = {}) { if ((canvas.width \u0026gt; 0) \u0026amp;\u0026amp; (canvas.height \u0026gt; 0)) { preProcesses += preProcessor pendingSets.value = preProcesses.size if (!taskRunning) { taskRunning = true preProcesses.forEach { it.invoke() } preProcesses.clear() pendingSets.value = 0 val task = object : Task\u0026lt;WritableImage\u0026gt;() { override fun call(): WritableImage = performMandelbrot { workDone, maxWork -\u0026gt; updateProgress(workDone, maxWork) } } task.setOnSucceeded { canvas.graphicsContext2D.drawImage(task.value, 0.0, 0.0) taskRunning = false if (preProcesses.isNotEmpty()) { mandelbrotSet() } } setProgress.unbind() setProgress.bind(task.progressProperty()) Thread(task).start() } } } This method is responsible for controlling what runs on the FXAT, and what runs on a background thread. Everything in here except for:\noverride fun call(): WritableImage = performMandelbrot { workDone, maxWork -\u0026gt; updateProgress(workDone, maxWork) } runs in the FXAT.\nIt turns out that weird things happen if the global parameters change while the rendering is running, so this has to be defended against. Also, we don\u0026rsquo;t want multiple Tasks running at the same time. It\u0026rsquo;s also true that we can run several parameter changes before running the rendering engine again. This means that we can save up the changes, run them in a batch and the run the renderer. And that\u0026rsquo;s what we do.\nThe flow is straight-forward. Add the new parameter change into the batch of changes pending, and then check to see if a Task is running. If it is, then you\u0026rsquo;re done.\nIf no Task is running, then stream through the pending parameter changes, running each one and then clear the list. Launch a Task to run the renderer.\nWhen the Task is complete, then push the WritableImage from the Task onto the canvas. If any new parameter changes have come in while the Task was running, then the change list will be non-empty. In this case, just call the routine again.\nRemember that the FXAT is single-threaded, and any actions from clicks and keystrokes are going to be put into the event queue and won\u0026rsquo;t be handled until after any other jobs are run on the FXAT. So it\u0026rsquo;s impossible for the pending change list to change list to change while any of this code is running on the FXAT. This means that this approach is pretty much fool-proof.\nAll of this works because of the earlier refactoring that passed responsibility for the parameter changes to mandelbrotSet().\nAbout the Stolen Keystrokes\u0026hellip; # The reason for that has been covered in my article about Events. In a nutshell, in any given scene something is going to have \u0026ldquo;focus\u0026rdquo;. It will end up being the \u0026ldquo;source\u0026rdquo; for keystroke Events. However, all Events get passed through a chain of Nodes starting at the Scene, and then working down through the various layout containers that hold the source Node. Every one of those layout containers gets a first chance to handle the Event before it\u0026rsquo;s passed on to the next level down. And they all have a chance to stop the Event from being passed down. This is called, \u0026ldquo;consuming\u0026rdquo; the Event.\nThis process of going down the chain is called the \u0026ldquo;Event Capturing Phase\u0026rdquo;, and controlled by installing EventFilters on the container Nodes.\nFor this case, we add the following EventFilter to the main BorderPane.\naddEventFilter(KeyEvent.KEY_PRESSED) { if (processKeystroke(it.code, it.isShiftDown)) { it.consume() } } And here\u0026rsquo;s the code for processKeystroke():\nprivate fun processKeystroke(keyCode: KeyCode, isShiftDown: Boolean): Boolean { var didSomething = true when (keyCode) { KeyCode.W, KeyCode.UP -\u0026gt; up(if (isShiftDown) 10 else 100) KeyCode.A, KeyCode.LEFT -\u0026gt; left(if (isShiftDown) 10 else 100) KeyCode.S, KeyCode.DOWN -\u0026gt; down(if (isShiftDown) 10 else 100) KeyCode.D, KeyCode.RIGHT -\u0026gt; right(if (isShiftDown) 10 else 100) KeyCode.EQUALS -\u0026gt; zoomIn() KeyCode.MINUS -\u0026gt; zoomOut() KeyCode.SPACE -\u0026gt; reset() KeyCode.ESCAPE -\u0026gt; Platform.exit() else -\u0026gt; { didSomething = false } } return didSomething } Since processKeystroke() only recognizes certain keystrokes, it returns a Boolean to indicate whether or not it did anything. Then we use that return value to decide if we should consume the Event. For the most part, the navigation keystrokes aren\u0026rsquo;t really used inside a TextField, so the two uses of keystrokes are fairly mutually exclusive. This approach works good enough.\nConclusion # Beginners seem to be taught only how to create code that runs, with little consideration for the quality of the code in respect to readability and maintance. But these are critical factors, even when the application is still in development. This project is a case in point, the actual problem was made much more difficult to solve because the entire code was much more complicated than it needed to be.\nI don\u0026rsquo;t think you need to read through all the code in detail and understand what every lines does in both versions to appreciate the process of cleaning up the code and making it easier to deal with. I\u0026rsquo;ve tried to explain the reasoning behind the changes as much as I can, so that you can decide if you agree with it or not.\n","date":"7 April 2024","externalUrl":null,"permalink":"/javafx/techniques/codingstyle/beginner-mistakes-1/","section":"JavaFX","summary":"Taking a look at a JavaFX application that suffers from some very common beginner mistakes, converting it to Kotlin and sorting out the issues.","title":"Common JavaFX Mistakes","type":"javafx"},{"content":"","date":"6 April 2024","externalUrl":null,"permalink":"/javafx/projects/","section":"JavaFX","summary":"Complete projects that show how to build real applications with Reactive JavaFX","title":"Example Projects","type":"javafx"},{"content":" Introduction # This was originally posed as a question on Reddit about writing vast amounts of Text onto a Canvas, because \u0026ldquo;TableView cannot handle the huge amounts of data to be displayed\u0026rdquo;. Of course, trying to have giant Canvas in a ScrollPane with thousands of lines of data is absolutely not the way to go. On the other hand TableView is exactly designed to handle large data sets while minimizing the use of screen resources.\nOnce this issue was sorted out, the OP continued on with TableView, but it seemed to me that this was better done as a ListView, and I wondered what it would take to do it. So, here it is.\nSource Code # The source code for this project can be found here\nFeatures # There\u0026rsquo;s a set of CheckBoxes at the bottom of the screen that control the display of the various formats of the data; octal, binary, decimal and character. Each ListCell has an \u0026ldquo;Edit\u0026rdquo; Button which controls the display of the bottom row of the ListCell which has a TextField and a \u0026ldquo;Commit\u0026rdquo; Button.\nThe display is broken up into 16 byte ListCells and each byte of the 16 is displayed in a VBox in the various formats.\nAll of the actual editing is done through the TextField in the bottom of the ListCell. When editing is happening and the TextField has focus, the hexadecimal display of byte closest to that of the caret in the TextField is highlighted. This isn\u0026rsquo;t perfect, but early testing revealed that without this indicator it was a little difficult to determine where the caret location corresponded to the byte by byte display.\nProgramming Notes # Here\u0026rsquo;s an overview of how the application is designed, with a few particulars highlighted\u0026hellip;\nFramework # Even though this application simply generates a few thousand bytes of random data, it has been designed with an Model-View-Controller-Interactor framework. There\u0026rsquo;s no file handling in this example, so there\u0026rsquo;s no Service level, and the Interactor generates the dummy data.\nEven given the simplicity of this, you can still see how the MVCI framework works.\nThe Data Model # The data model for the application is composed of a single ObservableList\u0026lt;HexRow\u0026gt;. HexRow itself is composed of an Integer to hold the file offset of the first byte in the row, and a ByteArray to hold the actual data.\nNote that neither of these to elements is an Observable type of value. There is some overhead to Observable data types, as they have to track Listeners and bound values and invalidation and so on. Generally this isn\u0026rsquo;t an issue, but if there are millions of bytes of data, each wrapped in an Observable class, it might get out of hand.\nWhat do you lose when the HexRow elements are not Observable? Not a lot for this kind of application. The ListCells are all autonomous, and the Observable elements are all in the local data model inside the ListCell. This is updated via updateItem when the ListCell is recycled, and the code in the \u0026ldquo;Commit\u0026rdquo; Button updates item directly.\nWhat is lost is the ability to have the ListCells all automatically redisplay if bytes are added or removed from any row. However, handling this case is going to take a fairly heavy amount of processing regardless, as each HexRow will need to be updated, taking data from one neighbour or another in turn down to the end of the file. In which case the whole list can probably be invalidated, which should cause the ListCells to reload.\nByteArray is interesting itself. It seems to be a Kotlin only data type (yeah Kotlin!) and has some nifty methods to convert to a String in hexadecimal format. It made most of the coding pretty easy.\nData Conversions # All of the data conversions are done off the hexadecimal String representation of the ByteArray held in the StringProperty encapsulated in ObservableValue.map() functions. This means that these values stay in lock-step as the StringProperty is edited, or the value in the StringProperty is updated via updateItem().\nThe primary method is called extractHex which pulls two digits from the String based on an index. If the String isn\u0026rsquo;t long enough, it returns null, so this method returns a String?. The subsequent conversions need to deal with this nullability. Kotlin has a nifty function, String.hexToInt() that takes the String representation of a hexadecimal number and converts it into an Integer, which can then by converted in a different base to display octal or binary through Int.toString(radix).\nprivate fun extractHex(bigString: String, index: Int): String? = if (bigString.length \u0026gt; ((index * 2) + 1)) bigString.substring(index * 2, (index * 2) + 2) else null @OptIn(ExperimentalStdlibApi::class) private fun toRadix(hexString: String, radix: Int) = hexString.hexToInt().toString(radix) private fun extractRadix(bigString: String, index: Int, radix: Int) = extractHex(bigString, index)?.let { toRadix(it, radix) } ?: \u0026#34;\u0026#34; @OptIn(ExperimentalStdlibApi::class) private fun extractChar(bigString: String, index: Int) = extractHex(bigString, index)?.hexToInt()?.toChar()?.toString() ?: \u0026#34;\u0026#34; You can see that the String.hexToInt() function is still experimental, but it seems to work well.\nControlling the Rows # There\u0026rsquo;s a data structure called HexCellControlModel. This is composed of a set of BooleanProperties. The main View instantiates this Model and then passes it to the HexEditorCells in their constructors. This way, each HexEditorCell will have a reference to the single Model instance. Then the CheckBoxes at the bottom of the GUI have their Selected Property bound to one of these BooleanProperties in HexCellControlModel.\nInside the HexEditorCell the Labels that display the values have their Visible and Managed Properties bound to one of the BooleanProperties in HexCellControlModel.\nIn this way, all the ListCells respond in unison to each of the CheckBoxes.\nThe Heading Row # The Labels across the top of the ListView are actually not part of the ListView itself. The entire View is a BorderPane with the centre occupied with just the ListView. The bottom has an HBox with the CheckBoxes in it, and the top has an HBox with the heading Labels in it.\nAll of the VBoxes that make up the main part of the HexEditorCell have a fixed minimum width, and the Labels in the heading row follow along with that.\nThere\u0026rsquo;s one quirk, the binary display takes significantly more horizontal space than the other modes, so when it is visible the minimum width has to increase to hold this. This is achieved by using ObservableValue.map() to convert the BooleanProperty into a ObservableDoubleValue that could then be bound to the MinWidth Property of the Labels.\nEditing # Originally, I pondered the idea of having context menus on the Labels for the hex values, and that\u0026rsquo;s still something that could be added. Instead, I reasoned that the byte by byte display was good for seeing and understanding the data, but it was probably just easiest to edit it as a String in a TextField.\nTo make it a bit easier to follow how the editing in the TextField related to the byte by byte display, a PseudoClass was added to the hex Labels to indicate that they corresponded to the values near the caret in the TextField. This PseudoClass is styled to put a border around the Label and change the background to a light blue. The border needed to be given insets of \u0026ldquo;-2\u0026rdquo; on the top and bottom to avoid having the Label grow vertically when the borders were added and displacing the contents of that column downwards.\nThe TextField as supplied in this example doesn\u0026rsquo;t have a Filter or TextFormatter to prevent non-hex digits from being added. This is something that would be needed to put this into production.\nConclusion # Virtually all of the cool stuff is inside the HexEditorCell, as expected. My original advice to the OP was:\nThink if you just had 16 bytes of data to edit on a screen. Not millions of bytes, just 16 and a whole screen to put a layout on.\nWould you carve it up into 16 independent cells? Probably not.\nWhat I would probably do is create an HBox with 16 VBoxes inside. Each VBox would hold a Label, one each for hex, octal, binary, decimal and character.\nAnd that\u0026rsquo;s exacly the process that I took here. Think of how you\u0026rsquo;d want to see or edit just a handful of data, and design a layout to serve that purpose. Then put it into a ListCell and let the ListView virtualization do the work of loading the data into it.\n","date":"6 April 2024","externalUrl":null,"permalink":"/examples/hex-editor/","section":"JavaFX","summary":"Here’s a sample application that uses a ListView to display and edit bytes in a data file, showing the values in hex, octal, binary, decimal and as characters.","title":"Example: Hexadecimal Editor","type":"javafx"},{"content":" Introduction # This tutorial was inspired by this question (and the answer) on StackOverFlow. There was a lot of back and forth about the OP\u0026rsquo;s strange ideas about how ListView and TableView work, but the answer posted by Sai Dandem was very good and forms the basis for what we\u0026rsquo;re going to look at in this article.\nThe Initial Problem # Let\u0026rsquo;s take a look at a screen shot that shows the issues clearly:\nThe most obvious problem (there are others) is that the border of the Region runs right through the Label at the top. That was the subject of the StackOverflow question; how to find a way such that the border at least appeared to stop before it went through the Label.\nIt\u0026rsquo;s not totally relevant, but here\u0026rsquo;s the code I used to generate this:\nclass LabelBoxApplication : Application() { override fun start(stage: Stage) { val scene = Scene(createContent(), 320.0, 240.0).apply { addStyleSheet(\u0026#34;/css/LabelBox.css\u0026#34;) addWidgetStyles() } stage.title = \u0026#34;LabelBox Demo\u0026#34; stage.scene = scene stage.show() } fun createContent() = BorderPane().apply{ center = VBox(Label(\u0026#34;This is the Title\u0026#34;).apply{ styleClass += \u0026#34;box-label\u0026#34; }).apply{ styleClass += \u0026#34;label-box\u0026#34; padding = Insets(-10.0, 0.0, 0.0, 8.0) } padWith(20.0) } } fun main() { Application.launch(LabelBoxApplication::class.java) } The main thing to note about this approach is that a negative Inset is used to move the Label up and into the area occupied by the border.\nA First Solution # This was the stylesheet that was used in this example:\n.label-box { -fx-border-width: 2px; -fx-border-color: black; -fx-border-radius: 6px; } .box-label { -fx-font-size: 16px; } We can make one small change to it, adding a background colour to the Label, which by default has a transparent background.\n.label-box { -fx-border-width: 2px; -fx-border-color: black; -fx-border-radius: 6px; } .box-label { -fx-font-size: 16px; -fx-background-color: white; } Now we get this:\nWhich is all very good until we change the background color of the BorderPane to some other colour:\nThere is one last trick we can try, which is to change the -fx-background-color of the Labal to \u0026ldquo;inherit\u0026rdquo;, but that yields this:\nWhich doesn\u0026rsquo;t work because the Label inherits from the VBox which has a transparent background. However, if we set the VBox to something other than transparent, like \u0026ldquo;inherit\u0026rdquo;\u0026hellip;\n.label-box { -fx-border-width: 2px; -fx-border-color: black; -fx-border-radius: 6px; -fx-background-color: inherit; } .box-label { -fx-font-size: 16px; -fx-background-color: inherit; } .wrapper-region { -fx-background-color: blue; } Then it will work again:\nComplex Backgrounds # If all you\u0026rsquo;re going to use this LabelBox with is simple backgrounds, then you\u0026rsquo;re done. This will work fine, and you need not do any more work.\nHowever\u0026hellip;\nIf you are going to use this with any kind of complex background, like a gradient or an image background, then you\u0026rsquo;ll have problems. Here\u0026rsquo;s what it looks like with a gradient:\nThe problem is that \u0026ldquo;inherit\u0026rdquo; causes the background to repeat from scratch in each region, which is not what we want.\nMaking a Section of the Border Transparent # It\u0026rsquo;s clear that obscuring the border by making the Label region non-transparent is not the answer to the problem. We need to make the border transparent in the same region that we have the Label.\nRefining the Layout # Before we get to the transparent border section, let\u0026rsquo;s take another look at our layout. Initially, it looked like this, restyled to show the regions better:\nThe green section is entire Pane for our LabelBox and the blue area is the content. Notice how the Label pokes out over the top of the Pane? That\u0026rsquo;s something we need to think about. We need to decide if we\u0026rsquo;re OK having some of our Pane's stuff existing outside its boundaries.\nIt can be a problem. Take a look at this:\nHere we see the LabelBox duplicated in a VBox. You can see how the Label on the lower LabelBox intrudes into the space occupied by the upper one. This is something that you can program around, but it\u0026rsquo;s probably better not to. It just makes the LabelBox trickier to deal with. So we\u0026rsquo;ll rearrange the layout to solve this:\nHere, the border has been added back in so that you can see how it now has to come down from the top in order to meet with the middle of the Label text. The Label is now completely contained within the boundaries of the Pane. You can stack them, and it will still look good without requiring any gap:\nUsing Clipping to Hide Some of the Border # JavaFX supports the idea of a \u0026ldquo;clipping\u0026rdquo; region, which defines the visual area in which an element will appear on the screen. In this case, we just want the border to be affected, and we need to define a shape that includes just the area that the Label will occupy. We want something like this:\nThe translucent grey box is the area that we want to restrict the border to show. We can create this shape by creating two Rectangles, one that covers then entire area, and the other that is just a little bigger than the Label, and in the same location as the Label. Then we call Shape.subtract() to remove the smaller Rectangle from the larger one.\nThere\u0026rsquo;s just one problem with this\u0026hellip;\nWe need to take in consideration that it\u0026rsquo;s just the border that we want to clip. This means that we cannot just add a border to the Pane and clip the Pane because it will also clip out the background of the Pane if it\u0026rsquo;s not transparent. So we\u0026rsquo;ll need to create a separate Pane that has nothing but a border, and then apply the clipping to this new Pane.\nLet\u0026rsquo;s look at the code:\nclass LabelBox2Application : Application() { override fun start(stage: Stage) { val scene = Scene(createContent(), 320.0, 240.0).apply { addStyleSheet(\u0026#34;/css/LabelBox.css\u0026#34;) addWidgetStyles() } stage.title = \u0026#34;LabelBox Demo\u0026#34; stage.scene = scene stage.show() } fun createContent() = BorderPane().apply { padding = Insets(20.0, 20.0, 20.0, 20.0) styleClass += \u0026#34;wrapper-region\u0026#34; center = VBox(20.0, createPane(), createPane()) } private fun createPane(): Region { val label = Label(\u0026#34;This is the Title\u0026#34;).apply { layoutX = 10.0 styleClass += \u0026#34;box-label\u0026#34; } val borderPane = Pane().apply { styleClass += \u0026#34;border-pane\u0026#34; layoutYProperty().bind(label.heightProperty().divide(2.0)) } val content = StackPane().apply { layoutX = 5.0 layoutYProperty().bind(label.heightProperty().add(2.0)) children += Button(\u0026#34;This is Some Content\u0026#34;) } val pane = Pane().apply { minHeight = 200.0 children += listOf(borderPane, label, content) needsLayoutProperty().addListener(InvalidationListener { Platform.runLater { setClipping(borderPane, label) } }) borderPane.minWidthProperty().bind(widthProperty()) borderPane.minHeightProperty().bind(heightProperty().subtract(label.heightProperty().divide(2))) content.minWidthProperty().bind(widthProperty().subtract(10.0)) content.minHeightProperty().bind(heightProperty().subtract(label.heightProperty().add(6.0))) borderPane.widthProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) borderPane.heightProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) label.widthProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) label.heightProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) } return pane } private fun setClipping(nodeToClip: Region, areaToHide: Region) { val rectangle = Rectangle(nodeToClip.width, nodeToClip.height + nodeToClip.layoutY).apply { layoutX = nodeToClip.layoutX } val clip = Rectangle(areaToHide.width + 5.0, areaToHide.height).apply { layoutX = areaToHide.layoutX layoutY = areaToHide.layoutY } nodeToClip.clip = Shape.subtract(rectangle, clip) } } fun main() { Application.launch(LabelBox2Application::class.java) } The Layout # All the layout code is in createPane(). You can see that there are four elements: the Label; the Pane with the border; the StackPane with the content; and the Pane to hold it all.\nWe\u0026rsquo;ve changed the main Region from the VBox in the first example to a Pane. This is because we\u0026rsquo;re going to need to monkey with the layout positions of the elements in any event, so we don\u0026rsquo;t need the automatic layout of VBox here. It will also make it easier to translate to a custom class (you\u0026rsquo;ll see).\nPane just piles its children on top of each other in the top left corner of the Pane. So we need to position everything manually.\nThe label stays at the top, but it needs to be tucked over to the right to avoid the corner arc of the border. The borderPane stays at the left, but needs to move down 1/2 the height of the label. The contentPane needs to move down the height of the label plus a little bit, and also to the right to avoid the border. This positioning just sets the top left corner of each of the elements. Additionally, the borderPane needs to have its width and height bound to the containing Pane, with the height adjusted to account for how much it dropped down at the top to meet the middle of the label.\nThe contentPane also needs to be bound to the size of the containing Pane but adjusted to stay within the borderPane without touching the border.\nThe rest of the code deals with the clipping\u0026hellip;\nThe Clipping # The clipping is done by calling Node.setClip(), and, in and of itself, it\u0026rsquo;s pretty simple. The biggest issue to deal with is the fact that our clipping Shape is not going to appear on the SceneGraph. This means that it won\u0026rsquo;t be managed by the Layout Manager when the layout changes.\nWhat this really means is that we cannot us bind() to bind the locations and dimensions of the Shape components to the elements that are on the screen. Bummer.\nWhat we need to do is to use InvalidationListeners to trigger a recalculation of the clipping whenever the dimensions of the label or the borderPane change. This is why we have the four Listeners added at the end of the layout code, each of which just calls the clipping function.\nThe setClipping function itself is very simple. The two Rectangles are created with correct sizes, they are positioned properly, the smaller rectangle is subtracted from the larger one and the Node.setClip() is called on the borderPane.\nThere\u0026rsquo;s one final oddity, and it\u0026rsquo;s due to the fact that we cannot just bind the clipping shapes to the label and borderPane dimensions. Our Listeners are only triggered when the dimensions change after the elements are on the SceneGraph. The initial values aren\u0026rsquo;t captured, and any attempt to look at them just gives you zeros. What we need to do is to capture the moment that they are added to the SceneGraph and call the clipping routine after they have their dimensions established. We can catch that by putting a Listener on the needsLayoutProperty of the containing Pane. This flips to true when it\u0026rsquo;s ready to build the layout. Unfortunately, this happens before the layout is built, so we need to put our call to the clipping routine inside a call to Platform.runLater() which will run the clipping routine as a separate job after the layout is completed.\nThe Result # That\u0026rsquo;s it, this program creates a \u0026ldquo;builder\u0026rdquo; function that creates a LabelBox that works with complex backgrounds:\nCreating a Custom Class # The only real difference between Pane and Region is that Pane exposes getChildren() as a public method, while it\u0026rsquo;s protected in Region. This is an important distinction when creating custom classes, but it means that our layout code, which is based on Pane can be moved straight out of the box into a custom class extending Region.\nLet\u0026rsquo;s do that:\nclass LabelBox : Region() { init { val label = Label(\u0026#34;This is the Title\u0026#34;).apply { layoutX = 10.0 styleClass += \u0026#34;box-label\u0026#34; } val borderPane = Pane().apply { styleClass += \u0026#34;border-pane\u0026#34; layoutYProperty().bind(label.heightProperty().divide(2.0)) } val content = StackPane().apply { layoutX = 5.0 layoutYProperty().bind(label.heightProperty().add(2.0)) children += Button(\u0026#34;This is Some Content\u0026#34;) } minHeight = 200.0 children += listOf(borderPane, label, content) needsLayoutProperty().addListener(InvalidationListener { Platform.runLater { setClipping(borderPane, label) } }) borderPane.minWidthProperty().bind(widthProperty()) borderPane.minHeightProperty().bind(heightProperty().subtract(label.heightProperty().divide(2))) content.minWidthProperty().bind(widthProperty().subtract(10.0)) content.minHeightProperty().bind(heightProperty().subtract(label.heightProperty().add(6.0))) borderPane.widthProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) borderPane.heightProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) label.widthProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) label.heightProperty().addListener(InvalidationListener { setClipping(borderPane, label) }) } private fun setClipping(nodeToClip: Region, areaToHide: Region) { val rectangle = Rectangle(nodeToClip.width, nodeToClip.height + nodeToClip.layoutY).apply { layoutX = nodeToClip.layoutX } println(areaToHide.width) val clip = Rectangle(areaToHide.width + 5.0, areaToHide.height).apply { layoutX = areaToHide.layoutX layoutY = areaToHide.layoutY } nodeToClip.clip = Shape.subtract(rectangle, clip) } } All we\u0026rsquo;ve done here is wrap the two methods from our builder code into a class extending Region and rename the createPane() method it init.\nAnd then to run it\u0026hellip;\nclass LabelBox3Application : Application() { override fun start(stage: Stage) { val scene = Scene(createContent(), 320.0, 240.0).apply { addStyleSheet(\u0026#34;/css/LabelBox.css\u0026#34;) addWidgetStyles() } stage.title = \u0026#34;LabelBox Demo\u0026#34; stage.scene = scene stage.show() } fun createContent() = BorderPane().apply { padding = Insets(20.0, 20.0, 20.0, 20.0) styleClass += \u0026#34;wrapper-region\u0026#34; center = VBox(20.0, LabelBox(), LabelBox()) } } fun main() { Application.launch(LabelBox3Application::class.java) } It just runs and looks exactly the same as the last screen snap.\nRefining the Custom Class # There\u0026rsquo;s a bit of cleanup we can do, now that we have everything encapsulated into a new class\u0026hellip;\nclass LabelledPane : Region() { private val rectangle = Rectangle() private val clipZone = Rectangle() private val label = Label(\u0026#34;This is the Title\u0026#34;).apply { layoutX = 10.0 styleClass += \u0026#34;pane-label\u0026#34; widthProperty().addListener(InvalidationListener { setClipping() }) heightProperty().addListener(InvalidationListener { setClipping() }) } private val borderPane = Pane().apply { styleClass += \u0026#34;pane-border\u0026#34; minWidthProperty().bind(this@LabelledPane.widthProperty()) minHeightProperty().bind(this@LabelledPane.heightProperty().subtract(label.heightProperty().divide(2))) layoutYProperty().bind(label.heightProperty().divide(2.0)) widthProperty().addListener(InvalidationListener { setClipping() }) heightProperty().addListener(InvalidationListener { setClipping() }) } private val content = StackPane().apply { layoutX = 5.0 layoutYProperty().bind(label.heightProperty().add(2.0)) minWidthProperty().bind(this@LabelledPane.widthProperty().subtract(10.0)) minHeightProperty().bind(this@LabelledPane.heightProperty().subtract(label.heightProperty().add(6.0))) children += Button(\u0026#34;This is Some Content\u0026#34;) } init { minHeight = 200.0 children += listOf(borderPane, label, content) styleClass += \u0026#34;labelled-pane\u0026#34; needsLayoutProperty().addListener(InvalidationListener { Platform.runLater { setClipping() } }) } private fun setClipping() { with(rectangle) { width = borderPane.width height = borderPane.height + borderPane.layoutY layoutX = borderPane.layoutX } with(clipZone) { width = label.width + 5.0 height = label.height layoutX = label.layoutX layoutY = label.layoutY } borderPane.clip = Shape.subtract(rectangle, clipZone) } } First off, we\u0026rsquo;ve renamed the class to LabelledPane, which is more in line with JavaFX\u0026rsquo;s TitledPane which is somewhat similar.\nNext we\u0026rsquo;ve moved the components out of init{} and established them as fields of the class. This means we don\u0026rsquo;t need to pass them to setClipping(), and we can bind their dimension properties to the class properties when they are initialized. This allows init{} to be much smaller.\nFinally, the StyleClasses have been renamed, and the CSS now has the components in a structure:\n.labelled-pane { } .labelled-pane \u0026gt; .pane-label { -fx-font-size: 16px; } .labelled-pane \u0026gt; .pane-border { -fx-border-width: 2px; -fx-border-color: black; -fx-border-radius: 6px; } When this is run, it looks just like the other examples, nothing has functionally changed.\nMaking it Useful # If all you ever wanted to put into your LabelledPanes was a Button that did nothing with the caption \u0026ldquo;This is Some Content\u0026rdquo;, then we\u0026rsquo;d be done. But since probably do want to put our own content into the LabelledPane, we\u0026rsquo;ll need a way to do it, which means adding a function to the class. I haven\u0026rsquo;t been able to find a way to override Region's getChildren() method and still use it to add the other components, so I\u0026rsquo;m calling it getContent(), and it will just delegate to the getChildren() method of the StackPane called content.\nDealing With Sizing # One thing that become very quickly apparent when you try to vary the contents of the LabelledPane is that we need to handle the sizing of both the contents and the LabelledPane itself in relationship to the layout. Region has no inherent behaviour that prevents its children from extending beyond its bounds. So if the children are bigger than the size of the LabelledPane, they\u0026rsquo;ll just run outside the LabelledPane. So we\u0026rsquo;ll need to find a way to tame this.\nFor testing, I used a Label as the content with a large amount of text in it. I turned wrapping on, and Label will use ellipses to indicate if there is more text than can fit into the space provided. This is, therefore, a fairly complicated child from a layout perspective, that will challenge the design of the LabelledPane to contain it and display in an optimal manner.\nTo get some insight, I peeked into the layout code for VBox and then stole same ideas from it. In practice, this can become problematic because many of the standard JavaFX classes have private methods, and attempting to pull code out of these classes often means that you try to call private and package-protected methods from your own code in your own packages. Copying out those private methods, you find that they, in turn, call other private methods. And so on\u0026hellip;\nIn this case, though, it was possible to prune out some small snippets that do a few important things without going down a rabbit hole leading to huge amounts of code copied from VBox and Region.\nHere\u0026rsquo;s the code for the class:\nclass LabelledPane(labelText: String) : Region() { private val rectangle = Rectangle() private val clipZone = Rectangle() private val id: Int = counter++ companion object { var counter = 0 } private val label = Label(labelText).apply { layoutX = 10.0 styleClass += \u0026#34;pane-label\u0026#34; widthProperty().addListener(InvalidationListener { setClipping() }) heightProperty().addListener(InvalidationListener { setClipping() }) maxWidthProperty().bind(this@LabelledPane.widthProperty().subtract(24.0)) isWrapText = true } private val borderPane = Pane().apply { styleClass += \u0026#34;pane-border\u0026#34; minWidthProperty().bind(this@LabelledPane.widthProperty()) minHeightProperty().bind(this@LabelledPane.heightProperty().subtract(label.heightProperty().divide(2))) layoutYProperty().bind(label.heightProperty().divide(2.0)) widthProperty().addListener(InvalidationListener { setClipping() }) heightProperty().addListener(InvalidationListener { setClipping() }) } private val content = VBox().apply { layoutX = 5.0 layoutYProperty().bind(label.heightProperty().add(2.0)) maxWidthProperty().bind(this@LabelledPane.widthProperty().subtract(10.0)) } init { children += listOf(borderPane, label, content) styleClass += \u0026#34;labelled-pane\u0026#34; minHeightProperty().bind(label.heightProperty().add(content.minHeightProperty()).add(20.0)) widthProperty().addListener(InvalidationListener { prefHeight = getContentHeight() }) needsLayoutProperty().addListener(InvalidationListener { Platform.runLater { setClipping() content.maxHeightProperty().unbind() prefHeight = getContentHeight() content.maxHeightProperty().bind(heightProperty().subtract(label.heightProperty()).subtract(10.0)) } }) } private fun getContentHeight(): Double { val value = computeChildPrefAreaHeight(content, width - 6.0) return value + label.height + 10.0 } private fun computeChildPrefAreaHeight(child: Node, width: Double): Double { var snapWidth = -1.0 if (child.isResizable \u0026amp;\u0026amp; child.contentBias == Orientation.HORIZONTAL) { snapWidth = snapSizeX( boundedSize( child.minWidth(-1.0), if (width != -1.0) width else child.prefWidth(-1.0), child.maxWidth(-1.0) ) ) } return snapSizeY( boundedSize( child.minHeight(snapWidth), child.prefHeight(snapWidth), child.maxHeight(snapWidth) ) ) } private fun boundedSize(min: Double, pref: Double, max: Double): Double { return min(max(pref, min), max(min, max)) } private fun setClipping() { with(rectangle) { width = borderPane.width height = borderPane.height + borderPane.layoutY layoutX = borderPane.layoutX } with(clipZone) { width = label.width + 5.0 height = label.height layoutX = label.layoutX layoutY = label.layoutY } borderPane.clip = Shape.subtract(rectangle, clipZone) } fun getContent(): ObservableList\u0026lt;Node\u0026gt; = content.children fun labelTextProperty() = label.textProperty() } Conclusion # I think this project is neat because it\u0026rsquo;s a cool problem/idea, and you could ask the question, \u0026ldquo;Shouldn\u0026rsquo;t something like this be included in the standard JavaFX layout classes?\u0026rdquo;. And perhaps it should. But the truth is that JavaFX gives programmers all the tools that they need to create just about anything that you can think of. Yes, there are some quirky bits about this that aren\u0026rsquo;t obvious if you\u0026rsquo;re just a beginner, but it can be done.\nThere\u0026rsquo;s nothing magical about the Node classes included with JavaFX. If you look at the source code, you\u0026rsquo;ll see that it\u0026rsquo;s just standard Java code, using JavaFX tools to exactly the same stuff that\u0026rsquo;s shown here. For sure, there\u0026rsquo;s some super complicated stuff in some of the classes that will melt you brain if you try to figure it out, but it can be figured out, and it\u0026rsquo;s not necessarily any better than anything you could write yourself.\nFrom time to time, I see someone complain that \u0026ldquo;No new controls have been added to JavaFX for years\u0026hellip;\u0026rdquo;. There\u0026rsquo;s never any mention of what they think is missing, so I guess they\u0026rsquo;re just complaining to look smart or something. Part of the reason for the general nature of that statement is that if there was something missing, and they knew enough, or were skilled enough to be affected by it, they should have enough knowledge and skill to build it themselves.\nThis LabelledBox control isn\u0026rsquo;t perfect, I noticed some quirky things happening with those Labels when I resized the window causing the wrapping and truncating to change. But it is good enough for a lot of things. I\u0026rsquo;m sure that when I use it in layouts in the future, I\u0026rsquo;ll find more problems. And maybe I\u0026rsquo;ll fix them. Or not.\nThe other thing that\u0026rsquo;s cool about this project is that it shows how dead simple it is to go from factory/builder method customizing a layout to creating a full blown custom class. As a builder, we used Pane because we need access to a public getChildren(), but as a custom class, we extend Region and then we have access to its protected getChildren() method. Other than that, though\u0026hellip;not too much. A little bit more work was required to make it act more like a standard JavaFX Node, especially with regards to styling was just about all it took.\n","date":"25 March 2024","externalUrl":null,"permalink":"/javafx/techniques/custom_controls/label_pane/","section":"JavaFX","summary":"Another example of creating a custom class.  This time to build a container with a border and a Label that sits on the border.","title":"Creating a Custom Component: LabelledPane","type":"javafx"},{"content":"There is nothing special about the standard Nodes included with JavaFX (mostly). If you look at their source code, you\u0026rsquo;ll see that they are mostly just plain old Java, and don\u0026rsquo;t tend to rely on hidden packages that you cannot access yourself. This means that you can create your own Nodes and they won\u0026rsquo;t be second-class citizens to the standard Nodes. You can use virtually all of the same techniques that the authors of JavaFX used to create professional screen elements that work they way that you want.\n","date":"25 March 2024","externalUrl":null,"permalink":"/javafx/techniques/custom_controls/","section":"JavaFX","summary":"JavaFX provides all the tools that you need to create your own Nodes that just as good as the standard one provided by the library. These articles talk about how to go about creating your custom Nodes that behave exactly the way that you want them to.","title":"Custom Controls","type":"javafx"},{"content":"","date":"18 March 2024","externalUrl":null,"permalink":"/categories/listview/","section":"Categories","summary":"","title":"Listview","type":"categories"},{"content":" Introduction # My first year university programming course, way back in the 1980\u0026rsquo;s, was in Pascal. We were forbidden to use GOTO statements in our assignments:\nIf any program submitted for an assignment contains a GOTO statement, the code will be deemed to be \u0026ldquo;non-functional\u0026rdquo; and the highest grade that it can receive will be a \u0026ldquo;D\u0026rdquo;.\nIn case you\u0026rsquo;re too young: Pascal was one of the earlier Structured Programming languages. In structured languages, programs are composed of \u0026ldquo;blocks\u0026rdquo; of code, and execution would start at the top of each block and exit only at the bottom. Blocks of code could be broken down into smaller blocks using control structures, but each sub-block would be subject to the same rules about starting at the top and ending at the bottom. In this course, you were forbidden to exit a block in the middle by using a GOTO statement.\nThis has stuck with me for nearly 40 years, but not because of the structured programming concepts. But rather, it\u0026rsquo;s the idea that a program can run and give the correct answer, and still be incorrect.\nI\u0026rsquo;m not saying that I picked up on this idea when I was 20 years old, but after a decade or so of programming professionally, I definitely had developed an intuition about it.\nThis is probably the biggest difference between a beginning software developer and a good senior developer. It\u0026rsquo;s not that the senior developer has more experience with different languages and libraries and domain knowledge - which they certainly do. But that the senior developer - if they are good - has developed an instinct for the kind of patterns that should be avoided, and those that are more likely to be \u0026ldquo;correct\u0026rdquo; in any given situation.\nOf course, it\u0026rsquo;s not a binary choice between \u0026ldquo;right\u0026rdquo; and \u0026ldquo;wrong\u0026rdquo;, but more about the idea that some approaches are objectively better (or worse) than others.\nAn Example # Let\u0026rsquo;s look at the kind of mistakes that a beginner can make, and see how more experienced developers pick up on the problems while the beginner misses the most important points that they make\u0026hellip;\nThis example comes from StackOverflow. I\u0026rsquo;ve converted it to Kotlin, and stripped out the useless FXML stuff since it\u0026rsquo;s just a GridPane.\nclass GridPaneAbuse : Application() { private val gridPane = GridPane() override fun start(stage: Stage) { val scene = Scene(createContent(), 400.0, 300.0).apply { addWidgetStyles() } stage.scene = scene stage.show() } private fun createContent(): Region = gridPane.apply { isGridLinesVisible = true for (i in 0..2) { for (j in 0..2) { val cell = VBox().apply { minWidth = 60.0 minHeight = 60.0 styleClass += \u0026#34;test-blue\u0026#34; } GridPane.setRowIndex(cell, i) GridPane.setColumnIndex(cell, j) children.add(cell) } } doStuff() } private fun doStuff() { for (i in 0..2) { for (j in 0..2) { val node: Node = gridPane.children[i * 3 + j] val rowIndex = GridPane.getRowIndex(node) val colIndex = GridPane.getColumnIndex(node) if (rowIndex != null \u0026amp;\u0026amp; colIndex != null) { print(\u0026#34;($rowIndex,$colIndex) \u0026#34;) } else { print(\u0026#34;(idk) \u0026#34;) } } println() } } } fun main() = Application.launch(GridPaneAbuse::class.java) This just draws a GridPane and populates it with some VBoxes. The method doStuff() attempts to use a pair of nested loops to iterate through all of the children Nodes in the GridPane to lookup their row and column indices from the GridPane. If it gets these indices, then it adds it to the console output. In the event that it cannot get the indices, it prints \u0026ldquo;idk\u0026rdquo; instead of the coordinates.\nThe problem is that this is the output:\n(idk) (0,0) (0,1) (0,2) (1,0) (1,1) (1,2) (2,0) (2,1) You can see that the top left corner Node doesn\u0026rsquo;t have coordinates, and then the remaining locations are displaced, with cell (2,2) missing.\nRather than keep you in suspense, it turns out that the grid-lines cause this behaviour as they are considered \u0026ldquo;children\u0026rdquo; of the GridPane. They are placed into a Group and the Group is added to the GridPane's children as element 0.\nThe best responses came from James_D, who is an absolute monster on StackOverflow. He has a 200K+ reputation, and answers most JavaFX questions before I\u0026rsquo;ve even seen them. He writes some really comprehensive answers, too, and they are always bang on the money.\nHere\u0026rsquo;s what he said about this problem:\nThere\u0026rsquo;s absolutely no guarantee at all that the child nodes of a grid pane are in the order your question assumes (which I think is one child node per cell, filling by row left to right and then moving top to bottom from row to row). Cells can have multiple child nodes, cells can be empty, child nodes can be unassigned to cells, and the order of nodes in the getChildren() list is just the order in which you insert them via something like getChildren().add(\u0026hellip;). Without knowing how you added the nodes to the grid, and everything else you did with the grid, the question is unaswerable.\nAt which point the OP updated his question to add some of the missing information. After that, the grid-lines issue came up and the final comment from the OP on the actual problem was this:\n@James_D thank you so much, the problem was in fact that grid lines were turned on(I thought it is just an aesthetic feature) and that somehow for some reason messes indexes of cells. So from: \u0026ldquo;If I remember correctly, if you make grid lines visible the GridPane internally adds a Path as a child node to display the grid lines.\u0026rdquo; does that mean the output I recived as \u0026ldquo;(idk)\u0026ldquo;was basically related to the path to those added lines?\nBut let\u0026rsquo;s be clear here: The grid-lines were NOT the problem!\nVery early on the the comment thread, James_D nails the issue:\nGenerally, though, this doesn\u0026rsquo;t seem the right way to structure your application. The relationships between the positions should be part of the application state (model); you should not be trying to calculate those things based on the UI classes (view).\nand then\u0026hellip;\n\u0026hellip;As I said, though, this simply isn\u0026rsquo;t the right approach. The relationships between these entities are application state: you should not rely on storing them in the UI objects.\nThe OP responds with this - which seems like they almost get it, but not quite:\nOkay so I should\u0026rsquo;ve made 2D matrix and connect it with grid pane, and work with matrix right? Well I thought about that but I thought it would waste unnececary (sic) memory so I tried it this way. Now it is kind of late, besides I would like to see why this is being printed and why does this happen with this code.\n\u0026ldquo;Now it is kind of late\u0026rdquo;. In other words, they are not going to take the time to refactor the application such that it is correctly built.\nI\u0026rsquo;m not trying to get down on the OP here. They are obviously a beginner, and they are in the first stage of the \u0026ldquo;Hierarchy of Competence\u0026rdquo;: \u0026ldquo;Unconscious Incompetence\u0026rdquo;:\nBasically, this just means that they don\u0026rsquo;t know what they don\u0026rsquo;t know. This is what it means to be a beginner, you make beginner mistakes and your intuition about how things work and what constitutes a good solution are often just plain wrong.\nThis also explains the Dunning-Kruger effect.\nA beginner intuits the wrong approach to a problem, which makes it more complex. The added complexity challenges their skills even more, and the solution becomes very difficult. However, due to their lack of experience, they assume that everyone has the same amount of challenge when solving this problem and they rate themselves as closer to \u0026ldquo;average\u0026rdquo; in skill level compared to everyone else than they should.\nAn expert, on the other hand, intuits a far better answer to the problem. They execute a solution that takes about 1/10 the time, effort and complexity than the beginner. However, the correct approach is so clear to the expert that they assume that everybody else sees it too, and they also rate themselves as closer to \u0026ldquo;average\u0026rdquo; in skill level compared to everyone else than they should.\nGetting back on track\u0026hellip;\nThe idea that you can save a meaningful amount of memory by utilizing a screen Node as a data store is wrong. But even more important, the idea of using a screen Node to store data is just something that more experienced JavaFX programmers wouldn\u0026rsquo;t even think about doing.\nJames_D sums it up perfectly:\n\u0026ldquo;I should\u0026rsquo;ve made 2D matrix and connect it with grid pane\u0026rdquo; Yes, basically something along those lines. For a game or simulation (or any UI application, but those seem to be more applicable here) it\u0026rsquo;s always a good plan to first write it from the data perspective only, so you can drive it just from code, and then to add the UI on top of that once it\u0026rsquo;s working. The memory consumed by the data representation is basically negligible compared to the memory consumed by the UI, so that should not be a concern.\nThe \u0026ldquo;grid-lines as a child of the GridPane\u0026rdquo; concept is interesting. Hey! I didn\u0026rsquo;t know this either. But, ultimately, it\u0026rsquo;s just that, \u0026ldquo;interesting\u0026rdquo;, but not particularly useful.\nIf this was school assignment, and this approach was submitted to me to mark, I would deem it \u0026ldquo;non-functional\u0026rdquo; and give it a \u0026ldquo;D\u0026rdquo;. Relying on the private internal implementation of a library is simply a recipe for disaster.\nAbout that \u0026ldquo;2D Matrix\u0026rdquo; # Let\u0026rsquo;s look at the idea that the OP had for reworking the architecture:\nOkay so I should\u0026rsquo;ve made 2D matrix and connect it with grid pane, and work with matrix right?\nPretty much every beginner looking at creating some grid based application or game comes up with the idea of some data structure like Cell[][]. That\u0026rsquo;s what the beginners intuition tells them to do.\nBut is a 2D array really the best way to structure this?\nI don\u0026rsquo;t think so. What\u0026rsquo;s the advantage to it? Just about the only thing that I can see is that you get sub-nanosecond random access to any Cell given it\u0026rsquo;s coordinates. That\u0026rsquo;s it.\nBut what\u0026rsquo;s wrong with Cell[][]?\nFirst of all, you end up with nested row/column loops all over the application. Even in the example code, above, there\u0026rsquo;s two of the darned things - and that\u0026rsquo;s only 30 lines of code.\nSecondly, you end up doing row and column math all the time. \u0026ldquo;Down\u0026rdquo; math is row = row + 1 and column = column. But then you have to check to make sure that you haven\u0026rsquo;t end out of bounds. Is the new row bigger than maxRows? How do you handle that?\nThirdly - and this is where I think it starts to get to the \u0026ldquo;Right Intuition\u0026rdquo; of \u0026ldquo;Unconscious Competence\u0026rdquo; - Cell[][] isn\u0026rsquo;t a very \u0026ldquo;Java\u0026rdquo; approach.\nLet\u0026rsquo;s look at this some more\u0026hellip;\nThe Java Way of Doing This # A fundamental concept in Java is the idea of \u0026ldquo;encapsulation\u0026rdquo; - data and the code that deals that data are bundled together, and very often the data is not exposed to the outside world. Cell[][] simply violates that idea.\nNormally, when I see Cell[][] implemented, Cell does not contain the location coordinates. Presumably, beginners feel that having row and column fields inside Cell is a waste of memory since it\u0026rsquo;s already stored in the indices to Cell[][]. The result is that Cell no longer owns its own data, and you cannot have methods in Cell that deal with that data and potentially hide it from the outside world.\nThe truth is that most applications don\u0026rsquo;t actually need to know the Cell coordinates for most logic. Finding adjacent Cells is often much more important. If a Cell can tell you which Cell is \u0026ldquo;down\u0026rdquo; from it, that\u0026rsquo;s usually good enough. How the Cell determines \u0026ldquo;down\u0026rdquo; isn\u0026rsquo;t important to your application logic - just that it does it.\nWhat\u0026rsquo;s really important is that getting the Cell coordinates is almost never the goal of any operation. The only reason you ever want the Cell coordinates is to get that Cell. So if your Cells themselves can pass you Cells, then you never need the coordinates.\nThink about this:\nLet\u0026rsquo;s say you\u0026rsquo;re writing some sort of chess game, and you need all of the possible moves for a bishop, which is the diagonals from its current square (Cell). What do you want, a list of coordinates for those Cells, or a list of the Cells? So if a Cell can give you the Cell \u0026ldquo;up to the right\u0026rdquo; (or whatever diagonal direction you want), then you can ask that Cell for it\u0026rsquo;s neighbour \u0026ldquo;up to the right\u0026rdquo;, until the answer comes back EMPTY, at which point you\u0026rsquo;ve hit the edge of the board.\nNow think about how you might implement this.\nCells and a Cell Factory # What I would probably do is have fields for the adjacent Cells and they would be nullable in Kotlin or Optional in Java:\nclass Cell\u0026lt;T\u0026gt;(val column: Int, val row: Int, val contents: T) { var north: Cell\u0026lt;T\u0026gt;? = null var south: Cell\u0026lt;T\u0026gt;? = null var east: Cell\u0026lt;T\u0026gt;? = null var west: Cell\u0026lt;T\u0026gt;? = null val northEast: Cell\u0026lt;T\u0026gt;? get() = north?.east val northWest: Cell\u0026lt;T\u0026gt;? get() = north?.west val southEast: Cell\u0026lt;T\u0026gt;? get() = south?.east val southWest: Cell\u0026lt;T\u0026gt;? get() = south?.west fun adjacentCells() = listOfNotNull(north, south, east, west) fun diagonalCells() = listOfNotNull(northEast, northWest, southEast, southWest) fun touchingCells() = listOf(adjacentCells(), diagonalCells()) } I\u0026rsquo;ve chosen to make my Cell class generic, since the topography of the Cell is independent of whatever you are holding in it. So we have field called contents and it will be of whatever type we specify with Cell\u0026lt;T\u0026gt;.\nIn Kotlin, you can have properties (which are a bit like fields in Java, but more sophisticated) that are derived from other properties. In this case we have the diagonally adjacent Cells accessed through read-only properties that are based on Cells that are at the north, south, east and west positions. The formula for northEast is:\nget() = north?.east The ?. operator is very much like Optional.map(). If north is Null, then the result is Null, otherwise it will return the east field (which might also be Null) of that Cell in the north location. The result is nullable, which, once again, is somewhat equivalent to Optional in Java.\nThe other three methods return lists of neighbour Cells that really are there, due to the listOfNotNull() function. This means that they return List\u0026lt;Cell\u0026lt;T\u0026gt;\u0026gt;, not a List\u0026lt;Cell\u0026lt;T\u0026gt;?\u0026gt;, so you can use the List items as Cell.\nNow, on to creating Cells:\nclass CellFactory\u0026lt;T\u0026gt;(private val maxColumns: Int, private val maxRows: Int, contentSupplier: () -\u0026gt; T) { private var cells: Array\u0026lt;Array\u0026lt;Cell\u0026lt;T\u0026gt;\u0026gt;\u0026gt; = Array(maxColumns) { column -\u0026gt; Array(maxRows) { row -\u0026gt; Cell(column, row, contentSupplier.invoke()) } } init { getCells().forEach { cell -\u0026gt; cell.north = getCell(cell.column, cell.row - 1) cell.south = getCell(cell.column, cell.row + 1) cell.east = getCell(cell.column + 1, cell.row) cell.west = getCell(cell.column - 1, cell.row) } } fun getCells() = cells.flatten() private fun getCell(column: Int, row: Int): Cell\u0026lt;T\u0026gt;? = if ((column \u0026gt;= 0) \u0026amp;\u0026amp; (column \u0026lt; maxColumns) \u0026amp;\u0026amp; (row \u0026gt;= 0) \u0026amp;\u0026amp; (row \u0026lt; maxRows)) cells[column][row] else null } This class is, as the name implies, just a factory for creating a collection of Cell. It has one field, and that\u0026rsquo;s a two dimensional array. You need to provide a Supplier (which in Kotlin is () -\u0026gt; T) to generate the contents for each Cell when it\u0026rsquo;s instantiated. This could be a BiConsumer taking column and row if you want, but I didn\u0026rsquo;t need it for this.\nThe init{} block does the work of the Cell math to initialize the non-diagonal neighbours of each Cell. This has to be done as a separate, second step because any particular Cell will probably be instantiated before all of its neighbours are instantiated (unless it\u0026rsquo;s in the bottom right corner), so we cannot get references to them.\nThe function getCell() handles the boundary cases, when the indexes are less than zero or greater than the dimensions of the matrix.\nFinally getCells() is the only public function, and it returns a List\u0026lt;Cell\u0026lt;T\u0026gt;\u0026gt;. This means that the calling class won\u0026rsquo;t even have access to the two dimensional matrix - we don\u0026rsquo;t need it any more.\nAn Application Using These Cells # I used a Model-View-Controller-Interactor (MVCI) framework to build an application that works with this design of Cell.\nLet\u0026rsquo;s look at the Model first:\nclass Model() { val cells: ObservableList\u0026lt;Cell\u0026lt;ObjectProperty\u0026lt;Color\u0026gt;\u0026gt;\u0026gt; = FXCollections.observableArrayList() } This looks a little messy because of all of the nested generic references. Reading from the inside out; our actual data is Color and it\u0026rsquo;s in an ObjectProperty so that we can bind it to our View Properties. So ObjectProperty\u0026lt;Color\u0026gt; is the generic type of our Cells. Finally, all of the Cells are held in an ObservableList, and that\u0026rsquo;s all we have in our application Model.\nNow the Controller:\nclass Controller() { private val model = Model() private val interactor = Interactor(model) private val viewBuilder = ViewBuilder(model) { interactor.handleCellAction(it) } fun getView() = viewBuilder.build() } This is very vanilla. The Controller instantiates the other three components and then provides a getView() to serve up the layout to the application. We\u0026rsquo;re passing a Consumer\u0026lt;Cell\u0026gt; to our ViewBuilder as a constructor parameter that invokes a method in the Interactor.\nAnd the Interactor:\nclass Interactor(private val model: Model) { init { model.cells += CellFactory(10, 10, this::colourGenerator).getCells() } private fun colourGenerator(): ObjectProperty\u0026lt;Color\u0026gt; { val colors = listOf(Color.RED, Color.BLUE, Color.YELLOW, Color.GREEN, Color.ORANGE) return SimpleObjectProperty(colors[Random.nextInt(5)]) } fun handleCellAction(cell: Cell\u0026lt;ObjectProperty\u0026lt;Color\u0026gt;\u0026gt;) { cell.contents.value = Color.BLACK cell.diagonalCells().forEach { it.contents.value = Color.BLACK } } } The init{} method creates a CellFactory and then puts the resulting list of Cells in the ObservableList in our Model. The Supplier for the CellFactory creates ObjectProperty\u0026lt;Color\u0026gt; with one of 5 random colours.\nKotlin doesn\u0026rsquo;t require use to declare the CellFactory as CellFactory\u0026lt;ObjectProperty\u0026lt;Color\u0026gt;\u0026gt;, because it can figure that out by the return type of the Supplier.\nWe\u0026rsquo;ll come back to the handleCellAction() method after we look at the ViewBuilder:\nclass ViewBuilder(private val model: Model, private val clickHandler: (Cell\u0026lt;ObjectProperty\u0026lt;Color\u0026gt;\u0026gt;) -\u0026gt; Unit) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region = Pane().apply { model.cells.forEach { cell -\u0026gt; children += Rectangle(48.0, 48.0).apply { fillProperty().bind(cell.contents) x = (cell.column * 50.0) + 1 y = (cell.row * 50.0) + 1 onMouseClicked = EventHandler { clickHandler(cell) } } } } } This will create a Pane and then populate it with a bunch of Rectangle. Each Rectangle has its FillProperty bound to the ObjectProperty\u0026lt;Color\u0026gt; in a Cell. The position of each Rectangle is based on the column and row of a Cell.\nThe ViewBuilder is responsible for determining:\nThe shape of the GUI elements - in this case, a Rectangle The size of the GUI elements - in this case, 48px square. The location of the GUI elements - in this case, in a grid using x \u0026amp; y translations. Some things to note about this:\nThis is the only place in the entire application that actually uses the column and row properties of Cell outside the CellFactory. It needs to do this because has to be able to determine the topography of the GUI in order to draw it. This loop doesn\u0026rsquo;t use nested loops for column and row. Who cares what order the Rectangles are drawn? Each Cell can be treated on its own as a self-contained entity. The ViewBuilder knows where to put it with any reference to any other Cells. The ViewBuilder has no reference anywhere to maxRows or maxColumns. It doesn\u0026rsquo;t need them. These last two points really turn the GUI creation upside down. It\u0026rsquo;s data driven: Rather than looking for the correct piece of data to match with a GUI element that we\u0026rsquo;re drawing, we create the GUI elements for each piece of data as we iterate through the data. If our application logic meant that we wanted to leave holes in the grid, then we would simply remove those Cells from the Model and the ViewBuilder wouldn\u0026rsquo;t create the Rectangles for them. The ViewBuilder wouldn\u0026rsquo;t have any logic to leave holes, they\u0026rsquo;d just happen.\nIn our GUI, each Rectangle has two relationships back to the Cell that caused it to be created. One is the binding to the FillProperty of the Rectangle and the other is the OnMouseClicked handler. This handler invokes the Consumer passed in via the constructor, and it simply passes the related Cell to that Consumer.\nNow let\u0026rsquo;s think about the click handler. Through the Controller, we\u0026rsquo;re going to pass the Cell back to the handleCellAction() method in the Interactor. Here it is again:\nfun handleCellAction(cell: Cell\u0026lt;ObjectProperty\u0026lt;Color\u0026gt;\u0026gt;) { cell.contents.value = Color.BLACK cell.diagonalCells().forEach { it.contents.value = Color.BLACK } } ```logo: /assets/logos/JavaFXLogo.png This turns the `Cell` black, and then turns all of the `Cells` at its diagonal corners black as well. ### Running the Application Here\u0026#39;s the Application: ``` kotlin class CellApplication : Application() { override fun start(stage: Stage) { val controller = Controller() stage.scene = Scene(controller.getView(), 500.0, 500.0) stage.show() } } And this is what it looks like in action:\nOnce you\u0026rsquo;ve clicked on a few Rectangles it looks like this:\nSome Variations # It wouldn\u0026rsquo;t be unfair to say, \u0026ldquo;Hey! This still uses a two dimensional matrix.\u0026rdquo; So let\u0026rsquo;s take a look at that, and create a CellFactory that doesn\u0026rsquo;t use a two dimensional array:\nclass CellFactory\u0026lt;T\u0026gt;(private val maxColumns: Int, private val maxRows: Int, contentSupplier: () -\u0026gt; T) { private var cells: List\u0026lt;Cell\u0026lt;T\u0026gt;\u0026gt; = mutableListOf() init { for (column in 0..maxColumns) { for (row in 0..maxRows) { cells += Cell(column, row, contentSupplier()) } } cells.forEach { cell -\u0026gt; cell.north = getCell(cell.column, cell.row - 1) cell.south = getCell(cell.column, cell.row + 1) cell.east = getCell(cell.column + 1, cell.row) cell.west = getCell(cell.column - 1, cell.row) } } fun getCells(): List\u0026lt;Cell\u0026lt;T\u0026gt;\u0026gt; = cells.toList() private fun getCell(column: Int, row: Int): Cell\u0026lt;T\u0026gt;? = try { cells.first { cell -\u0026gt; (cell.column == column) \u0026amp;\u0026amp; (cell.row == row) } } catch (e: NoSuchElementException) { null } } Here we\u0026rsquo;ve just used a list to store all of the Cells. We still need the two nested loops to handle the initial creation of the Cells with the row and column values. Instead of using the [][] operator to look up a Cell by column and row, we just use a filter on the List. The List.first() function throws an exception if it cannot find anything that meets the predicate, so we catch that and just return a Null instead.\nThis version works. It may run a nanosecond slower than the original version, but unless you have a zillion Cells, it\u0026rsquo;s not going to matter. But that does NOT mean that it\u0026rsquo;s as good as the first version. On one hand, it avoids the boundary checking in getCell(), but the original version is more direct, and therefore might be easier to understand and maintain.\nIn either version, you could change the name to something like CellSet and make CellSet.getCell() public. This would allow for random access to individual Cells while still keeping the underlying data structure private.\nThis design makes the application Cell centric, instead of grid-centric. This means that operations are likely going to be recursive in nature. Let\u0026rsquo;s add a function to Cell to generate a List of Cells that are in a line in any direction (up, down, left, right or any of the 4 diagonals):\nfun row(function: (Cell\u0026lt;T\u0026gt;) -\u0026gt; Cell\u0026lt;T\u0026gt;?): List\u0026lt;Cell\u0026lt;T\u0026gt;\u0026gt; = function(this)?.run { listOf(this@Cell) + this.row(function) } ?: listOf(this) This is a recursive function with a closure. The function tells how to find the next Cell in the series. If the next Cell in the series is Null, then we\u0026rsquo;ve hit the edge of the grid, in which case we just return the current Cell as the only member of a List. Each Cell up the recursion chain will add itself to the List that it gets back from the Cell down the recursion chain.\nIf we go back to the handleCellAction() method in the Interactor and change it to use this new function:\nfun handleCellAction(cell: Cell\u0026lt;ObjectProperty\u0026lt;Color\u0026gt;\u0026gt;) { cell.contents.value = Color.BLACK cell.row { it.northEast }.forEach { it.contents.value = Color.BLACK } } We now get black diagonals when we click on a Rectangle:\nLet\u0026rsquo;s say that we\u0026rsquo;re doing some kind of Chess based game and we want to show knight moves from a location, indicating the shape of the knight\u0026rsquo;s movement (kind of an \u0026ldquo;L\u0026rdquo; shape). We could do something like this:\nfun handleCellAction(cell: Cell\u0026lt;ObjectProperty\u0026lt;Color\u0026gt;\u0026gt;) { cell.contents.value = Color.BLACK cell.row { it.north?.east?.east }.forEach { it.contents.value = Color.BLACK } cell.row { it.north?.east?.east }.mapNotNull { it.north }.forEach { it.contents.value = Color.GRAY } cell.row { it.north?.east?.east }.mapNotNull { it.north?.east }.forEach { it.contents.value = Color.GRAY } } The first call to Cell.row() (which isn\u0026rsquo;t really generating a \u0026ldquo;row\u0026rdquo; of Cells any more), returns the squares that the knight would move to. The second call yields the first intermediate Cell in the \u0026ldquo;L\u0026rdquo; shape, and the third call yields the second intermediate Cell. These intermediate Cells are shaded gray.\nAnd yes, things are getting a bit confused with the riot of colours going on now, but you should be able to see the pattern.\nConclusion # If you go back and count, you\u0026rsquo;ll find that this example application has a grand total of about 100 lines of code. Cell and CellFactory are the biggest classes, at about 20 lines each, and all the others are less than 15 lines. The longest function in the application is the ViewBuilder.build() at 10 lines long - if you include the 3 } lines at the end. It\u0026rsquo;s pretty hard to get lost in a 100 line application broken down into \u0026lt;10 line functions.\nEven with this little code, this demo application does a lot. Well\u0026hellip;maybe not \u0026ldquo;a lot\u0026rdquo;, but certainly more than you\u0026rsquo;d expect.\nBut is this \u0026ldquo;Right\u0026rdquo;, or even \u0026ldquo;Better\u0026rdquo;? # I think we can all agree that using the GridPane to store the location information about the data is actually wrong. But what about building the application around a data model consisting of a two dimensional array? Is that wrong? Is it even worse?\nOccasionally, I see applications where a programmer has chosen to create Map\u0026lt;String, Int\u0026gt; because they would rather not \u0026ldquo;clutter up\u0026rdquo; their application with:\nclass MyClass() { val name: String val amount: Int } They\u0026rsquo;ll use a Map even when they really aren\u0026rsquo;t interested in fast, random access to particular entries based on name. What they want is the Entry\u0026lt;String, Int\u0026gt; because it ties those two values together without all the \u0026ldquo;trouble\u0026rdquo; of creating a dedicated class for it.\nIf you have a Map and the only thing you\u0026rsquo;re ever doing is iterating over Map.entrySet(), then you probably shouldn\u0026rsquo;t be using a Map.\nPersonally, if I did need that fast, random access, I\u0026rsquo;d still create the class and then use a Map\u0026lt;String, MyClass\u0026gt;. Beginners usually choke over the idea of having a class with just two fields, and then using one of those fields as the key to a Map. Something about wastage, I think.\nThe idea of using a two dimensional array is just about the same thing. The array \u0026ldquo;ties together\u0026rdquo; the x/y location with the data without going through the \u0026ldquo;trouble\u0026rdquo; of creating a class for it.\nBut I think there\u0026rsquo;s more to it than just that\u0026hellip;\nThere\u0026rsquo;s the naive idea that a two dimensional array mirrors the reality of the application - that it\u0026rsquo;s essentially a two dimensional grid and the mechanics of the application and the resulting implementation should revolve around that topography.\nBut the problem with that approach is that it saddles you with the need to deal with that matrix in everything that you do.\nYes! But is that \u0026ldquo;wrong\u0026rdquo;??? Is it even \u0026ldquo;worse\u0026rdquo;????\nI think that, without a doubt, taking an approach that increases the complexity of your application can be classified as \u0026ldquo;worse\u0026rdquo;.\nIt\u0026rsquo;s often far worse than it at first appears. Take this sample application as an example - getting rid of the two dimensional array results in a completely different view of the application as it\u0026rsquo;s no longer grid-centric, but cell-centric, and all of the logic changes. Although it\u0026rsquo;s hard to see it from such a small application, this change of perspective actually has an enormous potential to simplify virtually every aspect of how the application works.\nPersonally, I have the attitude that if I\u0026rsquo;ve written something and I see (or someone shows me) a better way to do it, then my solution is \u0026ldquo;wrong\u0026rdquo;.\nIn any discussion of these issues, however, someone will eventually say something to the effect of (and maybe you\u0026rsquo;re thinking it too):\n\u0026ldquo;That\u0026rsquo;s just your way of doing it and it\u0026rsquo;s all a matter of style. This other way runs and gets the correct answer too; it might not be your way but it\u0026rsquo;s just as good\u0026rdquo;.\nAnd now we\u0026rsquo;re back where we started: A program can run and get the correct answer, and yet still be \u0026ldquo;wrong\u0026rdquo;.\nOutside of a school project, running and getting the correct answer isn\u0026rsquo;t the goal itself, it\u0026rsquo;s just a necessary requirement that has to be met on the way to the goal. In the real world, applications often need to written so that they can be understood, maintained and enhanced over an extended period of time by a variety of developers. The actual goal is to get the correct answer and support future maintenance and enhancement.\n","date":"11 March 2024","externalUrl":null,"permalink":"/programming/right-wrong/","section":"Programming","summary":"Is there such a thing as a “Right” or “Wrong” when looking at different approaches to solving a programming problem?  Can one approach be objectively better than another?  Spoiler alert:  yes, it can.","title":"Is There a Right or Wrong Way to Program Something?","type":"programming"},{"content":"","date":"11 March 2024","externalUrl":null,"permalink":"/categories/kotlin/","section":"Categories","summary":"","title":"Kotlin","type":"categories"},{"content":"","date":"11 March 2024","externalUrl":null,"permalink":"/categories/programming/","section":"Categories","summary":"","title":"Programming","type":"categories"},{"content":" Introduction # This article/project was inspired by this StackOverflow Question. The OP wanted help creating a \u0026ldquo;radial menu\u0026rdquo; for his application. If you\u0026rsquo;re not familiar with a radial menu, they\u0026rsquo;re commonly seen in video games (mostly because they work really well with joystick controllers) and they look like this:\nThis is the example linked to from the original question.\nI\u0026rsquo;m not too sure how useful a radial menu is in a regular, business, application, but I thought it would make for an interesting project and as another example of how to create a custom control by extending Region.\nDevelopment Notes # This project didn\u0026rsquo;t turn out the way I had expected it to. Originally, I had planned to create the menu items as some sort of Pane turned into doughnut wedges via the setShape() function. However, they didn\u0026rsquo;t behave nicely, and that had to be abandoned.\nMy next attempt was to build the wedges as Shapes created from Paths, all built at the top of the menu and then rotated around the centre of the menu to their correct places. It would have been a win for the layout, as my intention was to create the Labels at the same time, and I wouldn\u0026rsquo;t need to worry about rotations when doing the layout.\nWhile that worked better than using Panes, it had some problems. The biggest was that the aspect ratio of the monitor meant that the rated wedges didn\u0026rsquo;t connect back to make a circle. They connected nicely, but the complete shape was stretched in one dimension. Also, when scaled up during a \u0026ldquo;mouse\u0026rdquo; hover, they grew in funny directions, and not straight out from the centre - which was a problem.\nFinally, I had no choice but to create each menu \u0026ldquo;wedge\u0026rdquo; in its correct place around the menu, and deal with the rotations of the Labels as part of their layout. The result is that the overall design really feels like a disconnected bunch of elements that happen to occupy the same space on the screen. That being said, it works well.\nBefore we look at any code, let\u0026rsquo;s take a look at what the final product should look like:\nIf you look closely, you can see that the menu item labelled, \u0026ldquo;This is the Sixth\u0026rdquo;, which has the mouse over it, is a bit bigger and brighter than the others. It\u0026rsquo;s also more opaque, but it\u0026rsquo;s hard to tell with the white background. It also has a fuzzy outline from the Bloom effect that\u0026rsquo;s been added. All of this is easier to see when you\u0026rsquo;re actually using the menu.\nYou can also see that the text is upside-down for the three items at the bottom of the menu. I decided to leave this way, as I felt that the added complexity of changing this outweighed the benefits. I was also tempted to find a way to have the text curve to follow along the outer radius - for the same reason.\nFinally, some of the geometry gets a bit goofy if the menu only has two or three items, and graphics tend to drift towards the centre. Here again, the complexity needed to correct this outweighed the benefits - who needs a radial menu with two items?\nTrigonometry and Path Shapes # This project is highly graphical, and drawing the shapes in the correct place is a big part of the coding.\nTrigonometry # There\u0026rsquo;s no getting around it, for this project you\u0026rsquo;ll need to understand a little trigonometry. So, let\u0026rsquo;s jump right into that and get it out of the way:\nThe whole menu looks like a doughnut, and throughout this article we\u0026rsquo;ll call the pieces of the doughnut that make up the menu items, \u0026ldquo;wedges\u0026rdquo;.\nThe math libraries, for the most part, deal with angles in radians, not degrees. Don\u0026rsquo;t freak out, a radian is just an amount and just as easy to deal with as degrees. In the same way that there are 360 degrees in a circle, there are 2π radians in a circle. That\u0026rsquo;s kind of a weird number, but it\u0026rsquo;s somewhere around 6, meaning that 1 radian is about 60 degrees - just so you can picture that in your head.\nObviously, the size of a wedge is dependent on the number of wedges that make up the menu. Looking at the diagram above, the total angular width of a wedge is 2θ (that\u0026rsquo;s the angle in the diagram). Since all of our trigonometry is based on that \u0026ldquo;theta\u0026rdquo; angle, that\u0026rsquo;s what we\u0026rsquo;re going to call it - \u0026ldquo;theta\u0026rdquo;.\nThe angle width of each wedge is 2π divided by the number of menu items (let\u0026rsquo;s call that numMenuItems), so 2π/numItems. Since θ is 1/2 of that, we can make it π/numMenuItems. We also want the first menu item to point straight up, and since straight up is 0 radians, we need to straddle 0 with our two θ angles. Looking at the diagram, the angles are -θ, and +θ. Going clockwise, the left side of the next wedge would be 1θ, and the right side would be 3θ. And so on.\nMathematically, if we (as programmers do) start counting at 0, then the left side of each wedge is ((2 * itemNumber) - 1) * θ and the right side is ((2 * itemNumber) + 1) * θ. So item number 0 gives -θ and +θ, and item number 2 gives θ and 3θ, and then 3θ and 5θ and so on.\nEach corner has an X and Y coordinate based on which multiple of theta that it uses, and which radius it is located on. For X, it\u0026rsquo;s:\nX = radius * sin(θ)\nFor Y, it\u0026rsquo;s:\nY = radius * cos(θ)\nWith θ determined by the formula from above.\nFinally, using the standard JavaFX coordinate system, this would put the origin [0,0] in the top left corner of the window. This will result in 3/4 of our menu above or to the left of the window. So we\u0026rsquo;ll need to translate the whole thing down and to the right. How much? Just a bit more than outer radius. We can just add this offset to every X and Y that we calculate.\nDependencies # This is very important. The geometry of our menu is actually dependent on a very small number of things:\nTheta (θ) is dependent on the number of menu items. Nothing else, and we can derive it just from knowing how many menu items we have.\nThe origin X and Y are entirely dependent on the outer radius.\nEvery corner is dependent on either the inner or outer radius, the origin location, θ and which itemNumber it is.\nWe can store all of these dependencies as properties of our class:\nprivate val menuItems = FXCollections.observableArrayList(menuItem1, menuItem2) private val numItems = Bindings.createIntegerBinding({ menuItems.size }, menuItems) private val theta: DoubleBinding = Bindings.createDoubleBinding({ PI / numItems.value }, numItems) private val innerRadiusProperty: StyleableDoubleProperty = SimpleStyleableDoubleProperty(INNER_RADIUS_META_DATA, 60.0) private val outerRadiusProperty: StyleableDoubleProperty = SimpleStyleableDoubleProperty(OUTER_RADIUS_META_DATA, 200.0) private val originX = Bindings.createDoubleBinding({ outerRadiusProperty.value * 1.04 }, outerRadiusProperty) private val originY = Bindings.createDoubleBinding({ outerRadiusProperty.value * 1.04 }, outerRadiusProperty) The numItems Property is derived from the size ObservableList holding the menu items. Theta is a binding to that through a formula and the inner and outer radii are StyleableDoubleProperties. OriginX and originY are Bindings based on the outer radius.\nDrawing Shapes # JavaFX has a Shape subclass called Path. Path is a closed shape that is constructed by stringing together a set of connected PathElements. Think of Path like drawing the shape with a pencil. You put the pencil down on the paper, then you move it along the paper to the next corner, and then the next and so on.\nThere\u0026rsquo;s a number of different PathElements in JavaFX, but we\u0026rsquo;re going to just use three of them: MoveTo, LineTo and ArcTo. We\u0026rsquo;ll use MoveTo to get the \u0026ldquo;pencil\u0026rdquo; in the right place to start drawing, then we\u0026rsquo;ll use ArcTo to draw the curves that run along the inner and outer circumfrences of the wedge, and LineTo to draw the straight sides. Actually, we won\u0026rsquo;t draw the left side because we\u0026rsquo;ll end at the fourth corner and JavaFX will automatically draw the line to our starting point and close the shape for us.\nOur path is going to look like this:\nMoveTo the outer left corner ArcTo the outer right corner LineTo the inner right corner ArcTo the inner left corner Auto-close But here\u0026rsquo;s the thing: Each of those PathElements is a JavaFX object of it own. This means that each one has Properties! ArcTo, for instance has properties for RadiusX and RadiusY (it allows of ellipse curves), and the destination X and Y. This means that each of these properties can be bound to the two or three Properties of our class that it depends on. Then if the number of menu items changes, or the inner or outer radius are changed, the shapes will automatically adapt and always be correct.\nLet\u0026rsquo;s take a look at that:\nprivate fun createShape(itemNumber: Int) = Path().apply { styleClass += \u0026#34;wedge\u0026#34; elements += MoveTo().apply { xProperty().bind(xBinding(theta.multiply((itemNumber * 2) - 1), outerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) - 1), outerRadiusProperty)) } elements += ArcTo().apply { radiusXProperty().bind(outerRadiusProperty) radiusYProperty().bind(outerRadiusProperty) isSweepFlag = true isLargeArcFlag = false xProperty().bind(xBinding(theta.multiply((itemNumber * 2) + 1), outerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) + 1), outerRadiusProperty)) } elements += LineTo().apply { xProperty().bind(xBinding(theta.multiply((itemNumber * 2) + 1), innerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) + 1), innerRadiusProperty)) } elements += ArcTo().apply { radiusXProperty().bind(innerRadiusProperty) radiusYProperty().bind(innerRadiusProperty) isSweepFlag = false xProperty().bind(xBinding(theta.multiply((itemNumber * 2) - 1), innerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) - 1), innerRadiusProperty)) } } The isSweepFlag and isLargeArcFlag determine which direction the curve of the arc goes, and whether the arc takes the shortest route between the two points or not. The JavaDocs are actually pretty good at describing this.\nMenu Items # The Presentation Model for each menu item is the class RadialMenuModel:\nclass RadialMenuModel( initialText: String, initialGraphic: Node, val action: () -\u0026gt; Unit, isDisable: Boolean = false, val effect: Effect = Bloom(0.2) ) { val disable: BooleanProperty = SimpleBooleanProperty(isDisable) val text: StringProperty = SimpleStringProperty(initialText) val graphic: ObjectProperty\u0026lt;Node\u0026gt; = SimpleObjectProperty(initialGraphic) } We have a field for the text and graphic components of the Label that will appear in the menu wedge, a functional element equivalent to a Java Runnable for the menu action and then an Effect that will be used when the menu item is hovered over. This effect field has a default which is Bloom(0.2).\nA new menu wedge can be created by instantiating a RadialMenuModel and then adding via the RadialMenu.addMenuItem() function.\nThe Labels # The contents of the menu wedges is just a Label. Here, it\u0026rsquo;s necessary to instantiate the Label and then translate it to the correct position and the use a Rotation transformation to keep it oriented downwards to the centre of the menu.\nThe tricky part here is that the placement of the items in JavaFX is done via the [0,0] location in the Node, which is the top-left corner. Because of the layout of the Label in this case, that\u0026rsquo;s the top left corner of the Text enclosed in it (or pretty close).\nSo, where does this top left corner go?\nWell, it depends on the amount of Text, which will determine how wide the Label is. Essentially, we need to get coordinates of the centre angle of the wedge just a bit in from the outer radius, and then shift it counterclockwise by 1/2 the width of the Label. Everything is done in terms of bindings, too. It\u0026rsquo;s finicky, but the solution in the code seems to work well enough:\nprivate fun createLabel(radialMenuModel: RadialMenuModel, itemNumber: Int) = labelOf(radialMenuModel.text, \u0026#34;contents\u0026#34;, radialMenuModel.graphic).apply { isMouseTransparent = true maxWidthProperty().bind(xBinding(theta, outerRadiusProperty).subtract(originX).multiply(1.5)) translateXProperty().bind( xBinding( theta.multiply(itemNumber * 2.0).subtract(widthProperty().divide(2).divide(outerRadiusProperty)), outerRadiusProperty.multiply(0.98) ) ) translateYProperty().bind( yBinding( theta.multiply(itemNumber * 2.0).subtract(widthProperty().divide(2).divide(outerRadiusProperty)), outerRadiusProperty.multiply((0.98)) ) ) transforms += Rotate(((theta.value * itemNumber) / PI) * 360.0, 0.0, 0.0).apply { angleProperty().bind( Bindings.createDoubleBinding( { (this@RadialMenu.theta.value * itemNumber * 360) / PI }, this@RadialMenu.theta ) ) } } Disabling Menu Items # This was a happy surprise. I checked and found that the disable property is inherited from Node - way, way up the hierachy. This also means that both Group and Shape have a disable property. This was counter-intuitive to me. Disable makes sense for controls like Button or TextField, but not so much for Nodes like Shape. So I tried it.\nI added a BooleanProperty called disable to RadialMenuItem and the bound the disable property of the Group that holds the wedge and the Label. I set up the sixth item to have its diable set to true. Bingo! It just worked. The wedge no longer responded to hover or mouse clicks. It also inherited some default styling from the Modena style sheet, which basically made it more transparent.\nStyling # The idea for this project was to create a control which, for the programmer, has the same kind of interface as any other control. This means making its styling configurable via CSS, just like any other JavaFX Node. For the most part, this done by giving the menu and the menu items specific styleclass selectors. Which then allows standard JavaFX styling elements to be used.\nThere are two elements, the inner and outer radii, that need to be available via CSS, so these two parameters are implemented via StyleableDoubleProperty, which then needs to be configured to connect to the CSS. Let\u0026rsquo;s take a look at these. First, the Properties themselves:\nprivate val innerRadiusProperty: StyleableDoubleProperty = SimpleStyleableDoubleProperty(INNER_RADIUS_META_DATA, 60.0) private val outerRadiusProperty: StyleableDoubleProperty = SimpleStyleableDoubleProperty(OUTER_RADIUS_META_DATA, 200.0) These are both straight-forward. They\u0026rsquo;re just regular Properties with tag for the CSS metadata that they link to.\nAnd now the configuration stuff where that CSS metadata is defined:\ncompanion object CssStuff { val OUTER_RADIUS_META_DATA: CssMetaData\u0026lt;RadialMenu, Number\u0026gt; = object : CssMetaData\u0026lt;RadialMenu, Number\u0026gt;(\u0026#34;-rm-outer-radius\u0026#34;, StyleConverter.getSizeConverter()) { override fun isSettable(styleable: RadialMenu) = !styleable.outerRadiusProperty.isBound override fun getStyleableProperty(styleable: RadialMenu) = styleable.outerRadiusProperty } val INNER_RADIUS_META_DATA: CssMetaData\u0026lt;RadialMenu, Number\u0026gt; = object : CssMetaData\u0026lt;RadialMenu, Number\u0026gt;(\u0026#34;-rm-inner-radius\u0026#34;, StyleConverter.getSizeConverter()) { override fun isSettable(styleable: RadialMenu) = !styleable.innerRadiusProperty.isBound override fun getStyleableProperty(styleable: RadialMenu) = styleable.innerRadiusProperty } private val cssMetaDataList = (Region.getClassCssMetaData() + OUTER_RADIUS_META_DATA + INNER_RADIUS_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getCssMetaData() = getClassCssMetaData() This type of code is explained in detail in another article, you can read about it here .\nIn a nutshell, we\u0026rsquo;re instantiating two anonymous inner classes that extend CssMetaData and linking them to our two StyleableDoubleProperties. Then we add these two objects to the cssMetaDataList that comes from our parent class, which is Region. Finally, we provide the functions so that the JavaFX layout engine can find this metadata and connect it to the actual CSS files for the Scene.\nThis code is all pretty much boilerplate, but you should note that the two styleclass selectors that it creates are -rm-outer-radius and -rm-inner-radius. Those are the selectors that we\u0026rsquo;ll need to use in our stylesheet.\nStyleclass Selectors and Colours # The entire Radial Menu is given the selector radial-menu.\nEach menu item is given two selectors: radial-menu-item and radial-menu-item-{itemNumber}. So we have radial-menu-item-0 and radial-menu-item-1 and so on. The label in each item is given the selector: contents.\nThe central circle is given the selector centre. The Label in the centre is given the selector centre-contents. The central Circle and the Label aren\u0026rsquo;t contained in a group, so the Label selector isn\u0026rsquo;t nested inside the centre selector.\nI checked the JavaFX source code for some routines like Color.saturate() to see how they work. Bascially, these routines convert the colours to HSB (Hue, Saturation, Brightness) and then manipulate the saturation and brightness elements of the colour. That gave me some insight into how to define the colours so that it was easy to come up with two versions of each colour - one for the \u0026ldquo;normal\u0026rdquo; status, and one for the \u0026ldquo;hover\u0026rdquo; status.\nI came up with a pallet of complementary pastel colours and got their HSB definitions. I decided on a two stage approach to colour definition. First I would define them as \u0026ldquo;named\u0026rdquo; colours; one for normal and then a saturated version. Like this:\n-brown-sugar: hsb(11, 42%, 54%); -s-brown-sugar: hsb(11, 52%, 45%); You can see that, in this case, the saturated version has the saturation parameter raised, and the brightness dropped, so it should be darker but more vibrant. Darker base colours need the brightness raised for the saturated version.\nI think that the HSB definitions made it super easy to fine tune the colours once I saw them running in the application. I even adjusted the hue on a couple of them when I thought that they were too similar. It would have been way, way more troublesome having to adjust the RGB value in a hex definition of the colours.\nAnd then I would assign them to \u0026ldquo;colour roles\u0026rdquo; with names that linked them to the RadialMenu. Like this:\n-rm-colour-0: -brown-sugar; -rm-saturate-0: -s-brown-sugar; I used the \u0026ldquo;-rm\u0026rdquo; prefix to avoid collisions with the Modena namespace. This gave me two colours for each menu item.\nTo implement the colours, I used a standard \u0026ldquo;trick\u0026rdquo; from Modena:\n.radial-menu-item .wedge { -fx-fill: -rm-fill-colour; } .radial-menu-item .wedge:hover { -rm-fill-colour: -rm-saturate-colour; } .radial-menu-item-0 { -rm-fill-colour: -rm-colour-0; -rm-saturate-colour: -rm-saturate-0; } (Note that this snippet just has the colour stuff, other styling info has been stripped out.)\nThe \u0026ldquo;trick\u0026rdquo; here is to define a standard colour, -rm-fill-colour, using it in the base radial-menu-item selector, then redefine it for the hover status. In this case, it\u0026rsquo;s a standard colour called -rm-saturate-colour. Default values for these colours are defined in the .root section. Finally, the standard definitions for these colours are overridden in the context of the specific menu items.\nUsing this technique, we don\u0026rsquo;t have to define two states, \u0026ldquo;normal\u0026rdquo; and \u0026ldquo;hover\u0026rdquo; for each menu item, which keeps the stylesheet a bit simpler.\nThe colour scheme can be completely changed without touching anything other than the definitions created in the .root section.\nThe Stylesheet # Here\u0026rsquo; the whole stylesheet, so you can see how it hangs together:\n.root { -brown-sugar: hsb(11, 42%, 54%); -s-brown-sugar: hsb(11, 52%, 45%); -sage: hsb(83, 12%, 55%); -s-sage: hsb(83, 23%, 35%); -air-force-blue: hsb(199, 20%, 53%); -s-air-force-blue: hsb(199, 35%, 45%); -burnt-sienna: hsb(19, 75%, 60%); -s-burnt-sienna: hsb(19, 85%, 45%); -space-cadet: hsb(227, 42%, 19%); -s-space-cadet: hsb(227, 65%, 40%); -lion: hsb(38, 47%, 57%); -s-lion: hsb(38, 65%, 45%); -feldgrau: hsb(159, 30%, 31%); -s-feldgrau: hsb(159, 40%, 31%); -rm-colour-0: -brown-sugar; -rm-saturate-0: -s-brown-sugar; -rm-colour-1: -sage; -rm-saturate-1: -s-sage; -rm-colour-2: -air-force-blue; -rm-saturate-2: -s-air-force-blue; -rm-colour-3: -burnt-sienna; -rm-saturate-3: -s-burnt-sienna; -rm-colour-4: -space-cadet; -rm-saturate-4: -s-space-cadet; -rm-colour-5: -lion; -rm-saturate-5: -s-lion; -rm-colour-c: -feldgrau; -rm-saturate-c: -s-feldgrau; -rm-fill-colour: -feldgrau; -rm-saturate-colour: -s-feldgrau; } .radial-menu { -rm-outer-radius: 200.0; -rm-inner-radius: 70.0; } .radial-menu .centre { -fx-fill: -rm-colour-c; -fx-opacity: 0.7; } .radial-menu .centre:hover { -fx-fill: -rm-saturate-c; -fx-opacity: 0.99; -fx-scale-x: 1.1; -fx-scale-y: 1.1; } .radial-menu-item { -fx-opacity: 0.85; } .radial-menu-item .contents { -fx-text-fill: ladder(-rm-fill-colour, BISQUE 34%, -space-cadet 35%); -fx-font-weight: bold; -fx-font-size: 16px; -fx-wrap-text: true; -fx-text-alignment: center; -fx-content-display: bottom; } .radial-menu-item .wedge { -fx-fill: -rm-fill-colour; -fx-stroke: derive(-rm-fill-colour, -40%); -fx-stroke-width: 3px; } .radial-menu-item .wedge:hover { -rm-fill-colour: -rm-saturate-colour; -fx-scale-x: 1.05; -fx-scale-y: 1.05; -fx-opacity: 1.0; } .radial-menu-item-0 { -rm-fill-colour: -rm-colour-0; -rm-saturate-colour: -rm-saturate-0; } .radial-menu-item-1 { -rm-fill-colour: -rm-colour-1; -rm-saturate-colour: -rm-saturate-1; } .radial-menu-item-2 { -rm-fill-colour: -rm-colour-2; -rm-saturate-colour: -rm-saturate-2; } .radial-menu-item-3 { -rm-fill-colour: -rm-colour-3; -rm-saturate-colour: -rm-saturate-3; } .radial-menu-item-4 { -rm-fill-colour: -rm-colour-4; -rm-saturate-colour: -rm-saturate-4; } .radial-menu-item-5 { -rm-fill-colour: -rm-colour-5; -rm-saturate-colour: -rm-saturate-5; } .radial-menu-item-6 { -rm-fill-colour: -rm-colour-6; -rm-saturate-colour: -rm-saturate-6; } .wrapper-region { -fx-background-colour: linear-gradient(to bottom, deepskyblue, khaki); } Putting it All Together # The centre section is pretty staight-forward, as there are no translations involving angles. Also, the centre isn\u0026rsquo;t put into a Group as there\u0026rsquo;s no need to handle toFront() for it.\nHere\u0026rsquo;s the code for the RadialMenu all in one place:\nclass RadialMenu( menuItem1: RadialMenuModel, menuItem2: RadialMenuModel, centreMenuItem: RadialMenuModel, vararg additionalMenuItems: RadialMenuModel ) : Pane() { private val menuItems = FXCollections.observableArrayList(menuItem1, menuItem2) private val numItems = Bindings.createIntegerBinding({ menuItems.size }, menuItems) private val theta: DoubleBinding = Bindings.createDoubleBinding({ PI / numItems.value }, numItems) private val innerRadiusProperty: StyleableDoubleProperty = SimpleStyleableDoubleProperty(INNER_RADIUS_META_DATA, 60.0) private val outerRadiusProperty: StyleableDoubleProperty = SimpleStyleableDoubleProperty(OUTER_RADIUS_META_DATA, 200.0) private val originX = Bindings.createDoubleBinding({ outerRadiusProperty.value * 1.04 }, outerRadiusProperty) private val originY = Bindings.createDoubleBinding({ outerRadiusProperty.value * 1.04 }, outerRadiusProperty) companion object CssStuff { val OUTER_RADIUS_META_DATA: CssMetaData\u0026lt;RadialMenu, Number\u0026gt; = object : CssMetaData\u0026lt;RadialMenu, Number\u0026gt;(\u0026#34;-rm-outer-radius\u0026#34;, StyleConverter.getSizeConverter()) { override fun isSettable(styleable: RadialMenu) = !styleable.outerRadiusProperty.isBound override fun getStyleableProperty(styleable: RadialMenu) = styleable.outerRadiusProperty } val INNER_RADIUS_META_DATA: CssMetaData\u0026lt;RadialMenu, Number\u0026gt; = object : CssMetaData\u0026lt;RadialMenu, Number\u0026gt;(\u0026#34;-rm-inner-radius\u0026#34;, StyleConverter.getSizeConverter()) { override fun isSettable(styleable: RadialMenu) = !styleable.innerRadiusProperty.isBound override fun getStyleableProperty(styleable: RadialMenu) = styleable.innerRadiusProperty } private val cssMetaDataList = (Region.getClassCssMetaData() + OUTER_RADIUS_META_DATA + INNER_RADIUS_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getCssMetaData() = getClassCssMetaData() fun outerRadiusProperty() = outerRadiusProperty fun innerRadiusProperty() = innerRadiusProperty var outerRadius: Double get() = outerRadiusProperty.get() set(value) = outerRadiusProperty.set(value) var innerRadius: Double get() = innerRadiusProperty.get() set(value) = innerRadiusProperty.set(value) init { styleClass += \u0026#34;radial-menu\u0026#34; children += createMenuItem(0, menuItem1) children += createMenuItem(1, menuItem2) children += createCentre(centreMenuItem) children += createCentreLabel(centreMenuItem) addMenuItem(*additionalMenuItems) minHeight = 2.08 * outerRadiusProperty.value minWidth = 2.08 * outerRadiusProperty.value } fun addMenuItem(vararg radialMenuModels: RadialMenuModel) { radialMenuModels.forEach { radialMenuItem -\u0026gt; menuItems += radialMenuItem children += createMenuItem(numItems.value - 1, radialMenuItem) } } private fun createCentre(radialMenuModel: RadialMenuModel) = Circle().apply { styleClass += \u0026#34;centre\u0026#34; centerXProperty().bind(originX) centerYProperty().bind(originX) radiusProperty().bind(innerRadiusProperty.subtract(15.0)) onMouseClicked = EventHandler { radialMenuModel.action.invoke() } onMouseEntered = EventHandler { effect = radialMenuModel.effect } onMouseExited = EventHandler { effect = null } } private fun createCentreLabel(radialMenuModel: RadialMenuModel) = labelOf(radialMenuModel.text, \u0026#34;centre-contents\u0026#34;, radialMenuModel.graphic).apply { this.translateXProperty().bind(originX.subtract(this.widthProperty().divide(2))) this.translateYProperty().bind(originY.subtract(this.heightProperty().divide(2))) isMouseTransparent = true } private fun createMenuItem(itemNumber: Int, radialMenuModel: RadialMenuModel) = Group().apply { val wedge = createShape(itemNumber) disableProperty().bind(radialMenuModel.disable) styleClass += \u0026#34;radial-menu-item\u0026#34; styleClass += \u0026#34;radial-menu-item-$itemNumber\u0026#34; maxWidth = outerRadiusProperty.value * 2.0 children += wedge children += createLabel(radialMenuModel, itemNumber) wedge.onMouseEntered = EventHandler { toFront() wedge.effect = radialMenuModel.effect } wedge.onMouseExited = EventHandler { wedge.effect = null } wedge.onMouseClicked = EventHandler\u0026lt;MouseEvent\u0026gt; { radialMenuModel.action.invoke() } } private fun createLabel(radialMenuModel: RadialMenuModel, itemNumber: Int) = labelOf(radialMenuModel.text, \u0026#34;contents\u0026#34;, radialMenuModel.graphic).apply { isMouseTransparent = true maxWidthProperty().bind(xBinding(theta, outerRadiusProperty).subtract(originX).multiply(1.5)) translateXProperty().bind( xBinding( theta.multiply(itemNumber * 2.0).subtract(widthProperty().divide(2).divide(outerRadiusProperty)), outerRadiusProperty.multiply(0.98) ) ) translateYProperty().bind( yBinding( theta.multiply(itemNumber * 2.0).subtract(widthProperty().divide(2).divide(outerRadiusProperty)), outerRadiusProperty.multiply((0.98)) ) ) transforms += Rotate(((theta.value * itemNumber) / PI) * 360.0, 0.0, 0.0).apply { angleProperty().bind( Bindings.createDoubleBinding( { (this@RadialMenu.theta.value * itemNumber * 360) / PI }, this@RadialMenu.theta ) ) } } private fun getX(origin: Double, angle: Double, radius: Double) = origin + (radius * sin(angle)) private fun getY(origin: Double, angle: Double, radius: Double) = origin - (radius * cos(angle)) private fun xBinding(angle: ObservableDoubleValue, radius: ObservableDoubleValue) = Bindings.createDoubleBinding({ getX(originX.value, angle.get(), radius.get()) }, angle, radius, originX) private fun yBinding(angle: ObservableDoubleValue, radius: ObservableDoubleValue) = Bindings.createDoubleBinding({ getY(originY.value, angle.get(), radius.get()) }, angle, radius, originY) private fun createShape(itemNumber: Int) = Path().apply { styleClass += \u0026#34;wedge\u0026#34; elements += MoveTo().apply { xProperty().bind(xBinding(theta.multiply((itemNumber * 2) - 1), outerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) - 1), outerRadiusProperty)) } elements += ArcTo().apply { radiusXProperty().bind(outerRadiusProperty) radiusYProperty().bind(outerRadiusProperty) isSweepFlag = true isLargeArcFlag = false xProperty().bind(xBinding(theta.multiply((itemNumber * 2) + 1), outerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) + 1), outerRadiusProperty)) } elements += LineTo().apply { xProperty().bind(xBinding(theta.multiply((itemNumber * 2) + 1), innerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) + 1), innerRadiusProperty)) } elements += ArcTo().apply { radiusXProperty().bind(innerRadiusProperty) radiusYProperty().bind(innerRadiusProperty) isSweepFlag = false xProperty().bind(xBinding(theta.multiply((itemNumber * 2) - 1), innerRadiusProperty)) yProperty().bind(yBinding(theta.multiply((itemNumber * 2) - 1), innerRadiusProperty)) } toBack() } } The Application # Here\u0026rsquo;s the code for my sample application that draws the RadialMenu:\nclass RadialMenuApplication : Application() { override fun start(stage: Stage) { val scene = Scene(createContent { Platform.exit() }, 640.0, 500.0).apply { addStyleSheet(\u0026#34;/css/RadialMenu.css\u0026#34;) addWidgetStyles() } stage.title = \u0026#34;RadialMenu Demo\u0026#34; stage.scene = scene stage.show() } private fun createContent(exitAction: () -\u0026gt; Unit) = BorderPane().apply { val menu = RadialMenu( RadialMenuModel( \u0026#34;This is the first\u0026#34;, FontIcon(\u0026#34;captainicon-049:52:BISQUE\u0026#34;), { println(\u0026#34;First item selected\u0026#34;) }), RadialMenuModel( \u0026#34;This is the second item in the menu\u0026#34;, FontIcon(\u0026#34;captainicon-176:52:BISQUE\u0026#34;), { println(\u0026#34;Second item selected\u0026#34;) }), RadialMenuModel(\u0026#34;Cut!\u0026#34;, FontIcon(\u0026#34;captainicon-238:52:BISQUE\u0026#34;), { exitAction.invoke() }) ) menu.addMenuItem( RadialMenuModel( \u0026#34;This is the third\u0026#34;, FontIcon(\u0026#34;captainicon-066:52:BISQUE\u0026#34;), { println(\u0026#34;Third item selected\u0026#34;) }), RadialMenuModel( \u0026#34;This is the Fourth\u0026#34;, FontIcon(\u0026#34;captainicon-039:52:BISQUE\u0026#34;), { println(\u0026#34;Fourth item selected\u0026#34;) }), RadialMenuModel( \u0026#34;This is the fifth\u0026#34;, FontIcon(\u0026#34;captainicon-154:52:BISQUE\u0026#34;), { println(\u0026#34;Fifth item selected\u0026#34;) }), RadialMenuModel( \u0026#34;This is the Sixth\u0026#34;, FontIcon(\u0026#34;captainicon-271:52:BISQUE\u0026#34;), { println(\u0026#34;Sixth item selected\u0026#34;) }, true ) ) center = menu padding = Insets(50.0) styleClass += \u0026#34;wrapper-region\u0026#34; } } fun main() { Application.launch(RadialMenuApplication::class.java) } I used the Ikonli package which gives excellent integration with FontAwesome for JavaFX. This gives FontIcon, which is an extension of Text that shows icons. There\u0026rsquo;s lots of good icons spread across several dozen icon packs made by various people. I used the Captionicon pack for this project.\nConclusion # I felt like this was a fun project, even if it isn\u0026rsquo;t really all that useful for a typical CRUD type business application. It\u0026rsquo;s a lot more about graphical drawing and layout than it is about functionality, but it has some interesting concepts that are worth learning about.\nAs an example of how to build a custom control by extending Region, I think it also has some merit. In fact, the extreme focus on the graphics shows that even this kind of approach can lend itself to creating a general purpose control that you can fit into any kind of application.\nHow to Implement the RadialMenu # This is different from most other kinds of controls because it\u0026rsquo;s intended to be superimposed over top of the other elements in the window, take over the screen and then disappear when an item is selected.\nSo, how do you do that?\nMy approach would be to put the main content pane of the application and the RadialMenu into a StackPane, with the RadialMenu added last and not visible. Whatever action in the main application would make the toggle the isVisible flag of the RadialMenu to true, and any action from the menu would toggle it back to false. You\u0026rsquo;d probably want to set the PickOnBounds property of the RadialMenu to true so that items under the RadialMenu in the main application screen are disabled.\n","date":"4 March 2024","externalUrl":null,"permalink":"/javafx/techniques/custom_controls/radial-menu/","section":"JavaFX","summary":"How to build a radial menu in JavaFX.  Maybe not the most useful idea itself, but building this custom component lets us take a look at some more complex layout challenges.","title":"Custom Control:  Radial Menu","type":"javafx"},{"content":" Introduction # I think most programming taught in schools and courses concentrates on creating working code. Beginning programmers are left with the impression that creating code that runs and gives the correct result is the hard part; that this is somehow the end goal of software development.\nIn fact, writing code that runs and give the correct result is usually trivially easy, especially once you have some experience. The real challenge involves writing code that programmers can work with; potentially programmers other than you, and potentially for years to come. Far too many systems go into production simply because they work, but nobody has thought about the cost of keeping it working as the requirements change over time.\nWhat makes a system difficult to understand and maintain?\nOf all the considerations when designing or coding a system, coupling is probably the most important to keep in mind. Conversely, tight coupling is the number one issue that makes systems hard to understand and maintain.\nCoupling is the big scary thing that you need to deal with as you type every line of code.\nWhat is coupling? # From Wikipedia:\n\u0026hellip;coupling is the degree of interdependence between software modules; a measure of how closely connected two routines or modules are; the strength of the relationships between modules.\nThe problem with coupling is that it makes it very difficult to do two things:\nUnderstand a System When a system has a great deal of interdependence between elements, then it becomes more necessary to understand how all of those elements work in order to understand any one element. Modify, Debug and Extend a System When there is tight coupling between elements, then changes to any one element start to have \u0026ldquo;ripple effect\u0026rdquo; through the whole system. The first change alters the way some other element behaves, and correcting that impacts another element and so on. Eventually, making any change to the system becomes a test of nerves. For this discussion, we\u0026rsquo;re going to put aside all the technical definitions of coupling that you can read about on Wikipedia because\u0026hellip; you can read about them on Wikipedia. Instead, we\u0026rsquo;ll try to focus on practical things that cause coupling, and aspects of writing good code that reduce coupling.\nCoupling and \u0026ldquo;Clean Code\u0026rdquo; # You\u0026rsquo;ve probably heard the term \u0026ldquo;Clean Code\u0026rdquo;, and maybe you have an idea about what it means. If you want to know a lot more, then check out Bob Martin\u0026rsquo;s Clean Coders, and maybe watch some of his videos. They are really quite good.\nA lot of the clean coding concepts involve the SOLID principles. Uncle Bob talks about these a lot.\nI\u0026rsquo;m going to go out on a limb, and I\u0026rsquo;m going to say the main purpose of most of the techniques that we generally consider to be \u0026ldquo;clean code\u0026rdquo;, are aimed a limiting coupling. This includes the SOLID principles, DRY (Don\u0026rsquo;t Repeat Yourself), and an awful lot of what Uncle Bob presents in his videos. He doesn\u0026rsquo;t explicitly present it as such, but when you think about what \u0026ldquo;Clean Coding\u0026rdquo; achieves, you\u0026rsquo;ll see that loose coupling is a big outcome from it.\n\u0026ldquo;Hiding\u0026rdquo;, \u0026ldquo;Revealing\u0026rdquo; and \u0026ldquo;Knowing\u0026rdquo; # In many cases, the best way to think about coupling is in terms of \u0026ldquo;hiding\u0026rdquo;, \u0026ldquo;revealing\u0026rdquo; and \u0026ldquo;knowing\u0026rdquo;. You\u0026rsquo;ll see these terms used to explain coupling over and over again in the rest of this article. Let\u0026rsquo;s take a look at what they mean for coupling:\nHiding and Revealing This is the fundamental mechanism of loose coupling. Elements of your system should hide as much as they can about themselves so that other elements cannot abuse them. An element should reveal only as much about itself as it absolutely has to in order to fulfil its role. Knowing This is about how much code in one element uses its knowledge of the inner workings of some other element to do its work. As soon as you use that knowledge, you\u0026rsquo;ve instantly created a dependency to the insides of that other element. \u0026ldquo;Coupling\u0026rdquo; tends to be an abstract term, and a little tougher to think about in practical terms. But if you think in terms of hiding as much as possible in every design decision, revealing as little as you can get away with, and then try not to write code that depends on knowing about the insides of other things in the system, then it\u0026rsquo;s easier to write better code.\nTypes of Coupling # Let\u0026rsquo;s take a practical look at the types of coupling that you\u0026rsquo;re likely to create in your code\u0026hellip;\nCode Cohabitation # The easiest way to create coupling is to put too much code in one place. The so-called \u0026ldquo;God Classes\u0026rdquo; are an example of this; putting virtually all of your functionality into a single class. You also see this frequently within methods. Whenever I see a method that\u0026rsquo;s 200 lines long, or 100 lines or even 30 lines long I know that there\u0026rsquo;s too much going on inside.\nThis is coupling because the various functions that the method does are interleaved with each other, sharing variables and space on the screen. It\u0026rsquo;s hard, sometimes even impossible to figure out which code belongs to what, sometimes it\u0026rsquo;s shared by multiple functions.\nWhat\u0026rsquo;s the problem with this?\nScope of Variables # It is much harder to keep track of variable scope within a method than it is between methods. If a variable is declared in one method, it\u0026rsquo;s not going to be available in another method unless it\u0026rsquo;s explicitly passed as a parameter. Within that method, though, any variable that\u0026rsquo;s declared outside a control block - most likely a loop or conditional branch - is global within that method. So, if you have a 200 line method, and you declare some variable near the top of that method, then you have to understand every use of that variable throughout the entire method - especially if the variable is mutable - in order to understand how it\u0026rsquo;s used at line 190 of the method.\nUnderstanding the Code # This is the main problem with long methods. For the most part, you need to understand the entire method in order to understand part of the method. Let\u0026rsquo;s say that you\u0026rsquo;re interested in some loop part-way through the method. Where did the data that\u0026rsquo;s being iterated over come from? Was it mutated in any way before this loop? Was it filtered somewhere? What happens to the data in this loop after the loop is finished? How will changes to this loop affect anything that happens with this data after the loop? What about that control variable? Where did it come from? Can you change it?\nWhen you are maintaining monolithic blocks of code there\u0026rsquo;s generally just one self-defence method that you can adopt: Leave as small a footprint on the code as you can while achieving your maintenance goal. There\u0026rsquo;s very little latitude to make bigger changes short of refactoring the entire method into smaller methods - which is hideously risky unless you totally understand every nuance of the method to start with. Every change you make carries the risk of some side effect that you didn\u0026rsquo;t anticipate\u0026hellip;because the code is coupled so tightly simply by cohabiting in the same method.\nUsing the Single Responsibility Principle # Most of these code cohabitation issues can be avoided by using the Single Responsibility Principle: The idea that each method should be responsible for only one single thing.\nA lot of programmers seem to give up on the Single Responsibility Principle (SRP) because it seems impossible. Surely, at some point some method has to be responsible for more than one thing, or else no application could ever do more than one thing, right?\nThe Single Responsibility Principle refers to direct responsibility. Delegated responsibilities don\u0026rsquo;t count.\nIf you have a method that needs to do three things and combine the results, just delegate those three things to separate methods and do the combination work in your primary method. If you follow this process, you\u0026rsquo;ll end up with methods that just contain flow logic and, because that\u0026rsquo;s all they do, it becomes very clear what that flow logic is.\nJust remember that combining results from delegated methods is a responsibility, and control flow is also a responsibility. So, if you are doing both in one method then you\u0026rsquo;re technically breaking SRP. Of course if either one is trivial, it\u0026rsquo;s probably not going to be an issue.\nWhen you\u0026rsquo;ve delegated the responsibilities to separate methods, the coupling is broken. Code in one method can be changed without worrying that code in another method will be broken. Of course, if the methods have other coupling, such as mutating class fields, you\u0026rsquo;ll still need to worry about that.\nToo Many Fields and Instance Variables # Within a class, fields (or instance variables, if you like) are globally accessible. Generally speaking, you shouldn\u0026rsquo;t use fields to simply provide a global variable.\nWhen should you use fields?\nFields are best used to represent the \u0026ldquo;state\u0026rdquo; of the object. This state persists, in memory, outside the context of running a method in the class. That\u0026rsquo;s really what they are for. That\u0026rsquo;s what you should use them for.\nI have seen cases where fields are introduced to limit the number of parameters that are passed between methods in a class. Sometimes it can seem to make the class \u0026ldquo;cleaner\u0026rdquo; because you\u0026rsquo;re not passing around a bunch of parameters from private function to private function. However, that technique carries a lot of baggage.\nWithin the scope of a class, a field is essentially a global variable. Instantly this creates an extreme amount of coupling between the methods in your class. Any method can access a field and use it any way that it wants, effectively altering the meaning of the field since the meaning is defined by the way that it is used. The only way to know what\u0026rsquo;s going on with a field is to understand every place that the field is used.\nGlobal variables are bad enough, but fields are persistent global variables. When you have a local variable, you declare, create and initialize it, and you know its status. If you pass it to another method, you know its status in both places. However, a field is declared, created and (usually) initialized when the object is instantiated - and that\u0026rsquo;s the only time you can take its status for granted. Any public method that accesses the field or calls private methods that access the field could be called at any time, and could change the field. You don\u0026rsquo;t have any control over this.\nNon-Private Methods # Non-private methods are the number one way that classes in Java reveal things about themselves to other classes. Every single non-private method that you add to a class ties you down just a little more because it exposes your class to potential coupling.\nGetters and Setters # Everybody knows that you\u0026rsquo;re supposed to make all fields private, and to provide getters and setters for those fields so that other classes can have access to the data. This is a strategy against coupling - those other classes know that they can get the data, but they don\u0026rsquo;t have access to the implementation of that data inside the class. These leaves you free to change the internal structure of the class without worrying about breaking other classes.\nReally though, you need to think of getters and setters in terms of services. What is the service that each getter or setter provides? What are other classes going to do with the data they get from a getter? Do they actually need a setter? What do you want to reveal about your class to other classes through the getters and setters?\nThe point to this is that getters and setters should be designed thoughtfully, not just tossed into a class because you have fields.\nAn Example: Names # Let\u0026rsquo;s say we have a class that has some name fields: firstName and lastName, and they\u0026rsquo;re just Strings. Now we need to provide access to that data. Do we just add a getFirstName() and getLastName() method to the class? What are the calling methods going to do with the data from getters? It\u0026rsquo;s OK to ask this. We\u0026rsquo;re providing a service through these getters, and we have a right to set boundaries around it. We could just provide getName() and have that method put firstName and lastName together with a space between them. Maybe that\u0026rsquo;s the limit of how far we\u0026rsquo;re willing to go.\nMake no mistake, providing getFirstName() and getLastName() is going to tie us down somehow. What happens if we decide to add a middleName field to our class? Are we going to add a getMiddleName() method to our class? Then do we need to chase down all the calling classes and modify them? What if we add a field for suffix, like \u0026ldquo;Jr\u0026rdquo;, \u0026ldquo;Sr\u0026rdquo;, \u0026ldquo;II\u0026rdquo;? How do we cope with that?\nCentral to all these questions is the idea of what class has the responsibility of putting the name in a format for use elsewhere? You could make a very strong argument that it\u0026rsquo;s our class with the data that has that responsibility. Perhaps we need to provide a getName() method as described above. Maybe we need to have a getFullName() that uses all the parts. Perhaps a getCommaName() that produces something like \u0026ldquo;Smith, Fred\u0026rdquo; would be needed.\nWhat happens if we decide to create a special Name class, and use it instead of our String fields? Now we can handle all the funky edge cases, all the exceptions and rare cases like a multi-word last names (like \u0026ldquo;Van Der Kamp\u0026rdquo;), and proper casing of names like \u0026ldquo;McDonald\u0026rdquo; or \u0026ldquo;O\u0026rsquo;Brian\u0026rdquo; and those kinds of things. And we can use this class anywhere that we have a name. Now you really don\u0026rsquo;t want all these calling methods using their own rule-base for formatting names.\nWith something like names, they don\u0026rsquo;t change very often. Do we need to have setters for these fields? Probably not.\nNow let\u0026rsquo;s be clear, all of this stuff is really hard if you\u0026rsquo;ve implemented your class with getFirstName() and getLastName(). Really hard. But it\u0026rsquo;s fairly easy if you just implemented getName() and let your class figure out how to deliver it.\nCoupling Through Getter Types # The other aspect of getters that shouldn\u0026rsquo;t just be written without thought is the return type. I\u0026rsquo;d be willing to bet that 99% of the time, programmers just return the same type as the field in their getters. But is this always the right thing?\nLet\u0026rsquo;s say you\u0026rsquo;ve got a field in your class age, and it\u0026rsquo;s just an int. Should you create a getter, getAge() that returns int? What are the units? Years? OK, let\u0026rsquo;s say it\u0026rsquo;s years. What if you decide that you need to track closer than integer years, so you\u0026rsquo;ll make it a double? Now you\u0026rsquo;re stuck.\nMaybe you should make getAge() return Number. Now the calling programs can use it as they want. You\u0026rsquo;ll still return an actual int, but it will be returned as Number.\nMaybe you should make getAge() return Duration. There\u0026rsquo;s no ambiguity in this. Duration has all the unit stuff baked in. You can still treat the data internally as int, but you can convert this to the number of years in a Duration value.\nThe important thing is that it may make sense to treat age as an int internally inside your class. Your code inside the class knows all about it and can deal with appropriately as an int. If it causes grief in your code, it\u0026rsquo;s going to be contained inside your class, and it won\u0026rsquo;t be an application-wide problem. But if you let that int leak out through the getter, then it becomes an application-wide problem. And should you decide to fix it by making it a Duration then that\u0026rsquo;s going to have a ripple effect through your application.\nOnce again, treat your getters like a service, not a data-access mechanism. Treat them like a service from the start.\nOther Methods # I\u0026rsquo;ve seen people express the opinion that every class should implement an Interface, and while I think that\u0026rsquo;s extreme, I do think that programmers tend to under-utilize interfaces. Having an Interface formalizes the contract about what a specific class is going to expose to the outside world, and it unlocks the client code from using a specific implementation of that contract.\nLet\u0026rsquo;s say that you have some class, ClassA, that does a number of related functions, and that you have some client class that invokes a few of ClassA's methods to do some of those things. The client code doesn\u0026rsquo;t care about the other functions that your ClassA does, just the ones that it calls. Also, your client code passes ClassA around to a number of methods as a parameter.\nNow, let\u0026rsquo;s image that something about the module that ClassA belongs to changes. ClassA will remain, but, going forward, is only going to be used in special circumstances that don\u0026rsquo;t include your client code. You need to use ClassB instead. That means that you\u0026rsquo;re going to have to change your client code, changing the type of every parameter where ClassA was passed from ClassA to ClassB.\nThis is all because of coupling.\nImagine that you had an Interface for the few functions of ClassA that your client code uses, let\u0026rsquo;s call it ClientInterface. Now you instantiate ClassA as ClientInterface and then pass it around as an instance of ClientInterface. When ClassB is created, it also implements ClientInterface and the only code you have to change is:\nClientInterface workerObject = new ClientA(); to:\nClientInterface workerObject = new ClientB(); This works even better if there\u0026rsquo;s some method in that service module that returns ClientInterface for you without your client code even knowing what implementation of ClientInterface is being returned.\nThe key thing here is, as usual, all about hiding, revealing and knowing. Using an Interface lets your service classes hide as much as possible, reveal only the required methods, and prevents the client code from knowing anything more about the service than it absolutely needs to.\nAccessing the Implementation Instead of the Interface # When I first started to program in Java, we used ArrayList all over the place. It was easy to use, and we passed and returned values as ArrayList throughout a lot of our code. Then Java 8 came along and we got Streams! Streams were awesome, but\u0026hellip;\nStream.toList() returns List - which is an Interface - and not ArrayList. Now, we started using Streams and returning List instead of ArrayList and then passing the results around between methods. But all those methods expected ArrayList!. There was nothing in any of this code that used any of the methods of ArrayList that weren\u0026rsquo;t in the List interface, but we now had to do a major refactoring to change all of our parameters and return values to List.\nThat was the big learning moment about coupling. We\u0026rsquo;d update a service method to use Streams and return a List. Then we\u0026rsquo;d get red squiggly lines all over our project. We\u0026rsquo;d chase those down, and then we\u0026rsquo;d get a whole new set of red squigglies as the methods that called those methods had problems. And so, on, and so on\u0026hellip;\nThis was a newbie mistake, but illustrates another level of coupling. None of the calling methods had any need to know that the returned Lists were ArrayList, but we chose to reveal that in our return values and then use that knowledge in our calling routines. Then, when we changed the inner workings of those methods to something that didn\u0026rsquo;t involve ArrayList we had that ripple effect throughout the whole code base. And somewhere in there, there probably was only one or two lines of code that actually tried to call an ArrayList method that isn\u0026rsquo;t in List.\nThis goes even further. Since all objects in Java exist in a hierarchy it\u0026rsquo;s often possible to return something further up the hierarchy than the actual object passed back by a method. Just so long as that parent object has whatever methods the code invoking it requires. For instance, if the only method that you want the invoking code to be able to call on a returned object is toString(), then you could just return Object. Perhaps more realistically, you could return Number instead of int, or double.\nThis happens a lot with Builder methods in JavaFX. Often, a Builder is returning a component that\u0026rsquo;s going to have nothing more done to it than to put it into a layout. In that case you don\u0026rsquo;t want to expose particular methods of the specific component classes. Like the setOnAction() method in Button. So you don\u0026rsquo;t return Button, you return Node which is pretty much the top level class for all the screen components. Sometimes you want the layout code to be able to set the maximum width of the return component, in which case you\u0026rsquo;d return Region which is a bit lower down than Node and is the top level class for all the layout container classes.\nThe big takeaway here is that you should give some careful thought to the values that return from your methods, and not just return whatever type is actually created inside the method code. Ask yourself about how much the calling routines should be able to know about the data that\u0026rsquo;s being returned and return an appropriate type.\nMutable Fields and Variables # I spent years and years working in various dialects of Pick BASIC. It\u0026rsquo;s pretty old-school. Structured with GOSUB but no formal \u0026ldquo;method\u0026rdquo; or \u0026ldquo;function\u0026rdquo; declarations with passed parameters. Variables were all untyped and without any scoping. The implications of all of this are that every variable is global, even if in a subroutine, and you can put just about anything in any variable at any time without any restrictions.\nYou could have things like this:\nX = \u0026#34;25\u0026#34; . . X = (X : \u0026#34;17\u0026#34;) + 1 . . X = \u0026#34;GEORGE\u0026#34; And it would work just fine. BTW: X = (X:\u0026quot;17\u0026quot;) + 1 would return 2518 (the : was the concatenation operator and X would be treated as a String for that).\nIt wasn\u0026rsquo;t unusual to have program files with 3K to 5K lines of code and dozens of subroutines, and any variable used anywhere was available anywhere else. The result was that any attempt at maintenance was a test of nerve. It was very hard to know if any particular variable had the value that you expected it at any given time because literally any of the code could modify it. And some variables were updated dozens of times in dozens of different places. It was also very hard to know what would happen if you changed a variable because, once again, it could be used in dozens of places.\nIt\u0026rsquo;s all due, of course, to coupling. Any code that uses a global variable is tightly coupled to every other piece of code that uses the same global variable. Actually, it\u0026rsquo;s coupled to all the other code in the file because you have to go searching to find all the uses - any code anywhere could use anything!\nMuch of this would be a non-issue if variables could be made non-mutable. Non-mutable variables can\u0026rsquo;t be changed, so you don\u0026rsquo;t have to pour over hundreds of lines of code looking for it on the left side of an equals sign.\nKotlin takes this to a new place in the Java world. Variables and fields in Kotlin need to be declared as mutable or immutable using the var or val keywords, with the convention being to prefer immutable whenever possible. In the Kotlin code I\u0026rsquo;ve written, about 90%+ of my variables are immutable.\nIn Java, we have final and you should use it whenever you can. Prefer local variables over instance variables whenever possible too. And don\u0026rsquo;t make variables static unless absolutely necessary.\nOf course, final isn\u0026rsquo;t used anywhere as often as it should be. It\u0026rsquo;s rare to find local variables declared as final in most code. Programmers only think about final variables when they use one in a lambda, and they get that compiler warning about \u0026ldquo;variables in a lambda must be final or effectively final\u0026rdquo;. Even then, the usual response it to look for a wrapper class like Atomic to put the value in to get around the final requirement.\nDuplicate Code # Most programmers don\u0026rsquo;t think about duplicate code as coupling, but it really is. Really, what could be more \u0026ldquo;interdependent\u0026rdquo; about two blocks of code than if they contain the same code?\nI\u0026rsquo;ve seen people argue that you can overdo DRY (\u0026ldquo;Don\u0026rsquo;t Repeat Yourself\u0026rdquo;). The idea being that if you\u0026rsquo;ve pulled the duplicate code out into a separate method and call it instead of having duplicated code, then any time you touch that method you have to worry about all the places that might call it.\nThis is nonsense. Not that you have to worry about all the places that call it, but the idea that if it\u0026rsquo;s duplicated then you don\u0026rsquo;t.\nIf you touch a piece of code that\u0026rsquo;s duplicated, then you have to ask yourself, \u0026ldquo;Do I need to make this same change in all the duplications?\u0026rdquo; The tricky part is that with duplicate code, a number of things might happen:\nYou Cannot Find, or You\u0026rsquo;re Not Aware of the Duplicates If you have a method, then it\u0026rsquo;s super easy with modern IDE\u0026rsquo;s to find all the places that the method is called. It\u0026rsquo;s hard NOT to be aware of how a method is used. But with duplicate code you don\u0026rsquo;t have that facility, so you have to go looking. And why would you even look? It\u0026rsquo;s Hard to Understand the Impact on Duplicate Code Very often, duplicate code isn\u0026rsquo;t 100% identical in every place that you encounter it. There\u0026rsquo;s always a different value checked, or a calculation is slightly different, or something like that. When you touch code that\u0026rsquo;s been duplicated you have to figure out whether you\u0026rsquo;re touching the stuff that really is duplicated. When you pull it out to a separate method, you generally parametrize the differences, so that the calls to the method hold the differences, not the duplicated code. Now, when you make a change, are you making a change to the caller, or are you making a change to the method? You Mess Something Up There is probably no more effective source of trivial bugs than duplicated code and copy/paste. If you have to do something 5 times over, each just a tiny bit different, then that\u0026rsquo;s 5 times as many chances to make a mistake. One other important point about DRY: Employing DRY generally forces you to think about the structure of what you\u0026rsquo;re building. It makes you understand that you have shared functionality in your system and requires that you identify it and organize it. DRY removes some amount of coupling, but it also brings it out into the open and puts structure around it that makes it easier to manage.\nFeature Envy # This really comes down to putting code in the right place. One antipattern that you see all the time is something like this:\nprivate int sampleMethod(ClassA classA, int somethingElse) { return (classA.getValue1() * classA.getValue2()) + (somethingElse * classA.calculateSomething()); } What we have here is a block of code that repeatedly calls methods in another class to derive a result based almost exclusively on those method calls. In other words, this code really has very little to do with the class in which it resides.\nThis is called \u0026ldquo;Feature Envy\u0026rdquo;. In this example, sampleMethod() represents a feature in the class in which it appears but really shouldn\u0026rsquo;t belong to it. It should be a feature of ClassA.\nA far better way to write this:\nprivate int betterMethod(ClassA classA, int somethingElse) { return classA.sampleMethod(somethingElse); } And ClassA.sampleMethod() would contain the calculation from the first code snippet.\nFrom a coupling perspective, we\u0026rsquo;ve now exposed only one non-private method of ClassA, instead of 3. So that\u0026rsquo;s a win. But almost certainly, the meaning of that calculation is something that relates to ClassA, not whatever class it was originally found in.\nAnd it\u0026rsquo;s clear that if something about ClassA changes, maybe the meaning of whatever is returned from one or both of the two getters, for instance, then the meaning of the result of that calculation changes. If it\u0026rsquo;s contained within ClassA, however, then that change can be handled inside ClassA without needing to worry about a ripple effect.\nBreaking The Law of Demeter # This is a slam dunk because the \u0026ldquo;Law of Demeter\u0026rdquo; is, by definition, a rule about loose coupling.\nThe most common example of the Law of Demeter is when you have stacked object composition. For instance, you have a field in a class which is another class, and that class in turn has a field which is another class and so on. The result is that you can get situations like this:\nsomething = classA.getField1().getField6().getField3().getField2(); Now our code here has to know not only that classA has getField1(), but it has to know that whatever type getField1() returns has a getField6() method. And then, of course, that that class has a getField3() method and so on. But if the object returned by classA.getField1() isn\u0026rsquo;t just a utility class like ArrayList or Map, is there any good reason for our code to know that classA has whatever is returned by classA.getField1() in it?\nAnd, of course, if anything changes in any of those 4 classes, this might stop working.\nFrom the perspective of coupling, it\u0026rsquo;s far better to have a single call:\nsomething = classA.getSomething(); Where:\npublic classA; . . public SomeClass getSomething(){ return field1.getSomething(); } } and:\npublic classXYZ; . . public SomeClass getSomething() { return field6.getSomething(); } } Of course, it needn\u0026rsquo;t be called getSomething() all the way down the chain.\nNot Using Packages Properly # You should understand that packages in Java aren\u0026rsquo;t just a way to organize your classes and break up the name-space. Packages are all about hiding, revealing and knowing - the elements of reducing coupling.\nClasses that share the same package inherently share an increased coupling to one another. This should represent a high level of \u0026ldquo;cohesion\u0026rdquo; in your system, where elements that share a package are highly related to each other. Cohesion is a good thing, and packages that have high cohesion have more latitude for coupling between classes without causing issues.\nThis is reflected in the access modifiers. package-private is the default modifier for Java elements, and it\u0026rsquo;s what you get if you don\u0026rsquo;t specify anything at all. An element with package-private means that it is exposed to other classes in the same package. If your package has high cohesion, this can often be acceptable.\nOn the other hand, if you\u0026rsquo;ve only got one package in your application, and all of your classes are in that one package, then your cohesion is zero and there\u0026rsquo;s no difference between package-private and public. Organization of your packages so that they have high cohesion allows you to say, \u0026ldquo;It\u0026rsquo;s okay if this bunch of classes has access to this method, even though I don\u0026rsquo;t want any other, unrelated classes calling it\u0026rdquo;.\nConclusion # Over the decades that I\u0026rsquo;ve been programming, I\u0026rsquo;ve read, and tried to understand, literally millions of lines of code written by other people. Sometimes the \u0026ldquo;other people\u0026rdquo; was me from a year earlier. It\u0026rsquo;s a core programming skill, and I think I\u0026rsquo;m pretty good at it, better than a lot of programmers I\u0026rsquo;ve worked with.\nWhen I open up a project and take a first look at it, I can tell in a few moments how experienced the programmers that wrote it were, and I can tell how easy or difficult it will be to maintain. How do I know? I look for all the stuff that\u0026rsquo;s written about in this article. If I see a class with a zillion fields with two letter names, none of them final, one or two giant methods, and everything declared public then I know it\u0026rsquo;s going to be rough going. And it\u0026rsquo;s going to be rough going because I know that all the other coupling rules are going to be broken, over and over and over again.\nCoupling is the number one challenge when understanding and maintaining a system. It\u0026rsquo;s a challenge for understanding because it means that in order to understand one tiny part of a system you need to understand large amounts of that system. It\u0026rsquo;s a challenge for maintaining a system because you cannot touch one tiny part of the system without possibly impacting large amounts of that system.\nThe nice thing about coupling is that you don\u0026rsquo;t have to think about it in an abstract way to avoid it. Just think about every element of your system hiding its secrets from every other part of the system and keeping its nose out of how those other parts work.\nMost of all, you can make huge steps towards reducing coupling by following DRY, SRP and avoiding feature envy. And do it from the start. Don\u0026rsquo;t treat it as a step to take later - because you probably won\u0026rsquo;t get a chance. And honestly, it doesn\u0026rsquo;t take any longer to write good, loosely coupled code, than it does to write bad, tightly coupled code.\n","date":"9 February 2024","externalUrl":null,"permalink":"/programming/coupling/","section":"Programming","summary":"Introduction # I think most programming taught in schools and courses concentrates on creating working code. Beginning programmers are left with the impression that creating code that runs and gives the correct result is the hard part; that this is somehow the end goal of software development.\n","title":"It's All About (Loose) Coupling","type":"programming"},{"content":" Introduction # Everybody has this problem when they start out. There\u0026rsquo;s an image, property, FXML or CSS file that they want to load into its appropriate JavaFX object, and all they can get is some variant of \u0026ldquo;Null Pointer Exception\u0026rdquo;.\nNo matter what they do. It\u0026rsquo;s maddening.\nIf they are on the right track, they\u0026rsquo;re at least using Object.getResource(). Inevitably, however, they get lost trying to figure out where they should put the file so that Object.getResource() can find it. It\u0026rsquo;s extra frustrating, because Object.getResource() has no facility to tell you where it looked for the file. If it can\u0026rsquo;t find it, it just returns Null, which doesn\u0026rsquo;t seem very enlightening, and certainly not useful.\nSo where does Object.getResource() look for the file?????\nThat\u0026rsquo;s what we\u0026rsquo;re going to talk about here.\nWhat\u0026rsquo;s With the Resources, Anyway? # Java applications are generally designed with the intention that you\u0026rsquo;re going to bundle them up into a Jar file and distribute them to client devices. That Jar file needs to be complete, and to have all the classes and files required to run the application. To that end, Java provides a mechanism to include files into a Jar file as resources, and methods to locate them at runtime.\nIn order to have your programs find your resources, you need to put those files in the correct place in your Jar file. This is 2024 (when I wrote this), and you should be using some kind of tool to manage your applications as projects. A good IDE is as start, but it\u0026rsquo;s even better if you use a build engine like Gradle or Maven to handle the actual build.\nIf you use one of these tools, and you put your files in the correct place in your source structure, then your resources files will be where they need to be at runtime, and your programs will find them.\nProject Organization # First off, I\u0026rsquo;m going to assume you\u0026rsquo;re using some kind of build engine like Gradle, Maven or (gasp!) Ant. If you\u0026rsquo;re not, you should be as these handle all your dependencies and organize your project in a nice predictable way. To keep things simple, I\u0026rsquo;m going to talk specifically about projects built with Gradle, but Maven is also very similar.\nOn the Source Side # Gradle will expect that your project is divided into Source Sets. A typical project will have a \u0026ldquo;main\u0026rdquo; Source Set, and a \u0026ldquo;test\u0026rdquo; Source Set. Your production code and resources would reside in the \u0026ldquo;main\u0026rdquo; Source Set, while unit tests and related resources would go in the \u0026ldquo;test\u0026rdquo; Source Set.\nWithin a Source Set, the project is divided into code sections, and a \u0026ldquo;resource\u0026rdquo; section. It\u0026rsquo;s a best practice to have one code section for each programming language in your project. Most JavaFX projects will have a \u0026ldquo;java\u0026rdquo; code section, but they could also have a \u0026ldquo;kotlin\u0026rdquo; section or a \u0026ldquo;groovy\u0026rdquo; section. Inside each language section you will have folders that correspond to the individual packages in your project.\nThere will only be a single \u0026ldquo;resource\u0026rdquo; section in a Source Set. It\u0026rsquo;s really just a folder, and the files contained within it can be in whatever directory structure that the programmer wants to use.\nOn the \u0026ldquo;Build\u0026rdquo; Side # Part of the Gradle build process is to copy all the classes after they have been compiled into a directory structure starting with \u0026ldquo;build\u0026rdquo;. There will be a subdirectory called \u0026ldquo;classes\u0026rdquo; and all the compiled classes will be found in there. There will also be a subdirectory called \u0026ldquo;resources\u0026rdquo;. Under that there will be a subdirectory each for \u0026ldquo;main\u0026rdquo; and whatever other Source Sets are built. Under these subdirectories will be folders that follow the structures that were used in the \u0026ldquo;resource\u0026rdquo; folders of each Source Set on the source side.\nIt is possible to configure a Gradle build to do all kinds of wild and wonderful things when copying the files from source to build. Files can be selectively copied, renamed, moved to different locations. Files can be duplicated into different directories, duplicated and renamed - almost anything. We\u0026rsquo;re not going to worry about those things here, because if you\u0026rsquo;re doing that in your Gradle build, you\u0026rsquo;re probably not going to be reading this article.\nWhat This Means # The biggest thing to keep in mind about all of this is that your code is going to run against the file and folder structure in the \u0026ldquo;build\u0026rdquo; subdirectory. This means that the real source of truth for how you should be looking for your resources is contained in the build subdirectory. If you are using an IDE like IntelliJ IDEA, you\u0026rsquo;ll be able to see the build directory right in your project structure window, and you can expand that folder and see exactly where Gradle has put your resource files.\nIf you\u0026rsquo;re stuck, then look in the build directory to see where your resources really are.\nLet\u0026rsquo;s take a look at the structure of my WidgetsFX project:\nThis is IntelliJ IDEA, and the orange part is the /build section of the project, rebuilt every time that Gradle does a build (or other operations). You can see how the files have been copied over. Perhaps the biggest thing to note is that /resources and /main have been inverted. We have /src/main/resources on the source side, and /build/resources/main on the build side.\nLet\u0026rsquo;s look at the Jar file that Gradle creates:\nHere you can see that all three of my CSS files are placed into /ca/pragmaticcoding/widgetsfx/css. There\u0026rsquo;s no main or resources anywhere. Could it be that these resources are just mingled in with the classes in their respective package folders? There are no classes defined in ca.pragmaticcoding.widgetsfx, but ca.pragmaticcoding.widgetsfx.layouts.buttons has some. Let\u0026rsquo;s add a CSS file to that package to see what happens:\nI\u0026rsquo;ve regenerated the Jar file, so we can have a look inside:\nSure enough, we now have button.css in our Jar file, right beside the class files that the build created for that package. This is also why, if you look any of this up on the Web, you\u0026rsquo;ll find lots of mentions of \u0026ldquo;classpath\u0026rdquo; in the explanations. It doesn\u0026rsquo;t look so much like classpaths in the resources folder, but when you peek into the Jar file you\u0026rsquo;ll see how the resources share the same classpath as the classes.\nNow we have a better idea about what resources are, why we need them and how they get deployed in our IDE and our Jar files, let\u0026rsquo;s look at how we access them.\nFinding Resources # This is where the trouble starts. You look up getResource(), and it\u0026rsquo;s in some class called Class.\nIt turns out that every class that you define has some (kind of) static field of the type Class, and you can even call Object.getClass() to get an instance of it. And Class has its own methods! One of those methods, it turns out, is Class.getResource().\nIf you look at the JavaDocs for Class.getResource() you\u0026rsquo;ll find this description:\nQuote Finds a resource with a given name. The rules for searching resources associated with a given class are implemented by the defining class loader of the class. This method delegates to this object\u0026rsquo;s class loader. If this object was loaded by the bootstrap class loader, the method delegates to ClassLoader.getSystemResource(java.lang.String).\nClass loader? What\u0026rsquo;s that?\nGood grief! This is getting complicated, from the JavaDocs for ClassLoader we have:\nQuote A class loader is an object that is responsible for loading classes. The class ClassLoader is an abstract class. Given the binary name of a class, a class loader should attempt to locate or generate data that constitutes a definition for the class. A typical strategy is to transform the name into a file name and then read a \u0026ldquo;class file\u0026rdquo; of that name from a file system.\nIt all seems very \u0026ldquo;meta\u0026rdquo;, but it\u0026rsquo;s actually not too bad if you take the time to read it slowly. It\u0026rsquo;s really just a class, like any other, and it has some method that locates your class file and does the actual instantiation of it into an object in memory. And just like any other class, it has methods that can be called, or even overridden in subclasses. One of those methods is ClassLoader.getResource(). The JavaDocs tell us that it will try to call its parent\u0026rsquo;s ClassLoader.getResource() first, otherwise it will use ClassLoader.findResource() to find the file.\nHowever, in the JavaDocs, ClassLoader.findResource() is a dead end, it just says:\nQuote Finds the resource with the given name. Class loader implementations should override this method to specify where to find resources.\nGreat. All that for nothing.\nLet\u0026rsquo;s go back to the JavaDocs for Class.getResource(), since it has some more information:\nQuote Before delegation, an absolute resource name is constructed from the given resource name using this algorithm:\nIf the name begins with a \u0026lsquo;/\u0026rsquo; (\u0026rsquo;\\u002f\u0026rsquo;), then the absolute name of the resource is the portion of the name following the \u0026lsquo;/\u0026rsquo;. Otherwise, the absolute name is of the following form:\nmodified_package_name/name Where the modified_package_name is the package name of this object with \u0026lsquo;/\u0026rsquo; substituted for \u0026lsquo;.\u0026rsquo; (\u0026rsquo;\\u002e\u0026rsquo;)\nAt this point, we at least have an idea what \u0026ldquo;before delegation\u0026rdquo; means - it\u0026rsquo;s going to pass the actual work on to a ClassLoader, but first it\u0026rsquo;s going to do something with our resource name.\nBefore it goes looking for the resource, it\u0026rsquo;s going to massage the resource location String that you pass to it. This really boils to one of two things:\nIf your resource name starts with a \u0026ldquo;/\u0026quot;\nWe\u0026rsquo;re done, just pass on the location exactly as it is.\nIf your resource name does NOT start with a \u0026ldquo;/\u0026quot;\nGet the package name of the Class, transform every \u0026ldquo;.\u0026rdquo; separating the sub-packages into \u0026ldquo;/\u0026rdquo; and then put it in front of the resource name, separated with another \u0026ldquo;/\u0026rdquo;.\nThis tells us everything that we need to know. By understanding how this method is going to pass our resource location on to the routine that actually finds it, we can control where we want to put our resources.\nSome Examples # First let\u0026rsquo;s look at the simplest example. An absolute path name starting with \u0026ldquo;/\u0026rdquo;:\n/myResource.xyz That\u0026rsquo;s going to be left untouched, the ClassLoader will be given:\n/myResource.xyz Now, let\u0026rsquo;s assume that we are calling this from com.example.package1.myClass, and we don\u0026rsquo;t use the leading \u0026ldquo;/\u0026rdquo;:\nmyResource.xyz This is going to result in the following passed to the ClassLoader:\n/com/example/package1/myResource.xyz Now, let\u0026rsquo;s look at two examples with paths, instead of just file names. First:\n/samples/myResource.xyz Which, unsurprisingly, will result in this passed to the ClassLoader:\n/samples/myResource.xyz And then, once again called from com.example.package1.myClass,\nsamples/myResource.xyz Will result in this passed to the ClassLoader:\n/com/example/package1/samples/myResource.xyz Calling it From Code: # So, how do you call Class.getResource()?\nIt\u0026rsquo;s pretty simple. However, most IDE\u0026rsquo;s will nag about possible NPE\u0026rsquo;s, so it\u0026rsquo;s cleaner if you handle the possibility that Class.getResource() will return Null in your code.\nRemember that getClass() is pretty much a static method as far as we are concerned. So you can call it that way.\nFrom Java # Generally, you won\u0026rsquo;t see anyone handling possible Null values from Class.getResource() properly in any examples on the Web. The usual implementation is something like this:\nscene.getStylesheets().add(this.getClass().getResource(resourceName).toExternalForm()); Honestly, getStylesheets().add() won\u0026rsquo;t care if you pass it a Null value, but toExternalForm() will throw an NPE. IntelliJ IDEA complains about it and gives the following \u0026ldquo;improvement\u0026rdquo; to the code:\nscene.getStylesheets().add(Objects.requireNonNull(this.getClass().getResource(resourceName)).toExternalForm()); This isn\u0026rsquo;t going to execute any better, but it gets rid of the annoying yellow squiggly under the code. You should probably check for Null and then only keep going if it isn\u0026rsquo;t Null, like this:\nURL resource = this.getClass().getResource(resourceName); if (resource != null) { scene.getStylesheets().add(resource.toExternalForm()); } Of course, if this fails to load the resource, you won\u0026rsquo;t get any notice, and you\u0026rsquo;ll probably waste a lot of time wondering why your styleclass selector isn\u0026rsquo;t working. Perhaps you should put an else block on to it and throw a custom BadResourceException or something like that. You could also just put the original version in a try/catch block that throws a meaningful exception when it fails.\nIn Java, you can get access to the Class in a static fashion, you just access {ClassName}.class:\nURL resource = ResourceDemo.class.getResource(resourceName); This is useful if you have a resource in a different package from the one that the class the code is in, but you still need to access it. Pick some class that is in that package and use it\u0026rsquo;s class to load the resource. This feels a bit like a kludge to me, you might be better off by putting a resource loader class in that package and calling it instead.\nFrom Kotlin # In Kotlin, it\u0026rsquo;s easy to avoid Null issues by using the ?.let operation. This is much like using Optional.ifPresent() in Java, and it feels like it inverts the logic by evaluating the resource and then doing something with it if it\u0026rsquo;s valid.\nNote that, in Kotlin, ::class returns a KClass, which is kind of the Kotlin version of the Java Class. To get the Java Class we call KClass.java() which gives us access to getResource().\nThese first two examples are essentially the same, we\u0026rsquo;re taking an already instantiated object, getting the Class and then calling getResource() on it. In most cases we\u0026rsquo;ll need to convert the URL to a String, so we use the ?.toString() to do the conversion if it\u0026rsquo;s not Null. Then, if the result is not Null we pass it to let{} to load the resource into whatever JavaFX class needs it. Note that the it -\u0026gt; is only included here for clarity, you would leave it out in real life.\nmyObject::class.java.getResource(resourceName)?.toString()?.let { it -\u0026gt; doSomethingWithIt } this::class.java.getResource(resourceName)?.toString()?.let {it -\u0026gt; doSomethingWithIt } These examples, however, will silently fail if they don\u0026rsquo;t have an Elvis operator at the tail of the chain to catch them. Here\u0026rsquo;s what the first example might look like with this implemented:\nmyObject::class.java.getResource(resourceName)?.toString()?. let { it -\u0026gt; doSomethingWithIt }?: throw BadResourceException(resourceName) Of course, you\u0026rsquo;ll have to provide the BadResourceException.\nThe next example is nifty. Here we just create an anonymous inner class instead of using something that\u0026rsquo;s already instantiated. It will get its package from whatever package it\u0026rsquo;s defined in. This is useful because it can be defined at the file level, outside of any classes:\nobject {}::class.java.getResource(resourceName)?.toString()?.let { it -\u0026gt; doSomethingWithIt } Finally, we\u0026rsquo;re going to use ::class as if it were a static function. This is about as close as you can get to saying, \u0026ldquo;Hey! Use this other package.\u0026rdquo;, when you call getResource(). This can be useful if you have your resources grouped at some parent package level, and you need to get at them from a sub-package. Alternatively, you can declare a function that uses object {} at that parent package level and call that (which might be a better approach).\nSomeClass::class.java.getResource(resourceName)?.toString()?.let { it -\u0026gt; doSomethingWithIt } Resource Organization # While you can put your resources into the \u0026ldquo;resources\u0026rdquo; section in any way that you want, you should keep in mind that the resource methods in Java are designed to facilitate organizing your resources by package. This might be useful to you, but it\u0026rsquo;s also probably the thing that\u0026rsquo;s causing you the most trouble with finding your resources.\nThe Key Consideration - Uniqueness # When deciding how you are going to organize your resources, you have one key question to ask:\nWhat is the scope in which a resource is going to be used?\nThis is because you can only have one resource with the same name in particular context, which roughly corresponds to a file in a directory. For a simple project with an uncomplicated layout, this question is largely moot because you don\u0026rsquo;t have opportunities for name collisions. However, when you start to use things like subprojects, or intend to overwrite resources from libraries, this becomes more important. Also, if you intend to use a standard resource name, but implemented in different contexts, this can become more complicated.\nPossible Schemas # Everything in /resources This is the simplest possible organizational scheme. Just dump all of your resource files into the root directory of /resources. Although it feels unsophisticated, there\u0026rsquo;s nothing wrong with this, and it\u0026rsquo;s perfectly fine for small applications or any situation where there is no point in imposing unneeded structure and complication. It\u0026rsquo;s also easy to use, because every class in your application can access every resource with any complicated code gymnastics. To access these resources, you\u0026rsquo;ll start off every resource name with /. Divided Up By Type The next step up is to organize your resources by type. So create a subdirectory off /resources for each resource type. This would mean you might have directories like /resources/images, /resources/css or /resources/sounds. You can use any other classification system that makes sense to you, including something that divides your application up, but doesn\u0026rsquo;t use the Java packages. This makes it a little easier to keep track of what resources you have, but doesn\u0026rsquo;t really have any programming impacts over using a single directory. Once again, this can be a great approach for any application that needs a bit more organization to make it easier manage the resources but doesn\u0026rsquo;t have any conflict issues with different packages, modules or libraries. Here too, it\u0026rsquo;s easy for every class in every package in your application to access any resource by starting every resource name with /{subdirectory}/. Divided Up By Package When you start to have possible conflicts with names between packages, then you\u0026rsquo;ll need to divide your resources into packages. For instance, if you have three different logo image files, all used in different packages, but you just want to call each file logo.png, then you\u0026rsquo;ll need to start dividing the resources by package. To access resources organized this way, just use the resource name without a leading /. Divided Up By Package and Type This is combination of the previous two methods. Within each package, have a subdirectory for each type of resource. For instance, you might have /resources/com/example/package1/images or /resources/com/example/package1/subpackagex/css. This is the kind of layout that you\u0026rsquo;d probably use if you were writing a large, complicated application or a library of some sort. To access these resources, you\u0026rsquo;ll start your resource name with the format {subdirectory}/. Note that there is no leading /. Some Combination of the Above There\u0026rsquo;s no rule that says that you need to have only one organization schema in place. If you have resources with a small scope that you want to keep isolated from other parts of your application, then put them in package dependent locations. At the same time, if you have resources that at global in their scope, then put them in a location which isn\u0026rsquo;t package dependent. Which method is best? # There really is no answer to this because the main driver is context. The best guide might be to simply to use the simplest possible scheme that works well for the project. For instance, if you\u0026rsquo;re building a library that is intended to be used by other projects, it would probably be best to consider \u0026ldquo;root\u0026rdquo; to be the base, unique, package for that library. That way you would avoid collisions with resources in the utilizing projects unless they deliberately chose to override your resource files.\nIn the same way, if you\u0026rsquo;re building a project that uses other libraries, you might want to keep all of your project\u0026rsquo;s resources off the root directory, in order to avoid collisions with resources from subprojects that are organized by package.\nSource and Build Structures # Everything in this article assumes that you are using a generic kind of Gradle build that doesn\u0026rsquo;t do anything fancy with the resource copying functions.\nThat isn\u0026rsquo;t always the case, however. For years, I worked on applications that were connected to a multi-user, server based system. We had versions of that system for development, UAT and, of course, the live system. Our CI/CD system was Jenkins, and we had different projects for both UAT and Live, and we used Gradle for the application build.\nOur developers ran the application from their IDE, and the base build just connected to our Dev system. There were environment variables that Gradle could pick up, and we had three versions of some parameter file. Let\u0026rsquo;s say, live.parms, dev.parms and uat.parms, and each one contained the information to connect to a different database system. In the /build/resources directory, it would just be called server.parms. In the UAT and Live Jenkins projects, we would set the environment variable to either UAT or LIVE and the build.gradle file had some logic in the copy section that would branch based on the value of the variable. One, and only one of those three files would be copied over into /build/resources/server.parms.\nIn our application code, we\u0026rsquo;d always call getResources(\u0026quot;/server.parms\u0026quot;). But we didn\u0026rsquo;t have a /src/resources/server.parms file! However, it worked because the code, at runtime, doesn\u0026rsquo;t look at /src, it looks in /build.\nConclusion # Hopefully, at this point you understand why Class.getResource() doesn\u0026rsquo;t have any facility to show you where it looked when it didn\u0026rsquo;t find you resource. That\u0026rsquo;s because it\u0026rsquo;s really too straight-forward to bother having one. The rules for determining the where a resource should be found are actually quite simple and easy to understand when you understand how the build system works with Java to set things up for you.\n","date":"7 February 2024","externalUrl":null,"permalink":"/java/resources/","section":"Programming","summary":"One of the toughest problems beginners encounter is resources that won’t load.  Here’s how to figure out how to organize your resources and how find them with your code.","title":"Where Are My Resources?","type":"programming"},{"content":" Introduction # In previous articles, we\u0026rsquo;ve looked at how to create custom components by using builders and factory methods, and then by extending Region to create a custom component class. If you\u0026rsquo;re writing JavaFX applications, then extending Region is all that you ever need to do to create a custom component, because it\u0026rsquo;s a pretty complete solution.\nThe idea of \u0026ldquo;skins\u0026rdquo; was mentioned briefly, but not pursued because you don\u0026rsquo;t really need it when you\u0026rsquo;re creating your own custom components for your own use.\nBut\u0026hellip;What\u0026rsquo;s the point of skins then?\nThe idea behind a skinnable Control is that you can change its visual representation without messing with the workings of the Control itself. Virtually all of the standard JavaFX Controls are built as skinnable components.\nNow, you may not ever want to create a brand new skinnable Control, but you very well might want to reskin one of the existing Controls. That\u0026rsquo;s what we\u0026rsquo;re going to look at here.\nIn this article we\u0026rsquo;re going to look at ToggleButton and change it so that it doesn\u0026rsquo;t look like a Button any more, but something entirely different.\nDisclaimer # There aren\u0026rsquo;t any \u0026ldquo;official\u0026rdquo; tutorials or documentation about how to go about creating a skin. The closest I\u0026rsquo;ve seen is a placeholder on the OpenJDK Wiki for Upcoming Topics, but that could be years old by now and still not coming. The best way that I know how to figure it out is to look at the source code for JavaFX and other libraries that have created skins.\nBuilding a skin is a bit different from creating a layout in a Region for a custom component. Some of the methods that you need to use are designed to be invoked - often indirectly - from the Layout Manager. In this respect, some of your coding can be considered to be \u0026ldquo;under the hood\u0026rdquo; from a JavaFX perspective.\nUndoubtedly, there are rules about how things should be done what\u0026rsquo;s allowed in some of these methods but you won\u0026rsquo;t find these spelled out anywhere and, once again, you have to figure them out for yourself.\nThis is the first time I\u0026rsquo;ve tried to do something like this, and I\u0026rsquo;ll be the first to admit that I don\u0026rsquo;t know everything about JavaFX skins and custom controls.\nHowever, the code in this tutorial works, and the result is a ToggleButton that acts like a ToggleButton (but doesn\u0026rsquo;t look like one), and that doesn\u0026rsquo;t break when you try to do normal ToggleButton stuff with it. Perhaps that\u0026rsquo;s enough.\nWhat is a Skin? # The Skin/Skinnable concept in JavaFX is an implementation of the Model-View-Controller framework specialized for JavaFX Controls.\nThe Skin itself takes the role of the View. Skin is an interface, that has only 3 methods: dispose(), getNode() and getSkinnable(). Of these, the most interesting is getSkinnable() which returns a reference to the Control that the Skin is attached to.\nSkinnable is the complimentary interface implemented by Controls that also has only 3 methods: getSkin(), setSkin() and skinProperty().\nNone of this appears to shed a light on how Skins work, however. And that\u0026rsquo;s about as far as you\u0026rsquo;ll get with the JavaDocs.\nLet\u0026rsquo;s look at how Skins work\u0026hellip;\nEvery Skinnable class has two parts, the Skin and the Control itself, which is the Skinnable. Keeping with the comparison to MVC, the Skinnable comprises the Model and the Controller in a single unit. The OpenJDK Wiki says the Control is the Model, and the Skin is the Controller + View, but I disagree with that (for what it\u0026rsquo;s worth).\nThe Model for these controls is composed of the Properties that you\u0026rsquo;re used to dealing with in JavaFX. These are things like the TextProperty, MinWidthProperty, OnEventProperty and VisibleProperty. The Skinnable/Controller contains all of the methods for accessing these Properties from outside the Control, like getText(), setMinWidth(), and visibleProperty().\nMost, if not all, of these Properties are shared with the Skin/View, so that they can be used in the component\u0026rsquo;s layout.\nYou instantiate one of these objects through the Skinnable part - the Control itself. Something like new Button(\u0026quot;Click Me\u0026quot;). All of the standard JavaFX Controls have a default Skin defined, and the Control will instantiate that by itself. Somehow (and we don\u0026rsquo;t really care how), the Skin gets passed to the Layout Manager which manages it in the GUI.\nReskinning a Control # Since Skinnable has a method, setSkin(), we can create our own Skins and attach them to the standard JavaFX Controls. This is actually the intention with the design - at least in theory - so that you\u0026rsquo;re not locked into the screen implementations that came \u0026ldquo;out of the box\u0026rdquo;. In reality it can get a bit tricky, the standard Skins often use a private JavaFX API that we don\u0026rsquo;t have access to - so extending an existing Skin will often be problematic.\nIt\u0026rsquo;s also possible to specify the skin via a stylesheet. In that case, you use the -fx-skin attribute.\nIn this article we\u0026rsquo;re going to look at reskinning ToggleButton to turn it into something quite different, a toggle (or \u0026ldquo;flip\u0026rdquo;) switch. What we are going for is something that looks like one of these:\nYou can see that this is quite a different look from the standard ToggleButton, which looks like a regular Button but has two looks, one for \u0026ldquo;Selected\u0026rdquo; and one for \u0026ldquo;Not Selected\u0026rdquo;. Our version will also have two looks:\nThe Standard ToggleButton Skin # Let\u0026rsquo;s take a look at the standard Skin for ToggleButton, so we can see how it works\u0026hellip;\npublic class ToggleButtonSkin extends LabeledSkinBase\u0026lt;ToggleButton\u0026gt; { private final BehaviorBase\u0026lt;ToggleButton\u0026gt; behavior; public ToggleButtonSkin(ToggleButton control) { super(control); behavior = new ToggleButtonBehavior\u0026lt;\u0026gt;(control); } @Override public void dispose() { super.dispose(); if (behavior != null) { behavior.dispose(); } } } That\u0026rsquo;s pretty underwhelming!\nThere\u0026rsquo;s really nothing here. Some behaviour stuff, whatever that is, but nothing visual. If you look at BehaviorBase you\u0026rsquo;ll find that it relates to traversal and hot key stuff - important things, but still nothing visual.\nNow, if you look at LabeledSkinBase you\u0026rsquo;ll see that there\u0026rsquo;s a lot of stuff there. A lot. It\u0026rsquo;s quite complicated. But it has nothing specific to ToggleButton or even Button to it at all.\nIt turns out that what makes a Button look like a Button has nothing to do with the Skin at all. It\u0026rsquo;s all in the styling. From the visual perspective, a Button is just a Label that\u0026rsquo;s been styled to look like a Button, and a ToggleButton is just a Button with an added Pseudo Class for selected.\nThe New Skin # But we want our ToggleButton to look like something completely different, and there isn\u0026rsquo;t any way to do that with just some CSS hocus pocus. We need a new layout with different elements that will show our toggle as a switch.\nIt turns out that the skin that gets us furthest towards this is CheckBoxSkin. Just like ToggleButtonSkin, it extends LabeledSkinBase, but it also has an extra element - the checkbox. So what we\u0026rsquo;re going to do is to add the toggle switch in the same manner that CheckBoxSkin adds the checkbox, but still base off ToggleButtonSkin.\nWhy not extend from CheckBoxSkin?\nCheckBox has three states: \u0026ldquo;selected\u0026rdquo;, \u0026ldquo;unselected\u0026rdquo; and \u0026ldquo;indeterminate\u0026rdquo;. CheckBoxSkin works with CheckBox and if we use CheckBoxSkin then we\u0026rsquo;ll need to implement our Control as a CheckBox not a ToggleButton. Which means that we\u0026rsquo;ll need to deal with \u0026ldquo;indeterminate\u0026rdquo;. To keep this as a ToggleButton we\u0026rsquo;ll need to use a ToggleButton skin.\nThe approach we\u0026rsquo;re going to take here is to instantiate a ToggleButton, and the use its setSkin() method to replace the standard skin with our own skin, which we\u0026rsquo;ll call ToggleFlipSkin. In this way we\u0026rsquo;re not going to change the behaviour of ToggleButton one bit. It will still behave exactly like a standard ToggleButton, except that its visual component will be customized.\nCustom Styleable Property # Before we get into the actual visual switch aspect, we need to deal with one factor that we\u0026rsquo;ll need in order to build that switch. And that means adding a StyleableProperty\u0026hellip;\nThere\u0026rsquo;s only one StyeableProperty that we\u0026rsquo;re going to add, one which will control the size of the toggle switch itself by specifying the radius of the flippers inside it. This radius will drive the rest of the sizing for the toggle switch.\nSince we\u0026rsquo;re not extending ToggleButton, we need to do all of this from inside the skin, but it\u0026rsquo;s essentially the same as adding any other StyleableProperty to Node. We need to add the StyleableProperty and then add the CssMetaData to support it.\nThe quirk here is that the skin does not implement Styleable, so we need to build the CssMetaData with reference to the ToggleButton control itself, which does implement Styleable. This results in some awkward self-references and casting back to the skin where the new CssMetaData is defined.\nLet\u0026rsquo;s look at just this part:\nfun flipperSizeProperty() = flipperSizeImpl val flipperSizeImpl: StyleableDoubleProperty = SimpleStyleableDoubleProperty(ToggleFlip.BUTTON_SIZE_META_DATA, this, \u0026#34;flipperSize\u0026#34;, 12.0) var flipperSize: Double get() = flipperSizeImpl.value set(value) = flipperSizeImpl.set(value) companion object CssStuff { val BUTTON_SIZE_META_DATA: CssMetaData\u0026lt;ToggleButton, Number\u0026gt; = object : CssMetaData\u0026lt;ToggleButton, Number\u0026gt;(\u0026#34;-wfx-flipper-size\u0026#34;, StyleConverter.getSizeConverter()) { override fun isSettable(styleable: ToggleButton) = !((styleable.skin as ToggleFlipSkin).flipperSizeImpl.isBound) override fun getStyleableProperty(styleable: ToggleButton) = (styleable.skin as ToggleFlipSkin).flipperSizeImpl } private val cssMetaDataList = (ToggleButton.getClassCssMetaData() + BUTTON_SIZE_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getCssMetaData(): MutableList\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt; { return getClassCssMetaData() } If you\u0026rsquo;ve read the article about customizing custom components that extend Region, you\u0026rsquo;ll see that this is very similar code.\nThe two implemented functions in new CssMetaData object - isSettable() and getStyleableProperty() - both take a Styleable as a parameter. In this case, it\u0026rsquo;s going to be our control, which is a ToggleButton. But of course we need to refer to the StyleableProperty which is defined in our skin, which means we have to call ToggleButton.getSkin(). Then we have to cast it our skin class which is ToggleFlipSkin - and this is safe because we couldn\u0026rsquo;t get here unless we were here in the first place.\nFinally, we need to get the rest of the CssMetaData elements from ToggleButton not the skin, and that\u0026rsquo;s how cssMetaDataList is created.\nBTW: If you want to avoid this round-about casting stuff by extending ToggleButton and adding the StyleableProperty and the CssMetaData directly to it, you\u0026rsquo;ll still get a little stuck. That\u0026rsquo;s because getCssMetaData() is final in ToggleButton (and most other controls). There\u0026rsquo;s actually a note that if you want to override it you should override getCssMetaData() in the skin.\nThe Toggle Switch Graphic # Now we are going to look at how to create the extra piece of visual component, the switch graphic itself\u0026hellip;\nThis is one of the rare occasions when AnchorPane is the right solution! Our toggle switch is an AnchorPane with two StackPanes inside it, each anchored to either the lef or right side of the AnchorPane. The StackPanes are made into circles by calling setShape(Circle(10.0)). These StackPanes are the \u0026ldquo;flippers\u0026rdquo; of the switch. The Visible property of each flipper is bound to the Selected property of the ToggleButton such that only one is visible at a time.\nThat\u0026rsquo;s all there is to it really. The minimum and maximum height and width properties of the flippers are bound to the FlipperSize StyleableProperty. The only tricky bit here is that the anchor distances need to scale up and down as the size of the flippers is bigger or smaller.\nLet\u0026rsquo;s take a look at the code for this:\nprivate fun createFlipperBox(): Region = AnchorPane().apply { val flipperOff = flipper(\u0026#34;flipper\u0026#34;, false) val flipperOn = flipper(\u0026#34;flipper\u0026#34;, true) setFlipperInsets(flipperOff, flipperOn) flipperSizeProperty().addListener { _ -\u0026gt; setFlipperInsets(flipperOff, flipperOn) } minWidthProperty().bind(flipperSizeProperty().multiply(6.0)) children += listOf(flipperOff, flipperOn) styleClass += \u0026#34;flipper-box\u0026#34; } private fun flipper(theStyle: String, showWhen: Boolean) = StackPane().apply { minWidthProperty().bind(flipperSizeImpl.multiply(2.0)) minHeightProperty().bind(flipperSizeImpl.multiply(2.0)) maxHeightProperty().bind(flipperSizeImpl.multiply(2.0)) maxWidthProperty().bind(flipperSizeImpl.multiply(2.0)) shape = Circle(10.0) styleClass += theStyle visibleProperty().bind( Bindings.createBooleanBinding( { control.isSelected == showWhen }, control.selectedProperty() ) ) } private fun setFlipperInsets(buttonOff: StackPane, buttonOn: StackPane) { val inset = flipperSize / 6.0 AnchorPane.setLeftAnchor(buttonOff, inset) AnchorPane.setTopAnchor(buttonOff, inset) AnchorPane.setBottomAnchor(buttonOff, inset) AnchorPane.setRightAnchor(buttonOn, inset) AnchorPane.setTopAnchor(buttonOn, inset) AnchorPane.setBottomAnchor(buttonOn, inset) } This is all very straight-forward, and there\u0026rsquo;s nothing very complicated about this layout. The minimum width of the flipper box is set to 6 times the radius of the flippers, which gives a nicely proportioned toggle switch design.\nThe Toggle Switch Styling # The biggest question about a toggle switch is, \u0026ldquo;Which way is on?\u0026rdquo;. In this design, it\u0026rsquo;s the one on the right, but that\u0026rsquo;s not going to be obvious to a user. We\u0026rsquo;ll need a visual clue.\nThere\u0026rsquo;s a lot of different ways to do this. To keep things simple for this example, we\u0026rsquo;re just going to use colours. You could, however, put images or text inside the StackPanes, you could add Text in the areas outside the StackPanes and you could add custom StyleableProperties to support these approaches. Just using colours may not be the best approach because some users might have difficulties differentiating them.\nFor colours, though, it\u0026rsquo;s all in the style sheets:\n.toggle-flip { -wfx-flipper-size: 8px; } .toggle-flip .flipper-box { -fx-base: #ffb8bb; -fx-background-color: -fx-shadow-highlight-color, derive(-fx-outer-border, -2%), -fx-inner-border-bottomup, -fx-body-color; -fx-background-insets: 1 0 0 0, 0, 3 0 1 1.5, 2; -fx-background-radius: 12px, 12px, 112px, 10px; -fx-text-fill: -fx-text-base-color; } .toggle-flip:selected .flipper-box { -fx-base: #6acda0; } .toggle-flip .flipper { -fx-background-color: derive(-fx-base, +90%); } Most of the complexity here is to create a 3D feel to the switch body via shading and highlighting. That\u0026rsquo;s why there\u0026rsquo;s 4 different background colours with different insets.\nThe colour change is done by manipulating -fx-base. This colour is set at the root of the Modena stylesheet as a global value. By redefining it in .toggle-flip .flipper-box it\u0026rsquo;s just changed for the AnchorPane and all of its children. It starts off as pastel pink, and then in .toggle-flip:selected .flipper-box it gets changed to pastel green.\nThe result looks like this for selected:\nAnd this for unselected:\nOne last point:\nIf you change the flipper radius to a much larger number, then the background radii from the style sheet start to make the AnchorPane corners decidely square. There\u0026rsquo;s no way to couple the -wfx-flipper-size to the -fx-background-radius attribute in the style sheet. It doesn\u0026rsquo;t seem unreasonable, however, to expect a programmer to adjust the background radii if they are changing the flipper size.\nHowever, the flipper size property is exposed publicly. If this size is manipulated programatically, there\u0026rsquo;s really no way to adjust the background radii in tandem. Doing this would involve calling up the AnchorPane background and expecting it to be a List of BackgroundFill and then manipulating the radius of each. But doing that assumes, then, that the style sheet specifies that the backgrounds are constructed this way. Which, of course, doesn\u0026rsquo;t need to be true.\nThe Layout of the Skin # So far, we haven\u0026rsquo;t really introduced any brand new concepts, just layouts and styling. Working with the skin layout itself is a little bit trickier. This is where we\u0026rsquo;re going to see how our new layout component interacts with the rest of the skin.\nRemember that ToggleButtonSkin extends LabeledSkinBase and that Labeled subclasses have two basic layout components; a Text and a Graphic and either one (or both) of them can be Null. We need to integrate a third layout component with those other two components in a way that makes sense. The skin itself is a Region, and the standard skin approach is to manually place the children in the Region and control the other parameters to make it work.\nThe updateChildren() Method # One thing about skins is that there are a whole bunch of special techniques and rules that aren\u0026rsquo;t documented or explained anywhere. The only way to learn is to look at existing examples and try to understand what they are doing and why they are doing it that way.\nThe first of these is the method to add our new layout element, the \u0026ldquo;FlipBox\u0026rdquo;, to the Region. For this we need to use this:\nprivate val flipperBox: Region? = createFlipperBox() override fun updateChildren() { super.updateChildren() if (flipperBox != null) { children.add(flipperBox) } } It appears that this is the only place where you should be adding new Nodes to the layout.\nHere flipperBox is a field in the skin. It is possible - actually, certain - that this will be called from the super-type before flipperBox has been initialized. Because of this, you have to check to make sure that flipperBox is not Null before you attempt to add it to the children of the Region.\nHonestly, I don\u0026rsquo;t know how it does this and Intellij complains that flipperBox can never be Null. But I have the stack dumps to show that it happens.\nThere\u0026rsquo;s a call to updateChildren() in the init{} block of our skin, this ensures that the flipperBox gets added to the layout once it has been initialized.\nThe layoutChildren() Method # The next important method to understand is layoutChildren(). This method is called whenever the Layout Manager needs to recalculate the layout of the skin Region. It is called automatically, and can be triggered by any number of things that you really don\u0026rsquo;t have any control over.\nOne thing that will trigger it is if you change the size or position of a Node inside the layout. Another that will trigger it is if you add more children (or children of children) to the layout. You will get an infinite loop if you add children inside the layoutChildren() method. So, don\u0026rsquo;t do that.\nLet\u0026rsquo;s look at our layoutChildren():\noverride fun layoutChildren(x: Double, y: Double, w: Double, h: Double) { val boxWidth = snapSizeX(flipperSize * 6.33) val boxHeight = snapSizeY(flipperSize * 2.33) val computeWidth = control.prefWidth(-1.0).coerceAtLeast(control.minWidth(-1.0)) val labelWidth = (computeWidth - boxWidth).coerceAtMost(w - snapSizeX(boxWidth)) val labelHeight = control.prefHeight(labelWidth).coerceAtMost(h) val maxHeight = boxHeight.coerceAtLeast(labelHeight) val xOffset = Utils.computeXOffset(w, labelWidth + boxWidth, control.alignment.hpos) + x val yOffset = Utils.computeYOffset(h, maxHeight, control.alignment.vpos) + y layoutLabelInArea(xOffset + boxWidth, yOffset, labelWidth, maxHeight, control.alignment) flipperBox.resize(boxWidth, boxHeight) positionInArea( flipperBox, xOffset, yOffset, boxWidth, maxHeight, 0.0, control.alignment.hpos, control.alignment.vpos ) } The first part of this method is figuring out how much space things should take up. Note that the coerceAtLeast() and coerceAtMost() functions are Kotlin native implementations of Math.max() and Math.min().\nIt\u0026rsquo;s important to note that all of these calculated numbers are just for the use of positioning the children in the layout. By the time this method is called, something higher up the chain has figured out where the whole thing goes in the screen layout and how big it will be - that\u0026rsquo;s what the x,y,w and h parameters are.\nThe last three calls do the positioning work\u0026hellip;\nThe method layoutLabelInArea() is the method that handles the traditional Label elements of the layout: the Text and the Graphic. If you like, you can think of our overall layout as an AnchorPane in a Region with a Label. This is the call that organizes the layout of the Label part.\nThe next call, to flipperBox.resize() is a method of Region and the JavaDocs say that your application should not call this directly.\nSo what\u0026rsquo;s up?\nOnce you\u0026rsquo;re inside layoutChildren(), as well as some other places in the skin, those \u0026ldquo;Application code should not\u0026hellip;\u0026rdquo; rules just don\u0026rsquo;t apply. This is code that is only invoked from inside the Layout Manager, and this is the place those methods are intended to be called from. In this case, resize() actually sets the size of the AnchorPane, and if you leave this call out, then you get a FlipperBox with zero size - and you can\u0026rsquo;t see it.\nFinally, we have positionInArea(). This is the method that figures out where to put the FlipperBox in the Region. This is a method that delegates to another positionInArea() method adding in a parameter for zero insets, and that delegates to method of Region that adds some more default parameters which in turn delegates to another method.\nFor the most part, this code was lifted directly from CheckBoxSkin and then the boxWidth and boxHeight calculations were changed to apply the flipperSize property.\nSetting the Control Size # Changing layoutChildren() almost finished the job, but the text was getting chopped off. This could be fixed by manually setting the minWidth() of the ToggleButton in the layout code, but that shouldn\u0026rsquo;t be necessary. Once again, checking the code for CheckBoxSkin had the answer.\nThe following methods were added to the skin:\noverride fun computeMinWidth( height: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computeMinWidth( height, topInset, rightInset, bottomInset, leftInset ) + snapSizeX(flipperBox.minWidth(-1.0)) override fun computeMinHeight( width: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computeMinHeight(width - flipperBox.minWidth(-1.0), topInset, rightInset, bottomInset, leftInset) .coerceAtLeast(topInset + flipperBox.minHeight(-1.0) + bottomInset) override fun computePrefWidth( height: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computePrefWidth( height, topInset, rightInset, bottomInset, leftInset ) + snapSizeX(flipperSize * 6.33) + snapSizeX(20.0) override fun computePrefHeight( width: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computePrefHeight(width - flipperBox.prefWidth(-1.0), topInset, rightInset, bottomInset, leftInset) .coerceAtLeast(topInset + flipperBox.prefHeight(-1.0) + bottomInset) These methods let the normal LabeledSkinBase skin calculate the dimension of the Label part of the layout, and then add in an allowance for the FlipperBox.\nThe main concern here was computePrefWidth(). It now adds enough space for the FlipperBox and a spacer of 20 pixels.\nSome further investigation revealed that these methods are expected to be overriden by skins in order to handle sizing properly.\nThe result looks like this:\nAll the Code in One Place # At this point, you\u0026rsquo;ve seen nearly all the code for the skin, just chopped up into little parts. Let\u0026rsquo;s take a look at it all together:\nimport com.sun.javafx.scene.control.skin.Utils import javafx.beans.binding.Bindings import javafx.css.* import javafx.scene.control.Skin import javafx.scene.control.ToggleButton import javafx.scene.control.skin.ToggleButtonSkin import javafx.scene.layout.AnchorPane import javafx.scene.layout.Region import javafx.scene.layout.StackPane import javafx.scene.shape.Circle class ToggleFlipSkin(val control: ToggleButton) : ToggleButtonSkin(control) { fun flipperSizeProperty() = flipperSizeImpl val flipperSizeImpl: StyleableDoubleProperty = SimpleStyleableDoubleProperty(ToggleFlip.BUTTON_SIZE_META_DATA, this, \u0026#34;flipperSize\u0026#34;, 12.0) var flipperSize: Double get() = flipperSizeImpl.value set(value) = flipperSizeImpl.set(value) private val flipperBox: Region = createFlipperBox() companion object CssStuff { val BUTTON_SIZE_META_DATA: CssMetaData\u0026lt;ToggleButton, Number\u0026gt; = object : CssMetaData\u0026lt;ToggleButton, Number\u0026gt;(\u0026#34;-wfx-flipper-size\u0026#34;, StyleConverter.getSizeConverter()) { override fun isSettable(styleable: ToggleButton) = !((styleable.skin as ToggleFlipSkin).flipperSizeImpl.isBound) override fun getStyleableProperty(styleable: ToggleButton) = (styleable.skin as ToggleFlipSkin).flipperSizeImpl } private val cssMetaDataList = (ToggleButton.getClassCssMetaData() + BUTTON_SIZE_META_DATA) as MutableList fun getClassCssMetaData() = cssMetaDataList } override fun getCssMetaData(): MutableList\u0026lt;CssMetaData\u0026lt;out Styleable, *\u0026gt;\u0026gt; { return getClassCssMetaData() } init { control.styleClass.setAll(\u0026#34;toggle-flip\u0026#34;) updateChildren() } override fun updateChildren() { super.updateChildren() if (flipperBox != null) { children.add(flipperBox) } } private fun createFlipperBox(): Region = AnchorPane().apply { val flipperOff = flipper(\u0026#34;flipper\u0026#34;, false) val flipperOn = flipper(\u0026#34;flipper\u0026#34;, true) setFlipperInsets(flipperOff, flipperOn) flipperSizeProperty().addListener { _ -\u0026gt; setFlipperInsets(flipperOff, flipperOn) } minWidthProperty().bind(flipperSizeProperty().multiply(6.0)) children += listOf(flipperOff, flipperOn) styleClass += \u0026#34;flipper-box\u0026#34; } private fun flipper(theStyle: String, showWhen: Boolean) = StackPane().apply { minWidthProperty().bind(flipperSizeImpl.multiply(2.0)) minHeightProperty().bind(flipperSizeImpl.multiply(2.0)) maxHeightProperty().bind(flipperSizeImpl.multiply(2.0)) maxWidthProperty().bind(flipperSizeImpl.multiply(2.0)) shape = Circle(10.0) styleClass += theStyle visibleProperty().bind( Bindings.createBooleanBinding( { control.isSelected == showWhen }, control.selectedProperty() ) ) } private fun setFlipperInsets(buttonOff: StackPane, buttonOn: StackPane) { val inset = flipperSize / 6.0 AnchorPane.setLeftAnchor(buttonOff, inset) AnchorPane.setTopAnchor(buttonOff, inset) AnchorPane.setBottomAnchor(buttonOff, inset) AnchorPane.setRightAnchor(buttonOn, inset) AnchorPane.setTopAnchor(buttonOn, inset) AnchorPane.setBottomAnchor(buttonOn, inset) } override fun computeMinWidth( height: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computeMinWidth( height, topInset, rightInset, bottomInset, leftInset ) + snapSizeX(flipperBox.minWidth(-1.0)) override fun computeMinHeight( width: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computeMinHeight(width - flipperBox.minWidth(-1.0), topInset, rightInset, bottomInset, leftInset) .coerceAtLeast(topInset + flipperBox.minHeight(-1.0) + bottomInset) override fun computePrefWidth( height: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computePrefWidth( height, topInset, rightInset, bottomInset, leftInset ) + snapSizeX(flipperSize * 6.33) + snapSizeX(20.0) override fun computePrefHeight( width: Double, topInset: Double, rightInset: Double, bottomInset: Double, leftInset: Double ) = super.computePrefHeight(width - flipperBox.prefWidth(-1.0), topInset, rightInset, bottomInset, leftInset) .coerceAtLeast(topInset + flipperBox.prefHeight(-1.0) + bottomInset) override fun layoutChildren(x: Double, y: Double, w: Double, h: Double) { val boxWidth = snapSizeX(flipperSize * 6.33) val boxHeight = snapSizeY(flipperSize * 2.33) val computeWidth = control.prefWidth(-1.0).coerceAtLeast(control.minWidth(-1.0)) val labelWidth = (computeWidth - boxWidth).coerceAtMost(w - snapSizeX(boxWidth)) val labelHeight = control.prefHeight(labelWidth).coerceAtMost(h) val maxHeight = boxHeight.coerceAtLeast(labelHeight) val xOffset = Utils.computeXOffset(w, labelWidth + boxWidth, control.alignment.hpos) + x val yOffset = Utils.computeYOffset(h, maxHeight, control.alignment.vpos) + y layoutLabelInArea(xOffset + boxWidth, yOffset, labelWidth, maxHeight, control.alignment) flipperBox.resize(boxWidth, boxHeight) positionInArea( flipperBox, xOffset, yOffset, boxWidth, maxHeight, 0.0, control.alignment.hpos, control.alignment.vpos ) } } Running It # The entire point of this approach was to be able to use a standard ToggleButton and then just replace the skin to implement a new look and feel. And that\u0026rsquo;s all you need to do to use this new skin:\nclass ToggleFlipDemo : Application() { private val nameProperty: StringProperty = SimpleStringProperty(\u0026#34;Not Started\u0026#34;) private var counter: Int = 0 override fun start(primaryStage: Stage) { primaryStage.scene = Scene(createContent()).addWidgetStyles().apply { object {}::class.java.getResource(\u0026#34;/css/toggleflip.css\u0026#34;)?.toString()?.let { stylesheets += it } } primaryStage.show() } private fun createContent(): Region = BorderPane().apply { val customControl1 = ToggleButton(\u0026#34;Power\u0026#34;).apply { graphic = FontIcon(\u0026#34;typ-plug\u0026#34;).apply { iconSize = 23 } skin = ToggleFlipSkin(this) } val customControl2 = ToggleButton(\u0026#34;Power\u0026#34;).apply { skin = ToggleFlipSkin(this) } val customControl3 = ToggleButton(\u0026#34;\u0026#34;).apply { skin = ToggleFlipSkin(this) graphic = FontIcon(\u0026#34;typ-plug\u0026#34;).apply { iconSize = 23 } } center = VBox(20.0, customControl1, customControl2, customControl3).apply { alignment = Pos.CENTER_LEFT padding = Insets(0.0, 0.0, 0.0, 100.0) } minWidth = 400.0 } padWith 20.0 } fun main() = Application.launch(ToggleFlipDemo::class.java) Final Notes # One thing about this design is that the toggle switch graphic always appears to the left of the Label. That\u0026rsquo;s because that\u0026rsquo;s how CheckBox works, and we started from there. It\u0026rsquo;s also easier to deal with if you have a column of these switches because the graphics will all line up nicely - something that could be an issue if the Label text for each is a different length with the graphic on the right. Not that that couldn\u0026rsquo;t be dealt with, by making the layout behaviour pin the graphic at the far right of the layout, and specifying a preferred width for each one. But that would make the design more complicated.\nAlso, you could introduce a StyleableProperty for the positioning of the label and graphic. You could also design it such that the switch graphic could be vertical instead of horizontal.\nThis design preserves the ability to use either or both of the Label text and graphic, like this:\nConclusion # There\u0026rsquo;s no point in pretending that reskinning a Control is simple and something suitable for beginners to JavaFX. The programming itself isn\u0026rsquo;t complex, but you do need a solid understanding of the basic principles of layouts, Nodes and Controls. You\u0026rsquo;ll need to be comfortable poking around in the source code for JavaFX, and understand how the pieces connect to each other.\nIf you do try this yourself, it\u0026rsquo;s almost guaranteed that you\u0026rsquo;ll knock up against undocumented constraints and quirks of the Skin class. There are times when it will be frustrating, and you\u0026rsquo;ll have no choice other than to test out various approaches to find what works.\nIt\u0026rsquo;s also instructive to look at the source code for ControlsFX and AtlantaFX, as they both contain completely custom skins that don\u0026rsquo;t rely on extending existing skins in JavaFX.\n","date":"19 July 2023","externalUrl":null,"permalink":"/javafx/techniques/toggle-button-reskin/","section":"JavaFX","summary":"An introduction to JavaFX Skins and Skinnable and how to create your own skin.","title":"Reskinning ToggleButton","type":"javafx"},{"content":" Introduction # One of the advantages of coding layouts by hand and following both DRY (Don\u0026rsquo;t Repeat Yourself) and the \u0026ldquo;Single Responsibility Principle\u0026rdquo; is that your code ends up composed of lots of small methods that do very specific things. Over time, it becomes very easy to start viewing the results of these methods as \u0026ldquo;custom layouts\u0026rdquo;, \u0026ldquo;custom controls\u0026rdquo; or \u0026ldquo;custom widgets\u0026rdquo;. This is especially true if you\u0026rsquo;ve built up a library of these methods that you call from different layouts.\nThis can be as simple as a method that instantiates a Label and then adds a StyleClass to it. If you call your method headingLabel(), then you\u0026rsquo;d start to think of the results not just as a Label, but a HeadingLabel, or maybe just a Heading.\nThere\u0026rsquo;s a great deal of power in this way of thinking about your layouts, because now the layout code isn\u0026rsquo;t calling a bunch of methods, it\u0026rsquo;s integrating a collection of custom widgets. That\u0026rsquo;s not just semantics, it is a different viewpoint.\nIf you keep applying DRY across your projects, or even layouts in the same project, you\u0026rsquo;ll eventually move some of those small methods, the ones that aren\u0026rsquo;t one-offs, into separate library so that you can share them across screens and projects. Once you\u0026rsquo;ve done that, you get the added bonus that you never have to think about how those custom widgets work, because you know that they work - and you can concentrate on your layout instead.\nIn this tutorial we\u0026rsquo;re going to look at how to start from a simple small method in a layout and then how you\u0026rsquo;d implement it as part of a shared library.\nOur Example # For this tutorial we\u0026rsquo;re going to work with a control that looks like this:\nIt\u0026rsquo;s an HBox with a Label, a TextField and a Button in it. When the TextField has focus, then the Button becomes the default button, so you can just type in the TextField and hit to trigger the Button. When the TextField is empty, then the Button is disabled.\nIt has some specific functionality, so it\u0026rsquo;s not totally trivial, and yet the implementation of the layout is pretty straight-forward and easy to understand. So let\u0026rsquo;s get to it\u0026hellip;\nFirst Version - Local Factory Method # The first way to build this, we\u0026rsquo;re going just create it as a local factory method. It\u0026rsquo;s going to be a \u0026ldquo;one-off\u0026rdquo;, so we don\u0026rsquo;t need to be particularly careful about it, and we can make all kinds of assumptions about how it\u0026rsquo;s being used - because we know how it\u0026rsquo;s being used:\nclass FactoryMethodStyle : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent()) stage.show() } private val name: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val results: StringProperty = SimpleStringProperty(\u0026#34;No search done\u0026#34;) private fun createContent(): Region = BorderPane().apply { center = createCustomBox(\u0026#34;Last Name:\u0026#34;, \u0026#34;Search\u0026#34;, name, EventHandler\u0026lt;ActionEvent\u0026gt; { doSearch() }); bottom = Label().apply { textProperty().bind(results) } padding = Insets(20.0) } private fun createCustomBox(labelText: String, buttonText: String, boundProperty: StringProperty, handler: EventHandler\u0026lt;ActionEvent\u0026gt;): Region = HBox().apply { val textField = TextField().apply { textProperty().bindBidirectional(boundProperty) } children += Label(labelText) children += textField children += Button(buttonText).apply { onAction = handler defaultButtonProperty().bind(textField.focusedProperty().and(name.isNotEmpty)) disableProperty().bind(name.isEmpty) } alignment = Pos.CENTER_LEFT spacing = 6.0 } private fun doSearch() { results.value = \u0026#34;Nothing found for: ${name.value}\u0026#34; } } fun main() = Application.launch(FactoryMethodStyle::class.java) This is Kotlin, but there\u0026rsquo;s nothing scary in here. If you\u0026rsquo;re really stuck, you can read my Kotlin for Java Programmers article, which should clear everything up.\nWe have a BorderPane, and in the centre we\u0026rsquo;re putting our widget, which we\u0026rsquo;ll call \u0026ldquo;CustomBox\u0026rdquo; throughout this article. The factory method needs the text for the Label and the Button, the StringProperty to bind to the TextField and the EventHandler for the Button.\nThen we construct the HBox and we create the contents, do all the bindings and put the parts into the HBox.\nThe method returns Region which isn\u0026rsquo;t going to expose any of the contents since there\u0026rsquo;s no public Region.getChildren() method - this is going to be important later on. Once you\u0026rsquo;ve got the CustomBox there\u0026rsquo;s really nothing you can do with it except put it in the layout. Region allows you to mess with the min and max width and height, but that\u0026rsquo;s about all, and there are the methods from Node, too.\nSecond Version - Builder Class # Now, we\u0026rsquo;re going to look at how you might start to move this out of your layout code altogether, and the ways that you might implement it as part of a library of sub-layouts and widgets that you use frequently.\nJavaFX has this handy Interface called Builder. It\u0026rsquo;s generic so you declare you class as implementing Builder\u0026lt;SomeClass\u0026gt;, with SomeClass being the class of the object that is going to be built.\nThe standard method with builders is to configure them, and then call Builder.build() to get the output. Let\u0026rsquo;s take a look at a builder for CustomBox:\nclass CustomBoxBuilder(private val labelText: String, private val buttonText: String, private val boundProperty: StringProperty, private val handler: EventHandler\u0026lt;ActionEvent\u0026gt;) : Builder\u0026lt;Region\u0026gt; { override fun build(): Region = HBox().apply { val textField = TextField().apply { textProperty().bindBidirectional(boundProperty) } children += Label(labelText) children += textField children += Button(buttonText).apply { onAction = handler defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind(textField.textProperty().isEmpty) } alignment = Pos.CENTER_LEFT spacing = 6.0 } } And here\u0026rsquo;s how we\u0026rsquo;d use it:\nclass BuilderStyle : Application() { override fun start(stage: Stage) { stage.scene = Scene(createContent()) stage.show() } private val name: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val results: StringProperty = SimpleStringProperty(\u0026#34;No search done\u0026#34;) private fun createContent(): Region = BorderPane().apply { center = CustomBoxBuilder(\u0026#34;Last Name:\u0026#34;, \u0026#34;Search\u0026#34;, name, { doSearch() }).build() bottom = Label().apply { textProperty().bind(results) } padding = Insets(20.0) } private fun doSearch() { results.value = \u0026#34;Nothing found for: ${name.value}\u0026#34; } } fun main() = Application.launch(BuilderStyle::class.java) To be honest, the CustomBoxBuilder.build() method is almost exactly the same as createCustomBox() from the first version. We don\u0026rsquo;t have access to name in CustomBoxBuilder so some small changes were made to the bindings to reference textField.textProperty() instead. Otherwise, they are identical.\nBut since CustomBoxBuilder is a generic tool, we can\u0026rsquo;t make assumptions about how it\u0026rsquo;s going to be used, and we can\u0026rsquo;t assume that all of the design decisions that we made initially are going to work for any new situations. So we\u0026rsquo;ll need to add some more capabilities to CustomBoxBuilder by adding decorators. Using decorators will allow us to use a \u0026ldquo;fluent API\u0026rdquo; style to configure our builder\u0026hellip;\ntypealias ButtonRunner = (Runnable) -\u0026gt; Unit class CustomBoxBuilder(private val labelText: String, private val buttonText: String, private val boundProperty: StringProperty) : Builder\u0026lt;Region\u0026gt; { private var spacing: Double = 4.0 private var buttonAction: ButtonRunner = {} private val actionRunning: BooleanProperty = SimpleBooleanProperty(false) override fun build(): Region = HBox().apply { val textField = TextField().apply { textProperty().bindBidirectional(boundProperty) } children += Label(labelText) children += textField children += Button(buttonText).apply { onAction = EventHandler\u0026lt;ActionEvent\u0026gt; { actionRunning.value = true buttonAction { actionRunning.value = false } } defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind((textField.textProperty().isEmpty).or(actionRunning)) } alignment = Pos.CENTER_LEFT spacing = this@CustomBoxBuilder.spacing } fun setSpacing(newValue: Double) = this.apply { spacing = newValue } fun setButtonAction(newAction: ButtonRunner) = this.apply { buttonAction = newAction } } First of all, Kotlin makes declaring decorators ridiculously easy. The structure:\nfun something() = this.apply{} will return the object itself, and run whatever is in the {} against the object. For spacing, we just added a private field to the builder, and then wrote a decorator setSpacing() method that updates the field. It\u0026rsquo;s almost exactly a traditional setter except that it returns the object.\nWhen we start thinking about how a Button might be used in a more general sense, it\u0026rsquo;s obvious that it could invoke a background Task that might take some time. We want to prevent clicking the Button again and launching a new background Task while the first one is running. This means we\u0026rsquo;ll need to disable it, and then create a Runnable that the Task can run when it\u0026rsquo;s completed.\nThat\u0026rsquo;s what we have here. First of all, we created a Kotlin thing called a typealias. It\u0026rsquo;s just a way to have a meaningful name on a more complicated type definition. In this case, it\u0026rsquo;s the object that we need to have passed in to run the Button action, hence ButtonRunner. In Java terms, Kotlin\u0026rsquo;s, (Runnable) -\u0026gt; Unit is the same thing as Consumer\u0026lt;Runnable\u0026gt;, which means that ButtonRunner == Consumer\u0026lt;Runnable\u0026gt;.\nFinally, the disabling logic for Button gets a little more complicated. We already have the DisableProperty bound to whether or not the TextField is empty, so we can\u0026rsquo;t just call Button.setDisable(true) because we\u0026rsquo;ll get an Exception about trying to set a bound Property. So we\u0026rsquo;ve introduced the actionRunning Property and added it into the disable binding. Then we just toggle actionRunning to control the disabling of the Button.\nThird Version - Extend a Layout Class (Not Recommended) # The builder method is great, but once you\u0026rsquo;ve run Builder.build() and have your Region there\u0026rsquo;s nothing that you can do to it other than put it in a layout. Let\u0026rsquo;s say that you wanted to change the spacing in response to something that happens in your GUI, you can\u0026rsquo;t do it.\nSo the answer is to have something that behaves more like a Node from the ground up. You can instantiate it, put it into a layout and continue to call methods in it to interact with it.\nThat approach is to create a custom class by extending HBox. This is somewhat equivalent to what most beginners do anyways, when they return HBox from their factory method instead of Region. Our example would look like this:\ntypealias ButtonRunner = (Runnable) -\u0026gt; Unit class CustomHBox(private val labelText: String, private val buttonText: String, private val boundProperty: StringProperty) : HBox() { private var buttonAction: ButtonRunner = {} private val actionRunning: BooleanProperty = SimpleBooleanProperty(false) init { createLayout() } private fun createLayout() { val textField = TextField().apply { textProperty().bindBidirectional(boundProperty) } children += Label(labelText) children += textField children += Button(buttonText).apply { onAction = EventHandler\u0026lt;ActionEvent\u0026gt; { actionRunning.value = true buttonAction { actionRunning.value = false } } defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind((textField.textProperty().isEmpty).or(actionRunning)) } alignment = Pos.CENTER_LEFT } fun setButtonAction(newAction: ButtonRunner) = this.apply { buttonAction = newAction } fun withSpacing(newSpacing: Double) = this.apply { spacing = newSpacing } } And you\u0026rsquo;d use it this way\u0026hellip;\nclass HBoxStyle : Application() { private val name: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val results: StringProperty = SimpleStringProperty(\u0026#34;No search done\u0026#34;) override fun start(stage: Stage) { stage.scene = Scene(createContent()) stage.show() } private fun createContent(): Region = BorderPane().apply { center = CustomHBox(\u0026#34;Last Name:\u0026#34;, \u0026#34;Search\u0026#34;, name).setButtonAction { postRunAction -\u0026gt; doSearch() PauseTransition(Duration(3000.0)).apply { onFinished = EventHandler { postRunAction.run() } play() } }.withSpacing(50.0) bottom = Label().apply { textProperty().bind(results) } padding = Insets(20.0) } private fun doSearch() { results.value = \u0026#34;Nothing found for: ${name.value}\u0026#34; } } fun main() = Application.launch(HBoxStyle::class.java) You can see that this implementation is only slightly different from the Builder structure. The build() method has been renamed to createLayout() and we now call it from init{} (which is almost like a constructor in Java). The method withSpacing() needed to be changed a little so that it delegates to HBox.setSpacing(), while still acting as a decorator.\nBut, why is this not recommended?\nFourth Version - Extend Region # The problem with creating a custom class by extending HBox is that you expose all of the methods of HBox to the client code. This includes, for instance, HBox.getChildren(). And that means that someone can add to and remove stuff from your layout. Or they could decide to modify the TextField somehow; just call HBox.getChildren() stream or loop through the List until you find the TextField and then start messing with it.\nIs this a big deal???\nThat depends. If you\u0026rsquo;re just working by yourself and you know you aren\u0026rsquo;t going to do those things (until you do), or if you know you can deal with the consequences, then it\u0026rsquo;s probably not a big deal.\nHowever, if you\u0026rsquo;re working in a bigger or more formal environment with other programmers, it can be huge. Part of the beauty of having a library of custom components is that - especially over time - you know that they work, and have been tested and proven. So you don\u0026rsquo;t even have to think about them much. You use them and you rely on them.\nBut if you have a situation where someone has found it expedient to monkey with the inner workings of one of those components from your library in the client code, then the bets are off. And if you don\u0026rsquo;t catch that they\u0026rsquo;ve done it, then it could cause confusion.\nEven worse, in that situation you now have coupling between the client code and the implementation of your custom class. Let\u0026rsquo;s imagine that they\u0026rsquo;ve done something to the Button by grabbing the children of the HBox and then taking the 3rd element from the List. But then you decide to add graphic to the HBox as the first element. Now the Button is the 4th element and this change will break their client code.\nSo the best thing is to create a custom class that extends from something that doesn\u0026rsquo;t expose it\u0026rsquo;s inner workings to the client code. The easiest answer to that is to use Region as your base class. The reason for this is that getChildren() is protected in Region, and isn\u0026rsquo;t public until Pane and its subclasses. So you can create a class that extends from Region, and your client code can\u0026rsquo;t mess with its implementation:\nclass CustomRegion(private val labelText: String, private val buttonText: String, private val boundProperty: StringProperty) : Region() { private var buttonAction: ButtonRunner = {} private val actionRunning: BooleanProperty = SimpleBooleanProperty(false) private val hBox: HBox init { hBox = createLayout() children.add(hBox) } private fun createLayout() = HBox().apply { val textField = TextField().apply { textProperty().bindBidirectional(boundProperty) } children += Label(labelText) children += textField children += Button(buttonText).apply { onAction = EventHandler\u0026lt;ActionEvent\u0026gt; { actionRunning.value = true buttonAction { actionRunning.value = false } } defaultButtonProperty().bind(textField.focusedProperty().and(textField.textProperty().isNotEmpty)) disableProperty().bind((textField.textProperty().isEmpty).or(actionRunning)) } alignment = Pos.CENTER_LEFT } fun setButtonAction(newAction: ButtonRunner) = this.apply { buttonAction = newAction } fun withSpacing(newSpacing: Double) = this.apply { hBox.spacing = newSpacing } } class RegionStyle : Application() { private val name: StringProperty = SimpleStringProperty(\u0026#34;\u0026#34;) private val results: StringProperty = SimpleStringProperty(\u0026#34;No search done\u0026#34;) override fun start(stage: Stage) { stage.scene = Scene(createContent()) stage.show() } private fun createContent(): Region = BorderPane().apply { center = CustomRegion(\u0026#34;Last Name:\u0026#34;, \u0026#34;Search\u0026#34;, name).setButtonAction { postRunAction -\u0026gt; doSearch() PauseTransition(Duration(3000.0)).apply { onFinished = EventHandler { postRunAction.run() } play() } }.withSpacing(50.0) bottom = Label().apply { textProperty().bind(results) } padding = Insets(20.0) } private fun doSearch() { results.value = \u0026#34;Nothing found for: ${name.value}\u0026#34; } } fun main() = Application.launch(RegionStyle::class.java) This is very similar to the HBox version except that the HBox is now added in as the sole element in Region.getChildren(). So we have our same HBox wrapped in a Region.\nJust doing that hides away the HBox. None of the client code can call Region.getChildren() because it\u0026rsquo;s protected.\nIt does mean that we have to deal with the Spacing issue. Since our client code can\u0026rsquo;t just call HBox.setSpacing(), we\u0026rsquo;ll have to provide a method in our custom class to provide this functionality. Here we have withSpacing() implemented as a decorator.\nFifth Version - Custom, Skinnable Control # We aren\u0026rsquo;t going to go into the details of this here, as it\u0026rsquo;s tutorial or two all in itself, but we\u0026rsquo;ll talk about it so tha you can know when it would be appropriate to use.\nOur CustomRegion is designed such that the GUI implementation is muddled up with the control code. It\u0026rsquo;s not a big deal here, because it\u0026rsquo;s still pretty simple, but it could be an issue if you wanted to change the implementation of the GUI.\nThe JavaFX class, Control extends Region and implements an interface called Skinnable. The design essentially allows an MVC-type structure to be used. Since there\u0026rsquo;s never any business/application logic to implement (it\u0026rsquo;s a GUI widget, after all), and the Model won\u0026rsquo;t have domain objects, the Model is essentially just the Presentation Model. The View implementation is done in a class that implements an interface called, Skin. The Control class becomes the Controller contains the Properties that comprise the Presentation Model, and the View is implemented in the Skin.\nIn practice, unless you want a client project to be able to rework the Skin, there\u0026rsquo;s very little reason to go through the trouble of creating a Skinnable Control. You can do virtually everything you need by extending Region. So we aren\u0026rsquo;t going to look at how to do this in this article as your not likely to need it building real applications.\nConclusion # I think the key idea is that every programmer, or every programming shop, is going to evolve their own conventions about how different layout components are going to be used and put together. There\u0026rsquo;s often lots of different ways to get to very similar outcomes in JavaFX, and best way is usually the one that makes the most sense to the programmers doing the coding.\nThat means that you\u0026rsquo;re going to find yourself using the same patterns over and over again, and you don\u0026rsquo;t want to be reinventing the wheel every time. And you certainly don\u0026rsquo;t want those details clogging up your layout code.\nMove the code that implements those patterns out into factory methods in your layout code, then if you use the same patterns in another layout, move them out into a library of factory methods that you can use anywhere. Most of your \u0026ldquo;custom widgets\u0026rdquo; are going to be implemented this way.\nWhen things get a little more complicated, or you need to tune the implementation to handle a variety of situations, then it might be time to implement a Builder class for your custom widget. If the implementation needs to be dynamic and respond to changes in the state of your GUI, then you might need to create a custom class that extends Region.\n","date":"27 February 2023","externalUrl":null,"permalink":"/javafx/techniques/custom_controls_when/","section":"JavaFX","summary":"Creating a custom control really isn’t that hard to do.  But when should you use one, and when should you just use a builder to create a layout?","title":"When and How to Create Custom Controls","type":"javafx"},{"content":" Introduction # There was a question that came up on StackOverflow about how to create a three way toggle switch that looked like something on an Apple AirPods menu:\nThe question was deemed to complex for StackOverflow and was closed, but I thought the principle was something that should be explored in any case. I took a swing at building something, and this is what I came up with:\nWhich I think looks a lot like the original, at least in spirit.\nNote: This article is still a \u0026ldquo;work in progress\u0026rdquo;, but I\u0026rsquo;m pushing it out live as soon as possible so that it might help the user that posted the original question on StackOverflow.com. I expect to change some of the code, and flush out the discussion about the approach in the next few days. That said, the code does work as it is and should be a reasonable starting place to build your own version.\nThe Design # It was pretty simple to build the layout. It\u0026rsquo;s just VBox with two HBoxes inside of it. The top HBox has a set of ToggleButtons and it\u0026rsquo;s styled to look like the track that a flip switch would travel along. The ToggleButtons are round, and they have a unique icon in each one.\nThe second HBox has the Labels that go along with the ToggleButtons. They need to all be spaced out evenly to make everything look right.\nI decided to implement this as custom class, extending Pane. Pane was as high up as I could go, since it\u0026rsquo;s the first one that supports getChildren(), meaning that I could put things in it.\nMy goal was to create something that was generically useful. By this, I mean a stand-alone Node that could be deployed just like any other JavaFX Node, and would behave substantially like all those other Nodes. This meant that I would need to provide properties and methods that would allow the layout code to configure it as required, and they would need to be accessed in the same way as the other Nodes.\nThere\u0026rsquo;s a Value property, and it\u0026rsquo;s defined as a JavaFX Bean. This means that there\u0026rsquo;s a valueProperty() method, a setValue() method and a getValue() method. The latter two are delegates to the Value property\u0026rsquo;s getValue() and setValue().\nI also created an Enum for this called ToggleSelection. It only has three values: LEFT, CENTRE and RIGHT. The layout code is going to need to translate this into something that relates to its Model.\nI wanted to keep the constructor simple, so it just takes the Strings for the three labels. To change the icons, there are three StringProperties deployed as Beans: LeftIcon, CentreIcon and RightIcon.\nThe Code # class ToggleSwitch(private val label1: String, private val label2: String, private val label3: String) : Pane() { private val _value: ObjectProperty\u0026lt;ToggleSelection\u0026gt; = SimpleObjectProperty(ToggleSelection.CENTRE) var value: ToggleSelection get() = _value.get() set(newValue) = _value.set(newValue) private val standardWidthProperty: DoubleProperty = SimpleDoubleProperty(0.0) private val theToggles = ToggleGroup() private val _leftIcon: StringProperty = SimpleStringProperty(\u0026#34;captainicon-146\u0026#34;) var leftIcon: String get() = _leftIcon.get() set(value) = _leftIcon.set(value) private val _centreIcon: StringProperty = SimpleStringProperty(\u0026#34;captainicon-150\u0026#34;) var centreIcon: String get() = _centreIcon.get() set(value) = _centreIcon.set(value) private val _rightIcon: StringProperty = SimpleStringProperty(\u0026#34;captainicon-158\u0026#34;) var rightIcon: String get() = _rightIcon.get() set(value) = _rightIcon.set(value) private val leftButton = createButton(_leftIcon) private val centreButton = createButton(_centreIcon) private val rightButton = createButton(_rightIcon) fun leftIconProperty() = _leftIcon fun centreIconProperty() = _centreIcon fun rightIconProperty() = _rightIcon fun valueProperty() = _value init { children += createContent() minWidth = 400.0 _value.bind(Bindings.createObjectBinding(this::checkValue, theToggles.selectedToggleProperty())) centreButton.isSelected = true } private fun checkValue(): ToggleSelection { return when (theToggles.selectedToggle) { leftButton -\u0026gt; ToggleSelection.LEFT centreButton -\u0026gt; ToggleSelection.CENTRE rightButton -\u0026gt; ToggleSelection.RIGHT else -\u0026gt; ToggleSelection.CENTRE } } private fun createContent(): Region = VBox(10.0).apply { children += buttonBox() children += labelBox() isFillWidth = false alignment = Pos.CENTER } private fun labelBox(): Region = HBox(20.0).apply { val labels = listOf(toggleLabel(label1), toggleLabel(label2), toggleLabel(label3)) children += labels standardWidthProperty.bind(Bindings.createDoubleBinding({ labels.maxOfOrNull { label -\u0026gt; label.width } }, *(labels .map { label -\u0026gt; label.widthProperty() } .toTypedArray()))) labels.forEach { label -\u0026gt; label.minWidthProperty().bind(standardWidthProperty) } alignment = Pos.CENTER } private fun toggleLabel(labelText: String): Region = Label(labelText).apply { styleClass += \u0026#34;toggle-label\u0026#34; alignment = Pos.CENTER } private fun buttonBox(): Region = HBox(20.0).apply { children += listOf(leftButton, centreButton, rightButton) spacingProperty().bind(standardWidthProperty.subtract(20.0)) styleClass += \u0026#34;button-box\u0026#34; } private fun createButton(iconName: StringProperty) = ToggleButton().apply { styleClass += \u0026#34;toggle-switch-button\u0026#34; theToggles.toggles += this graphicProperty().bind(Bindings.createObjectBinding({ createIcon(iconName.value) }, iconName)) } private fun createIcon(iconName: String) = FontIcon(iconName).apply { iconSize = 18 iconColor = Color.WHITE } } enum class ToggleSelection { LEFT, CENTRE, RIGHT } You can test it with this:\nclass ToggleSwitchDemo : Application() { private val nameProperty: StringProperty = SimpleStringProperty(\u0026#34;Not Started\u0026#34;) private var counter: Int = 0 override fun start(primaryStage: Stage) { primaryStage.scene = Scene(createContent()).addWidgetStyles().apply { object {}::class.java.getResource(\u0026#34;/css/toggleswitch.css\u0026#34;)?.toString()?.let { stylesheets += it } } primaryStage.show() } private fun createContent(): Region = BorderPane().apply { center = ToggleSwitch(\u0026#34;Noise Cancellation\u0026#34;, \u0026#34;Off\u0026#34;, \u0026#34;Transparency\u0026#34;).apply { valueProperty().addListener { _, _, newValue -\u0026gt; println(\u0026#34;New Value: $newValue\u0026#34;) } } } padWith 20.0 } fun main() = Application.launch(ToggleSwitchDemo::class.java) And\u0026hellip;you\u0026rsquo;ll need this Style Sheet:\n.root { -toggle-background: rgba(215,225,245,1); } .toggle-switch-button { -fx-min-width: 40.0; -fx-min-height: 37.0; -fx-background-radius: 40.0; -fx-background-color: derive(-toggle-background, -3%); } .toggle-switch-button:selected { -fx-background-color: cornflowerblue; } .toggle-label { -fx-text-fill: midnightblue; -fx-font-weight: bold; -fx-font-size: 11px; } .button-box { -fx-background-radius: 20; -fx-background-color: -toggle-background; -fx-padding: 4px; } Notes on the Code # First of all, this is Kotlin. If you\u0026rsquo;re confused by the Kotlin, then you can take a look at this article and this article for some help.\nThe first part of the code is setting up the Java Beans. I\u0026rsquo;m using a technique that was described in this article. The net result is that you get the three public functions that you need for a Bean.\nThe init{} section is kind of like a constructor, and it calls the layout function and connects the Value property to the ToggleGroup.\ncreateContent() gets the layout started. It creates a VBox and the calls two builder methods to create the HBoxes with the Buttons and Labels.\nThe only tricky part of the layout is getting the Labels spread out evenly in a generic manner, and keeping the ToggleButtons right on top of them.\nTo keep the Labels spread out, they need to all set to the same width, and that width needs to be enough to hold the biggest label. The obvious approach is the stream through the labels, and grab the biggest width, then set the MinWidth property of all three labels to that value.\nHowever\u0026hellip;\nWhen the layout is built, nothing\u0026rsquo;s actually in the GUI, so any calls to look at the size of the Label.getWidth() will just return \u0026ldquo;0.0\u0026rdquo;. So that\u0026rsquo;s not gonna work.\nHowever\u0026hellip;\nThe Width of the Labels is a Property. This means that we can set a Binding on it. Yeah! And this is what we do here\u0026hellip;create a Binding that\u0026rsquo;s dependent on the widthProperty() of all three labels, and that binding then streams through the Labels to grab the biggest width.\nWe\u0026rsquo;re also going to need that biggest width to space out our ToggleButtons. The best way to implement this is to create a private DoubleProperty field in the class, then directly bind that Property to our biggest width Binding. Then we can bind the minWidth() of each Label to this DoubleProperty.\nFor the ToggleButtons, we need to use this biggest width DoubleProperty to provide the spacing between the Buttons. This is easy, because we have HBox.spacingProperty(), and we can bind that to our biggest width Property with a slight adjustment for the actual width of the Buttons.\nIkonli # This code uses FontAwesome through the Ikonli library. You\u0026rsquo;ll need to add the dependencies to your project to use it:\nimplementation(\u0026#39;org.kordamp.ikonli:ikonli-javafx:12.2.0\u0026#39;) implementation \u0026#39;org.kordamp.ikonli:ikonli-captainicon-pack:12.3.1\u0026#39; The first dependency is the actual Ikonli library, and the second is the icon pack that I used.\nThis is using a slightly older version of the Ikonli library, and it looks like the newer versions handle\n","date":"16 February 2023","externalUrl":null,"permalink":"/javafx/techniques/tripletoggle/","section":"JavaFX","summary":"How to create custom control in JavaFX.  Here we look at how to build a three position toggle switch.","title":"JavaFX Custom Control - The Triple Toggle Switch","type":"javafx"},{"content":" What is Kotlin? # Kotlin is a multi-purpose programming language that runs in the JVM. In many ways, it\u0026rsquo;s what Java would have been had it been designed 20 years later.\nKotlin code is much more concise than the equivalent Java. It has native features that address many of the gripes that programmers have about Java, including the following:\nNull safety is built-in. Fields are implemented as Properties that include default (but configurable) getters and setters. The ability to add methods and fields to classes without formally extending them. Semicolons are optional! Automatic inference of data types when declaring Variables Default values in method signatures, reducing the need to overload methods. Kotlin is 100% compatible with Java, so you can freely use any libraries that are written in or for Java. With a few caveats, you can also call all of you Kotlin code from Java. You can even mix Kotlin in with Java in the same project!\nOf course, Kotlin works really well with JavaFX. In fact, JavaFX is way better with Kotlin than it is with Java.\nRead More\u0026hellip; # I\u0026rsquo;ve written a few articles about Kotlin, you can find them here:\n","date":"28 January 2023","externalUrl":null,"permalink":"/programming/kotlin/","section":"Programming","summary":"What is Kotlin? # Kotlin is a multi-purpose programming language that runs in the JVM. In many ways, it’s what Java would have been had it been designed 20 years later.\n","title":"Kotlin","type":"programming"},{"content":" Introduction # I\u0026rsquo;ve been using Kotlin for a while now, and I\u0026rsquo;ve been using it to write JavaFX applications. What I\u0026rsquo;ve found is that the things that make Kotlin better than Java, make Kotlin way, way better for JavaFX.\nIf you\u0026rsquo;re new to Kotlin, you might want to read my Kotlin for Java Programmers article.\nLet\u0026rsquo;s get one thing out of the way first\u0026hellip;\nDo You Need TornadoFX? # No.\nA lot of people are under the impression that TornadoFX is the implementation of JavaFX in Kotlin, and that you need it in order to use JavaFX with Kotlin.\nThis is simply not true. TornadoFX is an interesting library that does some cool things, but it\u0026rsquo;s not any kind of replacement for the JavaFX library.\nSo, what is TornadoFX?\nMore than anything else, it\u0026rsquo;s a wrapper for the standard JavaFX library that attempts to make using JavaFX less verbose and more \u0026ldquo;Kotlin-like\u0026rdquo;. It tackles the verbosity by supplying generic \u0026ldquo;builders\u0026rdquo; that make use of extension functions on Node classes to enable a more declarative style of configuration for layouts. It also incorporates tools to use an MVC framework, also in a manner to make it as less verbose as possible.\nAnd it\u0026rsquo;s very good at doing this. But this approach has a couple of issues:\nRepeated Configurations # If you spend any amount of time programming with JavaFX, you\u0026rsquo;re going to find that there are certain patterns of configuration and styling that you use over and over and over. And, if you\u0026rsquo;re using style sheets to handle your styling (as you should), then you\u0026rsquo;re going to be using most of those styles in the same way, over and over and over.\nWant you really want to avoid is repeating that code over and over and over. Apply DRY (Don\u0026rsquo;t Repeat Yourself), and put that code in a library that you can call. Any such library that you create is going to be highly personalized to yourself or your team.\nTornadoFX, is all about making it easier to write that code inside your layout. In many cases your layout code becomes composed of configuring builders that will generate your layout. The builders are nicely designed to streamline the code, but don\u0026rsquo;t move you towards DRY. You can probably do it, but it moves your task from writing builders for Nodes to writing builders for TornadoFX builders.\nConfiguration and Styling in the Layout # Let\u0026rsquo;s take a look at an example from the TornadoFX documentation, this shows a call to one of their builders:\ntableview\u0026lt;Person\u0026gt; { items = persons column(\u0026#34;ID\u0026#34;, Person::idProperty) column(\u0026#34;Name\u0026#34;, Person::nameProperty) column(\u0026#34;Birthday\u0026#34;, Person::birthdayProperty) readonlyColumn(\u0026#34;Age\u0026#34;, Person::age).cellFormat { text = it.toString() style { if (it \u0026lt; 18) { backgroundColor += c(\u0026#34;#8b0000\u0026#34;) textFill = Color.WHITE } } } } This would become your layout code with TornadoFX. You can see how this strips away a lot of the boilerplate that you\u0026rsquo;d need with a pure JavaFX implementation, and the result absolutely does do more with less.\nBut that \u0026ldquo;Age\u0026rdquo; column.\nYes, the customization of the Cell is way more straightforward than in pure JavaFX, but it\u0026rsquo;s still a clear violation of the \u0026ldquo;Single Responsibility Principle\u0026rdquo; - it should be delegated.\nBut the design of TornadoFX encourages you to put the configuration and styling in with the layout.\nSo, the question is whether or not the streamlining of the code compensates for the complication of having the configuration and styling mingled in with the layout.\nShould You Use TornadoFX? # If you\u0026rsquo;re following the ideas in this website - building Reactive layouts with MVCI and sticking to the \u0026ldquo;Single Responsibility Principle\u0026rdquo; and DRY - there\u0026rsquo;s probably not a lot of benefit to using TornadoFX.\nTornadoFX tries to deal with the complexity by simplifying the integration of the configuration into the layout code. What it doesn\u0026rsquo;t do is remove the configuration from your layouts, which, in my opinion, is a better approach.\nKotlin JavaFX Techniques # There are a number of cool features in Kotlin that make JavaFX a lot easier to implement, and some features that mean you need to take a look at how you should build things in Kotlin. Let take a look at some of these\u0026hellip;\nThe .apply{} Function # This is the star in Kotlin for JavaFX. When you call Object.apply{} it passes the object to the lambda in the {} as this, and apply{} returns the original object. This is especially useful in the pattern val x = Node().apply{}.\nLet\u0026rsquo;s say you\u0026rsquo;ve got a Label that you want have display some data the screen and bound to a Property in your model. You have a style class set up for, too.\nIn Java:\nLabel label = new Label(); label.getStyleClass().add(\u0026#34;data-label\u0026#34;); label.textProperty().bind(model.someDataProperty()); hbox.getChildren().add(label); But in Kotlin, you can use .apply{} and avoid instantiating label altogether:\nhbox.children += Label().apply{ styleClass += \u0026#34;data-label\u0026#34; textProperty().bind(model.someData) } (Note that, just like with a class in Java, you can skip the this. when referring to member fields and functions)\nNow, that might not seem like much, but it does lead to things like this:\nhBox = HBox(10.0, Label(\u0026#34;Name: \u0026#34;).apply{styleClass += \u0026#34;prompt-label\u0026#34;}, Label().apply{ styleClass += \u0026#34;data-label\u0026#34; textProperty().bind(model.someData) }) This can get a bit garbled (something to watch out for with .apply{}), so you could try:\nhBox = HBox(10.0).apply{ children += Label(\u0026#34;Name: \u0026#34;).apply{ styleClass += \u0026#34;prompt-label\u0026#34; } children += Label().apply{ styleClass += \u0026#34;data-label\u0026#34; textProperty().bind(model.someData) } } Perhaps even more useful is that you create functions using the same technique:\nhBox = HBox(10.0).apply{ children += listOf(Label(\u0026#34;Name: \u0026#34;).apply{styleClass += \u0026#34;prompt-label\u0026#34;}, dataLabel(model.someData)) styleClass += \u0026#34;red-border-box\u0026#34; } fun dataLabel(boundValue : ObservableStringValue) = Label().apply{ styleClass += \u0026#34;data-label\u0026#34; textProperty().bind(boundValue) } JavaFX Property Fields # Kotlin uses the term \u0026ldquo;property\u0026rdquo; to refer to what would be called \u0026ldquo;fields\u0026rdquo; in Java. Kotlin properties are actually more complicated structures that have integrated getters and setters (which can be customized) which are automatically called when you access the property. This means that you can directly reference the property from outside the class without worrying about tying down your internal implementation.\nOf course, in JavaFX a Property is specific kind of ObservableValue, which is quite different from a Kotlin property. Additionally, there is the concept of a Property Bean, which is a particular structure exposing the Property outside its containing class.\nA Property Bean has three methods in its containing class. For a Property called fred you would have the following methods:\nA getter for the Property value called getFred(). A setter for the Property value called setFred(). A getter for the property itself called fredProperty() In Kotlin, you can\u0026rsquo;t just write the getFred() and setFred() methods because they would conflict with the internal getter and setter for fred. So you have to do something a little bit different. I wrote an entire article about this a while back.\nHere\u0026rsquo;s a sample program from the original article, showing how this is done.\n{% gist f6d3bd9dfc261b6d411d7945172d5833 %}\nAnd the output looks like this:\nNickname: Shorty Property: ObjectProperty [value: Shorty] That gets the job done, and it allows you direct access to the values of the Property via Class.fred.\nThe JavaFX Property Bean is the accepted way to implement Properties in a Model, but\u0026hellip;\nDo you really need Property Beans? # This naming convention is primarily designed to facilitate those tools that use reflection in the class PropertyReference to access fields via a String name. In the standard JavaFX library, this class is used in the following places: PropertyValueFactory, TreeViewPropertyValueFactory and variants of Bindings.select(). The first two should probably be deprecated, because you shouldn\u0026rsquo;t use them any more now that we have lambdas and writing the equivalent Callback is trivial.\nThe last case is about 14 methods in the Bindings class that, \u0026ldquo;Create a binding used to get a member, such as a.b.c.\u0026rdquo;, according to the JavaDocs. Personally, I have never used Bindings.select(), and I suspect that it\u0026rsquo;s primarily intended when you want to pass around Property names as Strings and then directly use those names for binding.\nOf course, if you\u0026rsquo;re using external JavaFX libraries, they may require that you use the Property Bean format because they are using something that incorporates PropertyReference.\nBut\u0026hellip;if you\u0026rsquo;re not doing any of those things, you can ditch the Property Bean. Just make your Property property a val you\u0026rsquo;re done.\nPersonally, I\u0026rsquo;ve stopped habitually using the Property Bean pattern because it just doesn\u0026rsquo;t add any value for me.\nExtension Functions, Package Functions and Operator Overloading # The next few features go together produce some really powerful techniques which are the core tools you can use to compress your layout code.\nExtension Functions # One of the coolest features of Kotlin is the ability to extend a class without creating a new class. With this technique you can add both methods and data to an existing class without having access to the source of that class.\nFor instance, you can do something like this:\nfun Int.plusThree() : Int = this + 3 . . . val x: Int = 7 println(\u0026#34;The answer: ${x.plusThree()}\u0026#34;) This can be amazingly valuable with JavaFX, where there are a lot of classes that you use all the time where it would be handy to have methods that do something with a single call that normally take a few steps.\nExtension functions can also be used as decorators using .apply{}:\nprivate fun createContent(): Region = BorderPane().apply { val model: StringProperty = SimpleStringProperty(\u0026#34;Albert\u0026#34;) padding = Insets(20.0) center = HBox(10.0, Label().bound(model), Label(\u0026#34;George\u0026#34;)) .styled(\u0026#34;test-border3\u0026#34;) .padded(10.0) .aligned(Pos.CENTER) } } fun \u0026lt;T : Node\u0026gt; T.styled(newStyleClass: String): T = apply { styleClass += newStyleClass } fun \u0026lt;T : Region\u0026gt; T.padded(padSize: Double): T = apply { padding = Insets(padSize) } fun \u0026lt;T : Labeled\u0026gt; T.bound(otherProperty: ObservableValue\u0026lt;String\u0026gt;): T = apply { textProperty().bind(otherProperty) } fun HBox.aligned(pos: Pos): HBox = apply { alignment = pos } For this, however, it\u0026rsquo;s important to remember that extension functions are evaluated statically. This means that if you want a decorator to return the original type for any of the subclasses of a type you need to use the \u0026lt;T : Class\u0026gt; T. declaration. In this example, the setAlignment() method is included at the HBox class itself, so you can declare the decorator directly at HBox; getStyleClass() is defined at the Node level, so \u0026lt;T: Node\u0026gt; T is used; and setPadding() is defined at the Region level, so \u0026lt;T: Region\u0026gt; T is used.\nPackage Functions # Another cool feature in Kotlin is \u0026ldquo;Package Functions\u0026rdquo;. These are methods that aren\u0026rsquo;t part of any specific class, they\u0026rsquo;re just functions attached to a package. In this way they are very similar to static methods in Java, but you don\u0026rsquo;t have the baggage of having to call them with ClassName.staticMethod() format. This makes a dramatic difference in how the resulting code looks:\nfun labelOf(boundValue : StringProperty) : Label = Label().apply {textProperty().bind(boundValue)} . . . val hbox = HBox(10.0, Label(\u0026#34;Name:\u0026#34;), labelOf(model.nameProperty)) Operator Overloading # Operator Overloading is another nifty idea that Kotlin brings to the table which can simplify your layout code. The idea is that a specific set of operators are automatically associated with specific functions if they are defined in a class as operator. So the \u0026ldquo;+\u0026rdquo; operator is associated with class.plus() if it exists.\nBut here\u0026rsquo;s the cool part: The operator functions can be extension functions!\nThis means that you can add operator overloads to any of the JavaFX classes. Let\u0026rsquo;s see how this works. To me, the \u0026ldquo;+=\u0026rdquo; operator seems like a good fit to represent binding on a Property. Here\u0026rsquo;s how you would do that:\noperator fun StringProperty.plusAssign(otherProperty: StringProperty) = this.bind(otherProperty) . . . val model : StringProperty = SimpleStringProperty(\u0026#34;ABC\u0026#34;) val label = Label() label.textProperty() += model Or, if you prefer\u0026hellip;\noperator fun Labeled.plusAssign(otherProperty: StringProperty) = this.textProperty().bind(otherProperty) . . . val model : StringProperty = SimpleStringProperty(\u0026#34;ABC\u0026#34;) val label = Label() label += model Infix Functions # If a function of a class has a single parameter, then you can declare it as Infix. This means that you can drop the \u0026ldquo;.\u0026rdquo; and the brackets when making the call. The result is something that looks very declarative. This is especially true if you implement decorator functions as infix:\ninfix fun \u0026lt;T : Labeled\u0026gt; T.styledAs(labelStyle: String) = apply { styleClass += labelStyle } infix fun \u0026lt;T : Labeled\u0026gt; T.boundTo(value: ObservableStringValue) = apply { textProperty().bind(value) } fun testIt(): Unit { val fred: StringProperty = SimpleStringProperty(\u0026#34;abc\u0026#34;) val label1 = Label() styledAs \u0026#34;label-prompt\u0026#34; boundTo fred val label3 = Label().styledAs(LabelStyle.PROMPT).boundTo(fred) } The first two methods are declared as infix, but you don\u0026rsquo;t have to use them that way. These are all also decorator style methods that return the enclosing object. That means that you can string them together. The first calling example shows how this works, and the second shows it without the infix calling style. The two versions are functionally equivalent.\nYou have to be a little bit careful with the order of operations with infix, but chaining the decorators together like this works nicely.\nPutting it All Together # If you\u0026rsquo;re looking to create a standard toolkit that you can use over a variety of projects, then your best approach is to create Kotlin files in their own package to house your functions. You can call it FxExtentions.kt or ExtensionsFX.kt or WidgetsFX.kt. You don\u0026rsquo;t need to have a class of the same name, you can just drop all of your package functions and extension functions in there as you like. You can even add some custom classes, Kotlin\u0026rsquo;s cool with that.\nThis will get all this stuff right out of your layout code, and out of your mind - where you don\u0026rsquo;t want it.\nMy approach is to create a complementary CSS file to go with the code and then write the various utility functions to work with that CSS file.\nNaming becomes very important, and you have to think about how the code will read using the names that picked. This could also depend on how you intend to use the utilities. Consider this:\nval label = Label() styledAs LabelStyle.DATA boundTo model.someDataProperty Here the decorators have all been name as past-tense verbs, essentially declaring their effects. I think this reads nicely as a description of the Node; it\u0026rsquo;s a Label styled as data and bound to a property.\nHowever, you could use this:\nval label = Label() styleAs LabelStyle.DATA bindTo model.someProperty Now the decorators read as commands. This version is more declarative, and not descriptive.\nBut, if you don\u0026rsquo;t like the infix usage, then you would have:\nval label = Label.styledAs(LabelStyle.DATA).boundTo(model.someDataProperty) or\u0026hellip;\nval label = Label.styleAs(LabelStyle.DATA).bindTo(model.someDataProperty) Since we\u0026rsquo;re used to traditional function call being named as actions, the second version feels more natural. But both ways could possibly work.\nUtility Library Files # With these techniques you can create a toolkit that will make your Kotlin layout code look much more streamlined and declarative than Java code. The trick is to find all the \u0026ldquo;pain points\u0026rdquo; in your layout code, the places that you get bogged down in details, and move them into your library of functions.\nHere\u0026rsquo;s what I came up with in just a few minutes. First, WidgetFX.kt:\npackage ca.pragmaticcoding.widgetsfx fun Scene.addWidgetStyles() = apply { object {}::class.java.getResource(\u0026#34;widgetsfx.css\u0026#34;)?.toString()?.let { stylesheets += it } } fun \u0026lt;T : Parent\u0026gt; T.addWidgetStyles() = apply { object {}::class.java.getResource(\u0026#34;widgetsfx.css\u0026#34;)?.toString()?.let { stylesheets += it } } enum class TestStyle(val selector: String) { BLUE(\u0026#34;test-blue\u0026#34;), RED(\u0026#34;test-red\u0026#34;), GREEN(\u0026#34;test-green\u0026#34;) } infix fun \u0026lt;T : Node\u0026gt; T.testStyleAs(nodeStyle: TestStyle) = apply { styleClass += nodeStyle.selector } infix fun \u0026lt;T : Region\u0026gt; T.padWith(padSize: Double): T = apply { padding = Insets(padSize) } infix fun \u0026lt;T : Node\u0026gt; T.addStyle(newStyleClass: String): T = apply { styleClass += newStyleClass } fun textFieldOf(value: StringProperty) = TextField().apply { textProperty().bind(value) } infix fun TextField.bindTo(value: StringProperty) = apply { textProperty().bind(value) } fun buttonOf(text: String, handler: EventHandler\u0026lt;ActionEvent\u0026gt;) = Button(text) addAction handler operator fun Pane.plusAssign(newChild: Node) { children += newChild } infix fun HBox.alignTo(pos: Pos): HBox = apply { alignment = pos } infix fun \u0026lt;T : ButtonBase\u0026gt; T.addAction(eventHandler: EventHandler\u0026lt;ActionEvent\u0026gt;): T = apply { onAction = eventHandler } This is just a grab-bag of functions and utilities that aren\u0026rsquo;t necessarily related to each other.\nThere are decorators to add the widgetsfx.css stylesheet to either a Scene or a Parent. Since these are declared at a package level, there\u0026rsquo;s no class to provide getResource() to find the stylesheet. Instead the function declares an anonymous Object that will have the correct package which can provide the getResource() method.\nRegion.padWith() is super useful. It gets absolutely tedious writing setPadding(new Insets(10)) over and over and over. The vast majority of the time, you just want all of the sides to have the same padding so a function that just takes a single Double parameter makes a huge difference.\nand then Labels.kt\npackage ca.pragmaticcoding.widgetsfx enum class LabelStyle(val selector: String) { PROMPT(\u0026#34;label-prompt\u0026#34;), HEADING(\u0026#34;label-heading\u0026#34;) } infix fun \u0026lt;T : Labeled\u0026gt; T.styleAs(labelStyle: LabelStyle) = apply { styleClass += labelStyle.selector } infix fun \u0026lt;T : Labeled\u0026gt; T.bindTo(value: ObservableStringValue) = apply { textProperty().bind(value) } fun promptOf(value: ObservableStringValue) = Label() styleAs LabelStyle.PROMPT bindTo value fun promptOf(value: String) = Label(value) styleAs LabelStyle.PROMPT fun headingOf(value: String) = Label() styleAs LabelStyle.HEADING operator fun Labeled.plusAssign(otherProperty: StringProperty) = run { textProperty() += otherProperty } For this file, I\u0026rsquo;ve grouped together all of the functions that deal with Labels strictly as an organizational tool.\nIn both of these files, I\u0026rsquo;ve declared an Enum to abstract the CSS selector classes so that they don\u0026rsquo;t need to be known outside the utility files.\nObviously, these functions require a complementary style sheet to work with:\n.root { -theme-colour: #113969; -contrast-colour: #C6522F; -complementary1: #43AA8B; -complementary2: #B4CDED; -complementary3: #80A4ED } .test-blue { -fx-border-color: blue; -fx-background-color : lightcyan; } .test-red { -fx-border-color: firebrick; -fx-background-color : lavenderblush; } .test-green { -fx-border-color: green; -fx-background-color : derive(yellowgreen, +50%); } .label-prompt { -fx-text-fill: -theme-colour; -fx-font-weight: bold; -fx-font-size: 15px; } .label-heading { -fx-text-fill: -contrast-colour; -fx-font-weight: bold; -fx-font-size: 32px; } Using It # Good layout code should make it easy for any reader to understand at a glance how a layout works. This means keeping the configuration out of the way, as this is what causes confusion. But how you use these library functions can make a big difference to readability.\nFor instance, this is hard to read (horrible, actually):\nprivate fun createContent(): Region = BorderPane().apply { top = headingOf(\u0026#34;Test Screen\u0026#34;) center = VBox(20.0).apply { children += HBox(10.0).apply { children += Label(\u0026#34;Name\u0026#34;) styleAs LabelStyle.PROMPT children += TextField() bindTo nameProperty } padWith 10.0 alignTo Pos.CENTER_LEFT children += Button(\u0026#34;Click Me\u0026#34;) addAction { buttonAction() } } } testStyleAs TestStyle.BLUE padWith 20.0 and this is maybe a bit easier, but still too difficult:\nprivate fun createContent(): Region = (BorderPane() testStyleAs TestStyle.BLUE padWith 20.0).apply { top = headingOf(\u0026#34;Test Screen\u0026#34;) center = VBox(20.0, HBox(10.0, Label(\u0026#34;Name\u0026#34;) styleAs LabelStyle.PROMPT, TextField() bindTo nameProperty) padWith 10.0 alignTo Pos.CENTER_LEFT, Button(\u0026#34;Click Me\u0026#34;) addAction { buttonAction() }) } but specific builders work best, so this is probably clear enough:\nprivate fun createContent(): Region = BorderPane().apply { top = headingOf(\u0026#34;Test Screen\u0026#34;) center = VBox(20.0, createNameRow(), buttonOf(\u0026#34;Click Me\u0026#34;) { buttonAction() }) this testStyleAs TestStyle.BLUE padWith 20.0 } private fun createNameRow() = HBox(10.0, promptOf(\u0026#34;Name\u0026#34;), textFieldOf(nameProperty)) padWith 10.0 alignTo Pos.CENTER_LEFT But this is probably best of all:\nprivate fun createContent(): Region = BorderPane().apply { top = headingOf(\u0026#34;Test Screen\u0026#34;) center = VBox(20.0, createNameRow(), createButton()) } testStyleAs TestStyle.BLUE padWith 20.0 private fun createButton() = buttonOf(\u0026#34;Click Me\u0026#34;) { buttonAction() } private fun createNameRow() = HBox(10.0, promptOf(\u0026#34;Name\u0026#34;), textFieldOf(nameProperty)) padWith 10.0 alignTo Pos.CENTER_LEFT There\u0026rsquo;s no doubt that this last version achieves the \u0026ldquo;at a glance\u0026rdquo; objective of the layout code. You can see that the BorderPane has the top and centre occupied, and you can see that the centre has two rows in it. If you\u0026rsquo;re curious about the \u0026ldquo;Name\u0026rdquo; row, or the Button you can click through to them in the code. This is really no different than the best approach in Java, but the apply{}, the builders, and the infix functions strip out all of the extraneous boilerplate that would otherwise bloat the layout code.\nCoroutines # Coroutines are a neat feature that allow you to run multiple blocking operations on a single thread. When one task blocks, it doesn\u0026rsquo;t halt the thread, but is \u0026ldquo;parked\u0026rdquo; and the thread is freed up to run other tasks. As each task blocks, Kotlin can then \u0026ldquo;un-park\u0026rdquo; a previously parked task that has become unblocked, and run it on the thread until it either completes or becomes blocked again.\nThere is a special library (part of the Kotlin companion libraries for coroutines) that allows you to run coroutines on the FXAT. So that you can do something like this:\nprivate fun createContent(): Region = BorderPane().apply { padding = Insets(20.0) center = VBox(20.0, HBox(10.0, Label().bound(model)) .styled(\u0026#34;test-border3\u0026#34;) .padded(10.0) .aligned(Pos.CENTER), Button(\u0026#34;ClickMe\u0026#34;).apply { onAction = EventHandler { _ -\u0026gt; buttonAction() } }) } private fun buttonAction() { GlobalScope.launch(Dispatchers.JavaFx) { println(\u0026#34;Starting the function\u0026#34;) counter += 1 model.value = counter.toString() delay(10000) println(\u0026#34;Ending the function\u0026#34;) counter += 100 model.value = counter.toString() } } The Dispatchers.JavaFX tells it to launch the coroutine on the FXAT. Because of this, you can update model even though it\u0026rsquo;s bound to a screen element. The call to delay() is used to simulate a blocking operation. While the delay() is running, you can continue to use the GUI, and you can, in fact, click the Button again and launch another coroutine on the FXAT.\nAs you can see, running the code as a coroutine simply requires wrapping it in a call to GlobalScope.launch with the correct dispatcher. It\u0026rsquo;s really that trivial.\nPotentially, you could do any number of blocking operations that you\u0026rsquo;d normally use Task for using coroutines and it should work just fine. However you should keep a couple of things in mind:\nThis is only for blocking operations, like accessing an external API over a network. Running a very long process that simply takes a long time to complete is still going to hang your GUI.\nUsing this technique introduces the possibility of some of the concurrency issues that the single threaded FXAT is designed to avoid. This particular example is designed to demonstrate that each click of the button messes with the value of counter. If you had code that assumed that counter was equal to 101 after completion, it could fail.\nTo expand on the \u0026ldquo;concurrency issues\u0026rdquo; part: If you ran the same code without using coroutines, then the GUI would definitely hang for 10 seconds after you click the button. No jobs would get processed of the FXAT and no events would get handled. But you could guarantee that the state of the GUI would be exactly the same after the delay as it was before the delay(), and this includes model.value and counter. But with coroutines, when the execution continues after the delay() there\u0026rsquo;s no reason to believe that these things won\u0026rsquo;t have changed.\nCoroutines are generally considered an advanced technique in Kotlin, and are really included here for the sake of completeness. There are places where they can be of value, but you should probably continue to use Task for most situations.\n","date":"28 January 2023","externalUrl":null,"permalink":"/programming/kotlin/kotlin_for_javafx/","section":"Programming","summary":"Interested in programming JavaFX in Kotlin?  Here’s what you need to know to write JavaFX code that is so much better than anything you can write in Java.","title":"Kotlin For JavaFX","type":"programming"},{"content":" Introduction # We\u0026rsquo;re all familiar with Dialog boxes, they pop up with some important information, ask you, \u0026ldquo;Are you sure?\u0026rdquo;, let us pick a file, or make some other important decision before processing can continue. In the beginning, JavaFX didn\u0026rsquo;t have Dialogs and we were left to cook up our own solutions or use third party libraries. But they were added around JavaFX 8, and work quite well.\nThere are places where a Dialog makes a lot of sense, but a lot of places where they result in a bad user experience. First off, most users hate pop-ups and they\u0026rsquo;re almost always a surprise when the appear. So if you can design your UX such that whatever information would be in a Dialog is conveyed in your main GUI, your users will be happier. For instance, instead of putting up a dialog that indicates that some TextField contains an invalid value, how about configuring your TextField so that the users cannot enter invalid data?\nKotlin The examples in this article are written in Kotlin. If you need some help understanding Kotlin, then refer to this article. The Basics # Dialogs really aren\u0026rsquo;t that complicated, but this is one of those places where the JavaDocs is not a very good learning tool. This means that beginners get confused about how to go about using them. You\u0026rsquo;ll need to understand how their layout works, the ButtonType madness, and then about \u0026ldquo;Result Converters\u0026rdquo;.\nBefore we get into more advanced concepts, let\u0026rsquo;s look at the core concepts about how Dialog works\u0026hellip;\nButton Types # ButtonTypes is the concept that really gets beginners\u0026rsquo; heads spinning. It\u0026rsquo;s meant to abstract the meaning of the Buttons from the implementation of the Buttons, and make it easier to quickly implement a Dialog. Because you don\u0026rsquo;t have to worry about how the Buttons actually get on the screen, you can treat them like data through the ButtonTypes. Of course, none of this is explained properly, so it just causes a lot of confusion.\nLet\u0026rsquo;s clear that up\u0026hellip;\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content=\u0026ldquo;Dialogs are designed to be user interactions inserted into a procedural flow.\u0026rdquo; %}\nVery often, Dialogs are used to allow the user to make some kind of choice between a small number of options - which can even be something as simple as \u0026ldquo;proceed\u0026rdquo; or \u0026ldquo;stop\u0026rdquo;. To simplify the programming needed to implement these kinds of Dialogs, the designers of JavaFX have given us ButtonTypes and the Enum class ButtonBar.ButtonData.\nButtonType is a POJO with a set of statically predefined values that correspond to the values in ButtonBar.ButtonData. These are values like APPLY, OK, CANCEL and NEXT. You can make your own ButtonTypes if you want, and this allows you to have a button in your Dialog with a customized label but a standard value.\nIf you\u0026rsquo;re using Dialogs this way, then you don\u0026rsquo;t add the Buttons directly to the Dialog, you just add a list of the ButtonTypes that you want to have, and the Dialog takes care of adding the Buttons in the right place, and then it will tell you the ButtonType of the Button that the user clicked. There isn\u0026rsquo;t any way to directly add a Button to the standard \u0026ldquo;Button Bar\u0026rdquo; in a Dialog.\nHandling Return Values # Dialogs are primarily intended to be used modally as part of a procedural flow. \u0026ldquo;Modal\u0026rdquo; means that the window holding the Dialog is the only active window in the application, and the user cannot interact with any other part of your GUI until they have dismissed the Dialog. The normal way to put a Dialog on the screen is to use Dialog.showAndWait().\nDialog.showAndWait() is very special case in JavaFX coding. It\u0026rsquo;s almost like using a blocking operation in your code, which you would ordinarily never do on the FXAT. However, Dialog.showAndWait() does not block the FXAT. Events will continue to be processed on the FXAT, transitions and timers will continue to run, and the screen will continue to respond appropriately to window moves, mouse actions and keystrokes.\nWhat will happen, though, is that the code that contains the Dialog.showAndWait() call will stop. No more code will be executed until the Dialog has been closed. At that point, the execution will continue.\nAnd when the execution continues, the Dialog.showAndWait() call will have returned a value. This value is wrapped in Optional, meaning that you\u0026rsquo;ll have to check to see if there is an actual value. The JavaDocs for Dialog show three methods to handle the Optional with the notation that, \u0026ldquo;There is no better or worse option of the three\u0026hellip;\u0026rdquo;. That\u0026rsquo;s utter rubbish. The first approach uses Optional.get() which is an anti-pattern. The third approach, \u0026ldquo;The fully lambda approach\u0026rdquo;, is totally counter-intuitive, and probably inspired by the novelty of lambdas at the time that Dialogs were introduced.\nStick to Optional.ifPresent(), and if you want to have some code executed if there is no value returned you should use Optional.ifPresentOrElse().\nBut why would Dialog.showAndWait() return an Optional?\u0026hellip;\nAbnormal Closing of the Dialog # Since windows often have that \u0026ldquo;X\u0026rdquo; in the top corner, it\u0026rsquo;s possible to close them without clicking on one of the Buttons. This means that the Dialog can close without the user giving a response, and the way that is handled is to return the value as an Optional.\nThe JavaDocs refer to this as \u0026ldquo;abnormally\u0026rdquo; closing a Dialog. Not all Dialogs can be closed this way, it depends on the nature of the Buttons in the Dialog.\nIf a Dialog only has one Button, then it can be closed abnormally. If a Dialog has more than one Button, and one of those Buttons has been defined as a \u0026ldquo;Cancel\u0026rdquo; Button, then it can also be closed abnormally.\nIn the standard implementation of Dialogs, the \u0026ldquo;Cancel\u0026rdquo; Button is usually treated the same as an abnormal close, and the Dialog will return an empty Optional.\nDialog Utility Subtypes # There are three subtypes of Dialog, each intended to make it easy to perform specific types of common user interaction\u0026hellip;\nAlert # Alerts are intended to send a message to the user and require them to acknowledge it before moving on. Control of how the Alert will look is by assigning an AlertType to it. Beyond that, you can change the window title, the text which appears beside the icon, the message and the icon itself. Alert extends Dialog\u0026lt;ButtonType\u0026gt; which means that its showAndWait() method will return Optional\u0026lt;ButtonType\u0026gt;\nThe AlertTypes and How They Look # The JavaDocs don\u0026rsquo;t give much help about what the different AlertTypes actually do, just some generic description of their intended uses. The only way to see is to try them, so here\u0026rsquo;s some Kotlin code that will do the trick:\nclass AlertTest : Application() { override fun start(primaryStage: Stage) { primaryStage.setScene(Scene(createContent())) primaryStage.show() } private fun createContent(): Region = BorderPane().apply { center = Button(\u0026#34;Click Me\u0026#34;).apply { onAction = EventHandler { val alert = Alert(AlertType.CONFIRMATION).apply { contentText = \u0026#34;Click a Button\u0026#34; } alert.showAndWait().ifPresentOrElse({ println(\u0026#34;This is the result: $it\u0026#34;) }, { println(\u0026#34;No result\u0026#34;) }) } } padding = Insets(50.0) } } fun main() { Application.launch(AlertTest::class.java) } From this code I generated the following screen snaps, changing the AlertType each time\u0026hellip;\nAlertType.Confirmation:\nThis is the only AlertType that generates more than one button, and abnormally closing it will return a \u0026ldquo;Cancel\u0026rdquo; value. The idea behind this Alert is usually to allow the user one last chance to abort an operation before it starts.\nThe next three don\u0026rsquo;t differ in functionality, just the presentation of the message\u0026hellip;\nAlertType.Error:\nAlertType.Information:\nAlertType.Warning:\nAlertType.None:\nThis AlertType is useless without some further customization. In fact, it isn\u0026rsquo;t even possible to close this Dialog at all since there are no Buttons which means it won\u0026rsquo;t accept an abnormal closing either.\nHere\u0026rsquo;s how you can customize it:\nval alert = Alert(AlertType.NONE).apply { contentText = \u0026#34;This is contentText\u0026#34; headerText = \u0026#34;This is headerText\u0026#34; title = \u0026#34;This is the title\u0026#34; buttonTypes += listOf(ButtonType.APPLY, ButtonType.FINISH) graphic = AlertTest::class.java.getResource(\u0026#34;/images/boom.png\u0026#34;)?.toExternalForm()?.run { ImageView(this) }?.apply { fitWidth = 60.0 isPreserveRatio = true } } And that gives you this:\nOf course, you can apply all of these customizations to any of the AlertTypes.\nTextInputDialog # TextInputDialog looks very much like an Alert set to AlertType.CONFIRMATION, except that it also has a TextField placed into the content. This is intended to be a very quick way to set up a straight-forward input Dialog with a minimun of extraneous code.\nHere\u0026rsquo;s a simple example:\nprivate fun createContent(): Region = BorderPane().apply { center = Button(\u0026#34;Click Me\u0026#34;).apply { onAction = EventHandler { val dialog = TextInputDialog(\u0026#34;This is the default\u0026#34;).apply { contentText = \u0026#34;This is a message in\\n the content text\u0026#34; headerText = \u0026#34;Input Required\u0026#34; } dialog.showAndWait().ifPresentOrElse({ println(\u0026#34;This is the result: $it\u0026#34;) }, { println(\u0026#34;No result\u0026#34;) }) } } padding = Insets(50.0) } Which looks like this:\nTextInputDialog extends Dialog\u0026lt;String\u0026gt; so it will return an Optional\u0026lt;String\u0026gt;. The result will be empty if the user clicks the \u0026ldquo;Cancel\u0026rdquo; Button or abnormally closes the window. Otherwise it will return the value that is in the TextField.\nIn addition to the customization values available with Alert you can also get access to the TextField contained in the Dialog via TextInputDialog.getEditor(). This means that you can install a TextFormatter on it.\nChoiceDialog # Like TextInputDialog, ChoiceDialog is inteded to be a \u0026ldquo;no fuss\u0026rdquo; way to set up an input Dialog, but using a ComboBox instead of a TextField. You\u0026rsquo;ll need to provide the list of possible choices, as well as the default\u0026hellip;\nprivate fun createContent(): Region = BorderPane().apply { val button = Button(\u0026#34;Click Me\u0026#34;).apply { onAction = EventHandler { val dialog = ChoiceDialog\u0026lt;String\u0026gt;(\u0026#34;Wombat\u0026#34;, \u0026#34;Bunny\u0026#34;, \u0026#34;Wombat\u0026#34;, \u0026#34;Kangaroo\u0026#34;, \u0026#34;Panda\u0026#34;).apply { contentText = \u0026#34;This is a message in\\n the content text\u0026#34; headerText = \u0026#34;Input Required\u0026#34; } dialog.showAndWait().ifPresentOrElse({ println(\u0026#34;This is the result: $it\u0026#34;) }, { println(\u0026#34;No result\u0026#34;) }) } } center = button padding = Insets(50.0) } Which looks like this:\nUnlike TextInputDialog, you cannot grab the input control to customize it. You can, however, still customize the other common elements just like the other standard Dialogs.\nCreating Your Own Dialog # While Alert, TextInputDialog and ChoiceDialog are likely to satisfy 90%+ of your use cases for Dialogs, there are times when you\u0026rsquo;ll need to create something a little bit more complicated and customized. So let\u0026rsquo;s look at how you go about doing that\u0026hellip;\nDialogPane # Tip The Dialog class is NOT a screen component. It\u0026rsquo;s best thought of as a controller class.\nThe screen content of a Dialog is a class called DialogPane, which handles the layout. It has four main components, the \u0026ldquo;Header\u0026rdquo;, the \u0026ldquo;Content\u0026rdquo;, the \u0026ldquo;Expandable Content\u0026rdquo; and the \u0026ldquo;Button Bar\u0026rdquo;. If you\u0026rsquo;ve looked at the screen shots of TextInputDialog or ChoiceDialog earlier in this article then you\u0026rsquo;ve seen how this structure works.\nThe \u0026ldquo;Header\u0026rdquo; Section # The \u0026ldquo;Header\u0026rdquo; section contains two elements; some text and a graphic. These are actually two different Nodes, not the text and graphic of a Labeled type of Node. You can change the content of either of these two Nodes directly if you want. Often, the graphic is an ImageView, but you can change it to any kind of Node that you like.\nYou can directly change the values held in the default Nodes:\nsetHeaderText() This will update the text content of the \u0026ldquo;Header\u0026rdquo; section. setGraphic() This will update the content of the graphic in the \u0026ldquo;Header\u0026rdquo; section. You can put any kind of Node in here. These are also Properties so you can do the usual binding stuff if that makes sense for your application.\nBut if you want a completely custom \u0026ldquo;Header\u0026rdquo; section\u0026hellip;\nsetHeader() This will replace the entire \u0026ldquo;Header\u0026rdquo; section with your own Node. Once you have done this, you will not be able to use setHeaderText() or setGraphic(). If you haven\u0026rsquo;t called setHeader() to implement your own custom header Node, then calling getHeader() will return Null. In other words, you cannot use getHeader() to retrieve and manipulate the default header Node.\nThe \u0026ldquo;Content\u0026rdquo; Section # The \u0026ldquo;Content\u0026rdquo; section is occupied by an empty Label by default. You can update its text value directly.\nsetContentText() This will update the text value in the \u0026ldquo;Content\u0026rdquo; section. This is also a Property so you can do the usual binding stuff if that makes sense for your application. This looks like this:\nIf you decide that you do not like the default Label implementation for the \u0026ldquo;Content\u0026rdquo; section you may replace it with your own Node\u0026hellip;\nsetContent() This will replace the entire \u0026ldquo;Content\u0026rdquo; section with your own Node. Once you have done this, you will not be able to use setContentText() any more. If you haven\u0026rsquo;t called setContent() to implement your own custom content Node, then calling getContent() will return Null. In other words, you cannot use getContent() to retrieve and manipulate the default content Node.\nThe \u0026ldquo;Expandable Content\u0026rdquo; Section # If you have additional details that users may not always need/want to see, you can put it into the \u0026ldquo;Expandable Content\u0026rdquo; section. Unlike the \u0026ldquo;Header\u0026rdquo; and the \u0026ldquo;Content\u0026rdquo; sections, you cannot access a text value for it, you have to supply a Node via the setExpandableContent() method.\nIf this Node is non-null, then a \u0026ldquo;twisty\u0026rdquo; style arrow with the text, \u0026ldquo;Show Details\u0026rdquo; will appear in the \u0026ldquo;Button Bar\u0026rdquo;. Clicking on it will expand the Dialog size, and display the Node contained in the \u0026ldquo;Expandable Content\u0026rdquo; section. The \u0026ldquo;twisty\u0026rdquo; changes to say \u0026ldquo;Hide Details\u0026rdquo;.\nThe \u0026ldquo;Button Bar\u0026rdquo; # The \u0026ldquo;Button Bar\u0026rdquo; really is a ButtonBar and it sits at the bottom of the DialogPane. There\u0026rsquo;s no way to directly access it, and the only way to add or remove buttons is to add new ButtonTypes to the list from getButtonTypes().\nDelegate Methods in Dialog # There are convenience delegate methods in the Dialog class which call the appropriate method in DialogPane to modify all three of the content values. These are the setters and getters for contentText, headerText and graphic. To actually change the Nodes for any of these elements from the defaults (or to access the default Nodes) you\u0026rsquo;ll have to get the DialogPane from the Dialog first.\nFor some unfathomable reason, there\u0026rsquo;s no delegate method in Dialog to retrieve the list of ButtonTypes. So, once again, you\u0026rsquo;ll have to grab the DialogPane first.\nA Sample # Here\u0026rsquo;s the code for a customized Dialog with the default Nodes for the \u0026ldquo;Header\u0026rdquo; and \u0026ldquo;Content\u0026rdquo; sections, but with the values populated. There\u0026rsquo;s a single Button and \u0026ldquo;Expandable Content\u0026rdquo;:\nprivate fun createContent(): Region = BorderPane().apply { val button = Button(\u0026#34;Click Me\u0026#34;).apply { onAction = EventHandler { val dialog = Dialog\u0026lt;ButtonType\u0026gt;().apply { contentText = \u0026#34;This is a message in\\n the content text\u0026#34; headerText = \u0026#34;The HeaderText\u0026#34; title = \u0026#34;Dialog Title\u0026#34; graphic = this::class.java.getResource(\u0026#34;/images/boom.png\u0026#34;)?.toExternalForm()?.run { ImageView(this) }?.apply { fitWidth = 40.0 isPreserveRatio = true } dialogPane.buttonTypes += ButtonType.CLOSE dialogPane.expandableContent = Label(\u0026#34;\u0026#34;\u0026#34;This is the expandable content Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer tristique malesuada arcu a malesuada. Ut porta et mauris eu pellentesque. Curabitur molestie vel nunc vitae pharetra. In hac habitasse platea dictumst. Donec ut rutrum mauris. In hac habitasse platea dictumst. Donec tristique ac nisl pretium suscipit. Morbi magna quam, convallis ut vehicula et, lacinia nec orci. Quisque commodo neque vitae finibus malesuada. Integer a magna ultricies, consequat velit sit amet, laoreet purus. Pellentesque facilisis aliquam tortor eu eleifend. \u0026#34;\u0026#34;\u0026#34;) } dialog.showAndWait().ifPresentOrElse({ println(\u0026#34;This is the result: $it\u0026#34;) }, { println(\u0026#34;No result\u0026#34;) }) } } center = button padding = Insets(50.0) } This looks like this:\nAnd expanded it looks like this:\nStyling # Here\u0026rsquo;s the Modena.css entries for Dialog. It will point you to the correct strategy to customize the styling of your Dialog:\n/******************************************************************************* * * * Dialog * * * ******************************************************************************/ .dialog-pane { -fx-background-color: -fx-background; -fx-padding: 0; } .dialog-pane \u0026gt; .expandable-content { -fx-padding: 0.666em; /* 8px */ } .dialog-pane \u0026gt; .button-bar \u0026gt; .container { -fx-padding: 0.833em; /* 10px */ } .dialog-pane \u0026gt; .content.label { -fx-alignment: top-left; -fx-padding: 1.333em 0.833em 0 0.833em; /* 16px 10px 0px 10px */ } .dialog-pane \u0026gt; .content { -fx-padding: 0.833em; /* 10 */ } .dialog-pane:no-header .graphic-container { -fx-padding: 0.833em 0 0 0.833em; /* 10px 0px 0px 10px */ } .dialog-pane:header .header-panel { /*-fx-padding: 0.833em 1.166em 0.833em 1.166em; *//* 10px 14px 10px 14px */ -fx-padding: 0.833em; /* 10px */ -fx-background-color: -fx-box-border, linear-gradient(-fx-background, derive(-fx-background, 30%)); -fx-background-insets: 0, 0 0 1 0; } .dialog-pane:header .header-panel .label { -fx-font-size: 1.167em; /* 14px */ -fx-wrap-text: true; } .dialog-pane:header .header-panel .graphic-container { /* This prevents the text in the header running directly into the graphic */ -fx-padding: 0 0 0 0.833em; /* 0px 0px 0px 10px */ } .dialog-pane \u0026gt; .button-bar \u0026gt; .container \u0026gt; .details-button { -fx-alignment: baseline-left; -fx-focus-traversable: false; -fx-padding: 0.416em; /* 5px */ } .dialog-pane \u0026gt; .button-bar \u0026gt; .container \u0026gt; .details-button.more { -fx-graphic: url(\u0026#34;dialog-more-details.png\u0026#34;); } .dialog-pane \u0026gt; .button-bar \u0026gt; .container \u0026gt; .details-button.less { -fx-graphic: url(\u0026#34;dialog-fewer-details.png\u0026#34;); } .dialog-pane \u0026gt; .button-bar \u0026gt; .container \u0026gt; .details-button:hover { -fx-underline: true; } You can see that all of the stylings are applied to sub-components of DialogPane, and that you have separate entries for the header, the content, the expandable content and the button bar.\nThe best strategy for customizing these is to create a separate style class selector for your Dialog and then add it to the DialogPane of your Dialog. You don\u0026rsquo;t have to customize the base style class you\u0026rsquo;ve added, but you can add in entries for the sub-components that you want.\nThis was added to default.css:\n.fancy-alert \u0026gt; .content { -fx-background-color: lightblue; } We\u0026rsquo;re just adding a light blue background to the \u0026ldquo;Content\u0026rdquo; area of the Dialog\nprivate fun createDialog() = Alert(AlertType.CONFIRMATION).apply { this::class.java.getResource(\u0026#34;/css/default.css\u0026#34;)?.toString()?.let { dialogPane.stylesheets += it } contentText = \u0026#34;This is a sample message\u0026#34; dialogPane.styleClass += \u0026#34;fancy-alert\u0026#34; } {% include notice type= \u0026ldquo;primary\u0026rdquo; content= \u0026quot; Dialogs open in their own Scene, so they won\u0026rsquo;t inherit any style sheets that you\u0026rsquo;ve added to your main GUI screen.\u0026quot; %}\nYou\u0026rsquo;ll have add your stylesheet to the DialogPane which is the root Node of the Scene it opens in.\nThis results in this:\nThis isn\u0026rsquo;t pretty, but it does show that the content area has been styled. If you replace the standard Nodes for any of the areas, then you can directly add style classes to your custom Nodes, with the caveat that you\u0026rsquo;ll still need to add the stylesheet to the DialogPane.\nThe Result Converter # You may have noticed that the TextInputDialog and ChoiceDialog do not return Optional\u0026lt;ButtonType\u0026gt;. TextInputDialog returns Optional\u0026lt;String\u0026gt; and ChoiceDialog is a generic class, return Optional\u0026lt;T\u0026gt; where T is whatever type you declared your ChoiceDialog to be.\nSo how does Dialog do this?\nIt uses something called a \u0026ldquo;Result Converter\u0026rdquo;.\n{% include notice type= \u0026ldquo;primary\u0026rdquo; content= \u0026ldquo;Understanding how to implement your own Result Converter is the key to creating custom Dialogs that do more interesting things.\u0026rdquo; %}\nA Result Converter is a Callback. A Callback is a functional interface that\u0026rsquo;s usually invoked deep within some standard element to allow it to do something customized. It\u0026rsquo;s essentially a \u0026ldquo;hook\u0026rdquo; for you to tie in your own code that will be called at some standard place. That standard element will \u0026ldquo;call back\u0026rdquo; to your code to figure out some value for it.\nTechnically, a Result Converter is of type Callback\u0026lt;ButtonType, R\u0026gt;. This means that it will be passed a ButtonType and it will return a value of type R, and that R is going to be the same R that you declared your Dialog to be. You just have to supply the code that says, \u0026ldquo;Ok, the user clicked ButtonType.DoIt so I need to return this value\u0026hellip;\u0026rdquo;.\nA Simple Custom Dialog Example # Now let\u0026rsquo;s say we\u0026rsquo;ve written some process that could possibly terminate badly in some way. We\u0026rsquo;ve captured the crash, and we need to have the user specify how to proceed from here:\nHere we have three custom ButtonTypes, and we need to convert the button that\u0026rsquo;s clicked into something that\u0026rsquo;s meaningful to process that\u0026rsquo;s generated the Dialog. In this case we\u0026rsquo;re going to us the Enum RecoveryAction that we\u0026rsquo;ve created. So we\u0026rsquo;re going to use a Dialog\u0026lt;RecoveryAction\u0026gt;\u0026hellip;.\nclass CustomDialogExample : Application() { override fun start(primaryStage: Stage) { primaryStage.setScene(Scene(createContent())) primaryStage.show() } private fun createContent(): Region = BorderPane().apply { val button = Button(\u0026#34;Click Me\u0026#34;).apply { onAction = EventHandler { createDialog().showAndWait().ifPresentOrElse({ println(\u0026#34;This is the result: $it\u0026#34;) }, { println(\u0026#34;No result\u0026#34;) }) } } center = button padding = Insets(50.0) } private fun createDialog() = Dialog\u0026lt;RecoveryAction\u0026gt;().apply { val dump = \u0026#34;Core Dump\u0026#34; val support = \u0026#34;Contact Support\u0026#34; val ignore = \u0026#34;Who Cares?\u0026#34; resultConverter = Callback { buttonType -\u0026gt; when (buttonType.text) { dump -\u0026gt; RecoveryAction.CORE_DUMP support -\u0026gt; RecoveryAction.CALL_SUPPORT ignore -\u0026gt; RecoveryAction.IGNORE else -\u0026gt; null } } headerText = \u0026#34;The Program has Bombed\u0026#34; title = \u0026#34;Oops!!!\u0026#34; graphic = this::class.java.getResource(\u0026#34;/images/boom.png\u0026#34;)?.toExternalForm()?.run { ImageView(this) }?.apply { fitWidth = 40.0 isPreserveRatio = true } dialogPane.buttonTypes += listOf(ButtonType(dump), ButtonType(support), ButtonType(ignore)) contentText = \u0026#34;\u0026#34;\u0026#34;Something horrible has happened. Now you need to pick how you would like to handle the issue. \u0026#34;\u0026#34;\u0026#34; } } enum class RecoveryAction { CORE_DUMP, CALL_SUPPORT, IGNORE } fun main() { Application.launch(CustomDialogExample::class.java) } This is a very straight-forward use of the ButtonType -\u0026gt; RecoveryAction result converter. We just check the text associated with the ButtonType that the user clicked, and convert that into the Enum. Usually, the idea of using the text on the Button is a pretty horrible idea, but ButtonType doesn\u0026rsquo;t give a lot of options if you want to use the Buttons to select the action.\nA More Integrated Example # As you\u0026rsquo;ve seen from TextInputDialog and ChoiceDialog, it\u0026rsquo;s possible to put data collection Nodes into Dialog and then access them from your Result Converter to return something that\u0026rsquo;s not just dependent on the Button that was clicked. This opens up a whole world of possibilities, as your Dialog can now contain all manner of layouts and can work just like any screen, and then you can gather up results and pass them on to your process.\nLet\u0026rsquo;s move the choice from the Buttons and into the content of the Dialog. In this case, we\u0026rsquo;ll use RadioButtons:\nUnlike the previous version, it\u0026rsquo;s now possible to have an empty answer, because we have two buttons and one of them is a \u0026ldquo;Cancel\u0026rdquo; type. You can see that when that happens, we just return Null from the result converter. In the result converter, we check the ToggleGroup to see which RadioButton was selected. This all works because the Result Converter and the RadioButtons and the ToggleGroup were all defined in the same scope\u0026hellip;\nclass CustomDialogExample : Application() { override fun start(primaryStage: Stage) { primaryStage.setScene(Scene(createContent())) primaryStage.show() } private fun createContent(): Region = BorderPane().apply { val button = Button(\u0026#34;Click Me\u0026#34;).apply { onAction = EventHandler { createDialog().showAndWait().ifPresentOrElse({ println(\u0026#34;This is the result: $it\u0026#34;) }, { println(\u0026#34;No result\u0026#34;) }) } } center = button padding = Insets(50.0) } private fun createDialog() = Dialog\u0026lt;RecoveryAction\u0026gt;().apply { val dumpRB = RadioButton(\u0026#34;Core Dump\u0026#34;) val supportRB = RadioButton(\u0026#34;Contact Support\u0026#34;) val ignoreRB = RadioButton(\u0026#34;Who Cares?\u0026#34;) val toggleGroup = ToggleGroup().apply { toggles += listOf\u0026lt;Toggle\u0026gt;(dumpRB, supportRB, ignoreRB) } dialogPane.content = FlowPane(dumpRB, supportRB, ignoreRB).apply { hgap = 20.0 } resultConverter = Callback { buttonType -\u0026gt; if (buttonType == ButtonType.OK) { when (toggleGroup.selectedToggle) { dumpRB -\u0026gt; RecoveryAction.CORE_DUMP supportRB -\u0026gt; RecoveryAction.CALL_SUPPORT ignoreRB -\u0026gt; RecoveryAction.IGNORE else -\u0026gt; null } } else null } headerText = \u0026#34;The Program has Bombed\u0026#34; title = \u0026#34;Oops!!!\u0026#34; graphic = this::class.java.getResource(\u0026#34;/images/boom.png\u0026#34;)?.toExternalForm()?.run { ImageView(this) }?.apply { fitWidth = 40.0 isPreserveRatio = true } dialogPane.buttonTypes += listOf(ButtonType.CLOSE, ButtonType.OK) dialogPane.expandableContent = Label(\u0026#34;\u0026#34;\u0026#34;Something horrible has happened. Now you need to pick how you would like to handle the issue. \u0026#34;\u0026#34;\u0026#34;) } } enum class RecoveryAction { CORE_DUMP, CALL_SUPPORT, IGNORE } fun main() { Application.launch(CustomDialogExample::class.java) } Since we have some actual content in the Dialog, the text description has been moved into the \u0026ldquo;Expanded Content\u0026rdquo; area.\nThis is still a pretty simple example, but you should be able to see that you could use the \u0026ldquo;Content\u0026rdquo; section of a Dialog as the View in an MVCI framework. You can have a Model and the Result Converter would be a method call in your Interactor. The Controller would be responsible for instantiating the actual Dialog and pass that back through getView(). If you did something like that, then the sky\u0026rsquo;s the limit for the kind of things that you could put into a Dialog.\nSummary # Dialogs are primarily useful in two ways:\nWhen you have a process that needs to be interrupted to interact with the user. When you some kind of supplementary data entry that you don\u0026rsquo;t want to clutter your main GUI. Of the two, the first is probably the most common. When you just need to send a message to the user, then your best route is to use one of the Alert versions of Dialog. When you need more complicated data collection from the user, then you\u0026rsquo;re probably going to have to design a custom Dialog to do the work.\n","date":"25 January 2023","externalUrl":null,"permalink":"/javafx/elements/nodes/dialogs/","section":"JavaFX","summary":"Dialogs are the basic tools for interacting with the user in a controlled way as part of a procedural flow.  They are best thought of as a way to collect and return information from the user.","title":"Dealing With Dialogs","type":"javafx"},{"content":" Introduction # The first time I ever looked at a Kotlin program was when one of my developers found a cool JavaFX library called \u0026ldquo;DirtyFX\u0026rdquo;. We wanted to understand it worked, but when we looked at the source code on GitHub we were stymied because it was in Kotlin.\nIt seemed impenetrable.\nYears later, I\u0026rsquo;ve gone back to Kotlin and learned it. It\u0026rsquo;s awesome. And, if you\u0026rsquo;re a competent Java programmer, you can learn enough to start using it with a 4 hour YouTube tutorial. Then it takes about a week or so to get comfortable, and somewhat longer to get proficient at it.\nThis article isn\u0026rsquo;t designed to replace a 4 or 5 hour YouTube tutorial. It\u0026rsquo;s intended to give a typical Java programmer enough information to understand a typical Kotlin program in a much shorter period of time. It\u0026rsquo;s also intended to give a typical Java programmer a feel for how Kotlin works, and a start to understanding why many Kotlin programmers think it\u0026rsquo;s the way forward in the JVM world.\nI\u0026rsquo;ve also provided links to the Kotlin official documentation as part of the text. So if you want to look into some aspect of Kotlin a little more deeply, it should be easy for you to do so. The Kotlin docs are really well written and easy to understand, too.\nOne more thing: If you study Kotlin for any length of time, you\u0026rsquo;ll come across the term \u0026ldquo;idiomatic Kotlin\u0026rdquo;. This is the idea that while Kotlin shares the JVM, just writing code the way you would in Java but in Kotlin isn\u0026rsquo;t going give you something that feels like Kotlin. There\u0026rsquo;s definitely a Kotlin approach to coding, and coding in that way gives you \u0026ldquo;Idiomatic Kotlin\u0026rdquo;. In order to write code this way, you do really have to have a solid grasp of a lot of Kotlin, but once you get used to some of the quirky things you\u0026rsquo;ll see in Kotlin code you should be able to easily read and understand it.\nWhy Kotlin # When I learned Kotlin I realized that there was lots of stuff in Java that bugged me (or should have) that Kotlin just fixes in a seamless and natural way. Null safety is a clear example of that. Sure, in Java you can use Optional, but Kotlin\u0026rsquo;s approach is nicely integrated into the language in such a way that Null safety is virtually mandatory in any code you write. From what I\u0026rsquo;ve seen, just about every Java programmer who takes the time to learn enough Kotlin to become somewhat proficient in it wishes that they could switch over to Kotlin full time.\nIn general, Kotlin code is shorter and more intuitive than the equivalent in Java, and is therefore easier to read and understand. One example you\u0026rsquo;ll see here is how Kotlin eliminates the need for writing getters and setters, while providing the same functionality and separating the public interface from the internal workings of your class. Then Kotlin goes one step further by interpreting external references to member properties as calls to the getters and setters. In the end, Kotlin allows you to write code exactly the way that you always wanted to, and does all the work, behind the scenes, of the boilerplate code you have to write in Java.\nClass Structure # Let\u0026rsquo;s start at the top, class structure. We\u0026rsquo;ll use this sample code to talk about a few topics:\nclass MyClass(val property1 : Int, private var property2 : Int = 0) : SomeInterface { var property3: SomeInterface = SomeImplementation() protected val property4 = mutableListOf(\u0026#34;abc\u0026#34;, \u0026#34;def\u0026#34;) private var property5: Int init { property5 = property1 + 25 } fun someMethod(param1 : String, param2 : Double = 3.2) : Boolean { val localVariable : Int = 44 . . . return booleanValue } } Declaring Types # The first thing that you\u0026rsquo;ll notice is that there aren\u0026rsquo;t any semi-colons at the ends of the lines. You can use them, but you don\u0026rsquo;t need to.\nKotlin basically supports all the same types as Java, although the primitive types aren\u0026rsquo;t used. So anything that would be int or Integer is Int in Kotlin.\nThe second thing that you\u0026rsquo;ll notice is that type declarations are of this structure:\nkeyword name : type This structure is the same whether you\u0026rsquo;re declaring a field, a variable, a class or a method.\nIf you are initializing a variable and it can be reasonably inferred as to what it is, then you do not need to explicitly specify the type:\nvar abc = 5 Will give you Int. However, if you want Long, then you need to specify it unless the initial value is too large for Int:\nvar abc : Long = 5 The same structure follows when you want to define a variable as an interface when the initialization calls the constructor of an implementation of that interface.\nInstance Variables # Here, we\u0026rsquo;re very specifically using the term \u0026ldquo;instance variables\u0026rdquo; because Kotlin has a different structure for static elements, and these instance variables are very different from Java fields. Kotlin calls instance variables, \u0026ldquo;Properties\u0026rdquo;.\nA Kotlin property is actually a structure that is backed by a \u0026ldquo;field\u0026rdquo;. There is a default getter and setter for the property, and should you chose to override them you can access the backing field inside your getter/setter code. For now, this is all you need to know about this.\nOne advantage to Kotlin is that it automatically invokes the getter or setter for a property when it is directly accessed via dot notation in your code. Let\u0026rsquo;s look at this snippet:\nmyClass.property3 = myClass.property4[2] This is completely proper and valid Kotlin code. It calls the setter for property3 and the getter for property4. On top of that, it calls List.get() by using [] construct. You can also do this:\nmyClass.property4 += \u0026#34;xyz\u0026#34; Here it accesses the getter for property4 and then calls List.add() via the += operator. This is called \u0026ldquo;Operator Overloading\u0026rdquo;. You can define your own overloaded operators by defining methods on a class with particular names.\nIf this alone doesn\u0026rsquo;t sell you on Kotlin, I don\u0026rsquo;t know what will. You get all of the benefits of getters and setters with none of the overhead! And the operator overrides make your code look the way you always wanted it to in Java, but couldn\u0026rsquo;t be.\nMutability # By now you should have noticed the val and var prefixes for property and variable declarations. These indicate whether or not the reference is mutable. It\u0026rsquo;s not optional in Kotlin to specify this. Functionally, val is very close to the final keyword in Java, with the exception that there\u0026rsquo;s no default to \u0026ldquo;not final\u0026rdquo;.\nThe convention is to declare everything as immutable using val unless you specifically have a need for it to be mutable. In practice, this makes a huge difference in the readability of code since you don\u0026rsquo;t need to chase through the code looking for any places that any variable might have been changed. This is also a boon when writing multi-threaded code.\nPersonally, I find that 95%+ of my variables are declared with val. I almost feel guilty when I use var.\nLists are generally considered immutable unless you declare them as mutable. Remember that declaring a List variable as mutable is not the same as a mutable List.\nConsider this:\nval list1 = listOf(\u0026#34;a\u0026#34;, \u0026#34;b\u0026#34;) var list2 = listOf(\u0026#34;d\u0026#34;, \u0026#34;e\u0026#34;) val list3 = mutableListOf(\u0026#34;x\u0026#34;, \u0026#34;y\u0026#34;, \u0026#34;z\u0026#34;) The only operations you can perform on list1 are List.get() type operations. You cannot add, remove or change any of the elements. The same restrictions apply to list2, however you can replace it with a new immutable list, meaning that:\nlist2 = listOf(\u0026#34;dog\u0026#34;, \u0026#34;cat\u0026#34;, \u0026#34;budgie\u0026#34;) would be allowed. You can add or remove elements to list3, but but you cannot instantiate a new List as list3.\nMethods # Methods in Kotlin are called Functions and declared using the fun keyword. The return type of a function is declared the same way as for a variable and the body of the method is enclosed in {} if there is more than one line. If the function body is an expression, then you can just use = and put the code. For instance:\nfun doubleIt(x: Int): Int { return x * 2 } fun double(x: Int): Int = x *2 are equivalent. In the second case, the type declaration can be left out as the compiler can infer it.\nVoid functions either return Unit or just leave out the return type altogether.\nFunctions can have any number of parameters declared and those parameters can have default values. Consider this:\nfun multiplyIt(x: Int, multiplier: Int = 2) = x * multiplier Which can be called in the following ways:\nval answer = multiplyIt(3,2) val answer = multiplyIt(x = 3, multiplier = 2) val answer = multiplyIt(multiplier = 2, x = 3) val answer = multiplyIt(3) val answer = multiplyIt(x = 3) And all of these will return the same result.\nClass Declarations # Now that we\u0026rsquo;ve covered all of that, you can understand the class declaration itself.\nLooking at the code at top of this article, you\u0026rsquo;ll notice that the class declaration looks almost like a constructor. That\u0026rsquo;s because it is, but in most case you can skip the constructor keyword for the primary constructor.\nOne thing that\u0026rsquo;s different from a Java constructor is that all of the parameters listed as val or var automatically become properties of the class. Just like with Functions, you can declare default values for those properties, if they are not specified in the constructor call.\nPrimary constructors in Kotlin do not contain any code. However, you can declare init {} blocks in your class and these will be executed, in the order that they appear, from the primary constructor. You can do just about anything that you would expect to do, including initializing the value for a val property in an init block.\nIf a class implements an Interface, or extends another class, then that can be specified using the type style declaration that was used for functions.\nStatic elements are created by including them in a \u0026ldquo;Companion Object\u0026rdquo; inside the class.\nVisibility Modifiers # While the default visibility for Java is package-protected, there is no equivalent for this in Kotlin and the default visibility is public. There is also private, which does what you\u0026rsquo;d expect, and protected which exposes the class member to any sub-classes.\nBy default, all functions in a Kotlin class are final, and must be declared with the open modifier in order to be overridden by a subclass.\nLanguage Features # At this point, you should have enough information to open up a Kotlin class file and navigate around it, understanding at least the structure of the class. Now let\u0026rsquo;s look at some of the features of the Kotlin language\u0026hellip;\nLambdas # Lambdas are much more tightly integrated into Kotlin than Java. The syntax is similar:\nval lambda = {param: Int -\u0026gt; some code here} The entire thing is enclosed in the {}. If you aren\u0026rsquo;t going to use the parameter, then you can just replace it with _.\nval lambda = {_ -\u0026gt; some code here} If the parameter can be inferred, then you can leave out the param -\u0026gt; entirely and reference it as it. We\u0026rsquo;ll come back to that shortly.\nThat\u0026rsquo;s the basic syntax. One thing that\u0026rsquo;s handy is that if the last parameter in any function declaration is a function, then you can pass it as a \u0026ldquo;trailing lambda\u0026rdquo;, and if there are no other parameters you can leave out the parentheses entirely.\nSo, you can do something like this:\nval x = doSomething(3, 4){x:Int -\u0026gt; x*2} . . . fun doSomething(a: Int, b: Int, func : (Int) -\u0026gt; Int): Int { val intermediate = (a*3) - (b *12) return func(intermediate) } You can also use method references much the same way as in Java.\nYou\u0026rsquo;ll also see the term \u0026ldquo;higher order function\u0026rdquo; in a lot of documentation. This is simply a function that takes another function as a parameter. It\u0026rsquo;s far more common in Kotlin than Java\u0026hellip;\nFunctions as Data # This is probably one of the biggest features of Kotlin that should have a impact on the \u0026ldquo;feel\u0026rdquo; of programs written in Kotlin. Kotlin makes it very easy to pass snippets of code around from place to place, very much like data. Java has headed a little bit in this way with the introduction of \u0026ldquo;Functional Interfaces\u0026rdquo; and lambdas, but Kotlin bakes the concept in from the beginning.\nFirst off, you can declare a function as a data type just by specifying its inputs and outputs, in a format that looks a little bit like a cross between a lambda and a generic type declaration. For instance:\nvar abc : (String, Int) -\u0026gt; Double Here, the variable abc is declared to be a function that takes a String and an Int as input and returns a Double. There\u0026rsquo;s no need to declare a Functional Interfaces like Function, Predicate or Consumer as you do in Java.\nYou can even declare a \u0026ldquo;Type Alias\u0026rdquo; to give a name to your function type:\ntypealias NumberFinder = (String, Int) -\u0026gt; Double var abc : NumberFinder It\u0026rsquo;s possible to get a little lost between functions declared via fun and functions instantiated as lambdas or as variables. In truth, Kotlin really does treat them very much the same. For in every instance below, doubler is the same:\nval doubler : (Int) -\u0026gt; Int = {x:Int -\u0026gt; x*2} val doubler : (Int) -\u0026gt; Int = {it *2} val doubler : (Int) -\u0026gt; Int = {doubleIt(it)} val doubler : (Int) -\u0026gt; Int = ::doubleIt val doubler = {x:Int -\u0026gt; x*2} val doubler = {x: Int -\u0026gt; doubleIt(x)} val doubler = ::doubleIt . . . fun doubleIt(x: Int) = x * 2 The last two versions really make it clear, since you don\u0026rsquo;t need to even specify the type, and doubler as just a pointer for the local function really lays it bare.\nFinally, you can treat doubler from above just like doubleIt() in your code:\nval y = doubler(abc) val j = doubleIt(abc) Both would work just fine.\nExtension Functions # It\u0026rsquo;s possible to add a function to a class without creating a subclass. This is called an \u0026ldquo;Extension Function\u0026rdquo;. For instance, you can add a function to Int to determine if it is even:\nfun Int.isEven() = {this %2 == 0} An extension function is an example of a \u0026ldquo;receiver function\u0026rdquo;. A receiver function is one that is automatically passed a parameter which is known inside the function by a standard name, usually this. In the case of a receiver called this, if it can be inferred by the compiler the this can be left out of a statement when accessing class members (very much like in Java).\nScope Functions # Scope Functions are a special class of \u0026ldquo;receiver\u0026rdquo; functions.\nThere are 5 Scope Functions, and 4 of them are Extension Functions. So we\u0026rsquo;ll look at them first.\nTransformation Functions (My Term) # These two functions take the object as a parameter and return some other value. The differ only in only how they name the received object. They are let and run.\nLet\u0026rsquo;s look at run first:\nval stringLength: Int = \u0026#34;This is a String\u0026#34;.run{this.length} That\u0026rsquo;s pretty banal, but you get the idea. The received object is referred to as this. You should also note that when it\u0026rsquo;s clear, the this can be inferred by the compiler and left out of the code\u0026hellip;\nval stringLength: Int = \u0026#34;This is a String\u0026#34;.run{length} Now, let:\nval stringLength: Int = \u0026#34;This is a String\u0026#34;.let{it.length} You can see that run and let are pretty much the same, except that the received object is referred to as it in let. When a Scope Function uses it you can use the normal lambda syntax to change it to something more meaningful if you like.\nConfiguration Functions (Also, My Term) # These two functions take the object as a parameter and return it back, allowing you to configure an item without instantiating it as a variable. They are apply and also.\nI use apply all the time with JavaFX, as lots of JavaFX Nodes require pretty standard set-up which, in Java, requires instantiating them as a variable just to do something simple:\nhBox = HBox(5, Label(\u0026#34;Hello\u0026#34;).apply{styleClass += \u0026#34;label.prompt\u0026#34;}) Once again, the this can be inferred by the compiler and left out of the code. For those not familiar with JavaFX this is the equivalent of this.getStyleClass().add(\u0026quot;label-prompt\u0026quot;), as getStyleClass() returns a MutableList.\nStill though, that can get ugly, but the same structure can be moved to a function:\nhBox = HBox(5, promptLabel(\u0026#34;Hello\u0026#34;)) . . . private fun promptLabel(text: String) = Label(text).apply{styleClass += \u0026#34;label.prompt\u0026#34;} Which is a structure I use all the time.\nThe Non-Receiver Function - with # If you\u0026rsquo;re old enough to remember the Pascal programming language, then you might be familiar with with. It\u0026rsquo;s a great way to clean up a block of code that has many references to members of a single object. Whatever parameter passed to with becomes this in the associated lambda and, of course, the this can then be omitted in references to that objects members. So you can do something like this:\nval theResult: String = with(myClass) { name = \u0026#34;Fred\u0026#34; val x = age + 20 doSomething(\u0026#34;abc\u0026#34;) } Where name, age and doSomething() are members of myClass.\nSince with is a function, it can return a value, in this case the result of MyClass.doSomething().\nThe Non-Extension Version of run # This version of run is used when a situation requires an expression, but you want to put multi-line code instead. The format is run {}. There is no receiver object in this case.\nNull Safety # Null Safety is one of the biggest features of Kotlin! There is no reason at all to ever get an NPE in Kotlin. Ever.\nAs important as it is, it had to be left until this point because you need to understand how lambdas and Scope Functions work first, which shows you how tightly integrated into the language null safety is.\nNone of the normal data types, like Int, or Double or String are allowed to have a Null value. This means that they must always be initialized when declared, and you cannot put a Null value into them.\nIf you want to entertain the idea of having a Null value, then you must declare them as \u0026ldquo;Nullable\u0026rdquo;. Nullable types are specified by putting a ? after the type name. So Int?, Double? and String? are all types that are allowed to hold Null values.\nBut note that Int? is actually a different type from Int. You cannot perform a mathematical operation on Int? directly, nor can you use an Int? in an operation to assign to Int. One more time, Int? is not Int.\nIn fact, Int? is closer to the Java Optional\u0026lt;Integer\u0026gt; than anything else.\nIn order to use the value in a Nullable type, you need to (effectively) extract it into its non-nullable type. This is generally done via the ?. operator, and the ?: (called the \u0026ldquo;Elvis operator\u0026rdquo;). The Elvis operator is a little bit like the ternary operator in Java and, in Java would work like this:\nint x = (nullableInt.isNotNull()) ? nullableInt.getIntValue() : y; and looks like this in Kotlin:\nval x : Int = nullableInt ?: y In both cases, x would be whatever integer value was in nullableInt if it was non-null, and y if it was null.\nJust as Java\u0026rsquo;s Optional has map(), Kotlin has ?.{}. The code in the {} will be executed with the value passed as a parameter if it\u0026rsquo;s non-null, otherwise nothing happens and the value remains Null.\nval x : String? = getSomething() val y : Double? = x?.length()?.run{ val abc = this * 3 return abc / 7.0 } . . . fun getSomething(): String? {} In the expressions for y the code after ?. is only executed if the value is non-null.\nLanguage Structure # This is a quick survey of some of the common language elements that are used in Kotlin that are different from Java.\nString Templates # You can use templates to create strings:\nval x: Int = 123 val string: String = \u0026#34;There are $x carrots\u0026#34; The value of string will be \u0026ldquo;There are 123 carrots\u0026rdquo;. For more complex expressions, use {}. Also, println is part of the standard library, so there\u0026rsquo;s no need for System.out.println()\u0026hellip;\nprintln(\u0026#34;There are ${fridge.carrots + table.carrots} carrots\u0026#34;) Equals # No surprise here, in Kotlin \u0026ldquo;==\u0026rdquo; does what you\u0026rsquo;ve always wanted it to do in Java! \u0026ldquo;==\u0026rdquo; invokes the equals() method for any non primitive class. If you want referential equality, use \u0026ldquo;===\u0026rdquo;.\nIf Statements # For the most part, if works exactly as in Java. However, if can also be used as an expression that returns a value.\nval x = if (y \u0026gt; 30) 23 else 100 is valid and will assign 23 to x if y is greater than 30, otherwise x will be assigned 100.\nFor this reason there is no Ternary operator in Kotlin. This is just about the only case where Kotlin is a little bit more verbose than Java. However, if as an expression is used frequently when the branches are blocks of code.\nWhen Expressions # The when expression is very similar to the new form of the Java switch statement. Like if, when can be used as either a statement or an expression so it can return a value. When the subject of a when statement is an Enum or a sealed class, then the branches of the when must be exhaustive, or include an else branch.\nFor Loops and Range Expressions # In Kotlin for loops always work across a collection. To increment a value over a range, Kotlin has a type of Collection called a Range. The Java code:\nfor(int x = 0; x \u0026lt; 6; x++) {} would become:\nfor(x in 0..5) {} If you want to iterate over a Collection but also have an index, there\u0026rsquo;s a way to do that, too:\nfor((index, item) in collection.withIndex()){ println(\u0026#34;Item # $index is ${item.description}\u0026#34;) } Collection Operations and Sequences # In Kotlin, you can do almost all of the operations you would do with Streams directly on any Collection. This means that there\u0026rsquo;s no need to perform .stream() on a List or any other Collection most of the time. Operations performed on a Collection create a new Collection with the elements transformed in some way.\nWhen you do want to process in a manner similar to Java Streams, Kotlin has Sequences. You can use the Collection.asSequence() function to do this. There are some cases where the performance of a Sequence might be better than a series of Collection operations. There are also some function that are only available as Sequence operations.\nYou can also create an infinite Sequence with the generateSequence() function that specifies a starting value and a function to create the next value. You can then treat it like any other Sequence. Sequence has a function called take(x) that allows you to take the first x values of a Sequence.\nMaps and Pairs # Maps in Kotlin are made up of tuples, just as in Java, but they are thought more of as a collection of tuples than key/value containers. Tuples are constructed and then added to the Map as in the following code:\nval testMap = mutableMapOf(\u0026#34;x\u0026#34; to 27, \u0026#34;abc\u0026#34; to 55, \u0026#34;hello\u0026#34; to 74) testMap.put(Pair(\u0026#34;fred\u0026#34;, 18)) Additionally, you can use the [] shorthand operator instead of put() and get() on a Map\ntestMap[\u0026#34;george\u0026#34;] = 100 println(testmap[\u0026#34;fred\u0026#34;]) ","date":"19 December 2022","externalUrl":null,"permalink":"/programming/kotlin/kotlin_for_java_programmers/","section":"Programming","summary":"Need to understand a Kotlin program, but you only know Java?  This article should give you everything you need to know to get started.","title":"Kotlin For Java Programmers","type":"programming"},{"content":" Introduction # This article is an introduction to and a \u0026ldquo;how to\u0026rdquo; for the framework that I\u0026rsquo;ve developed for building Reactive JavaFX applications. It\u0026rsquo;s called \u0026ldquo;Model-View-Controller-Interactor\u0026rdquo;, or just, \u0026ldquo;MVCI\u0026rdquo;.\nMVCI was created as a practical solution to the challenges that are posed by JavaFX, specifically when it\u0026rsquo;s used as a Reactive programming tool. It\u0026rsquo;s easier to understand than the other frameworks while not making any compromises on functionality or technical correctness.\nMotivation and Development # My introduction to JavaFX was building GUI applications to augment and extend a complicated legacy enterprise application. My team had some experience with Swing, but decided to move over to JavaFX when version 2 came out in 2014. We felt that Swing screens looked dated tired, and JavaFX had a much more modern look and feel - as well as a more modern programming approach - than Swing did. The dated look and feel of Swing might have had much to do with our lack of skill to create anything better, but the JavaFX screens looked better right out of the box.\nRight or wrong, that was the direction I chose and we began a multi-year journey to learn how to deal with JavaFX while under pressure to deliver new functionality to the organization at an acceptable pace.\nWhat we didn\u0026rsquo;t realize was just how difficult JavaFX is to learn by yourself and, since it was brand new, there was nobody out there to teach it, or write about it with real knowledge and experience. Inevitably our process was full of mistakes, horrible code and lots of places where we had to re-think our approach to various challenges.\nOver the course of this I learned not to bother with FXML since it just complicated our code. I tried to implement MVC, as best we understood it, and this added a lot of structure to our code and made things cleaner, but still left a lot of things unsolved.\nI learned that scraping data out of the screen Nodes and loading them back into the Model every time the \u0026ldquo;Save\u0026rdquo; Button was pressed was a losing strategy. Binding the value properties of the Nodes to the Model was the way to go! This was a game-changer, as it totally changed our approach to screen building. I had started down the path to Reactive design without even knowing it!\nI learned to bind the other properties of the Nodes to the Model, and I learned to bind, whenever possible, to the Model and not between the screen Nodes. This made our layout components independent of each other.\nI also learned to put ALL of the business logic in one place. Don\u0026rsquo;t put logic to disable a Button in the layout code, but put it somewhere else and have it reflected in the Model. Then bind the Button.disableProperty() to that Model element.\nAs this went on, we had a set of guidelines and general principles to follow when building screens and functions but I still saw it as trying to fit into one of the accepted frameworks, usually Model-View-Controller. Some of the things we did seemed to break the designs of these frameworks, but they did result in better applications.\nFinally, in the past couple of years, I\u0026rsquo;ve come to realize that what I had developed was something unique, and not an adaptation of any of the existing patterns. I think it\u0026rsquo;s worth sharing with other programmers that want to build better JavaFX applications.\nReactive Programming # Let\u0026rsquo;s talk a little bit about \u0026ldquo;Reactive\u0026rdquo; programming. What is it?\nThere\u0026rsquo;s a few ways to look at it:\nStatic Layouts that Behave Dynamically This is a really important concept. Reactive systems allow you to create a layout as static elements, meaning that you don\u0026rsquo;t actually change the Nodes that are on the screen, add new Nodes or take any away. Instead the screen Nodes are connected to data elements that control how they work. Changes in those data elements result in dynamic changes to the screen that the user sees. At the back-end of the system, there are business rules that update those data elements as nothing more than data, without any knowledge of how those data elements will impact the screen. State Data is a Pipeline Between the Business Logic and the View This is another way to look at it. The \u0026ldquo;State\u0026rdquo; of the GUI is represented by some collection of data which is connected to the various properties of the elements of the View. Either end can potentially change some of that data, at which point it is available to the other end instantly. Both ends of the pipeline are free to interpret that data in a context which has meaning to it, without understanding what it means to the other end. So a layout may use a Boolean State element to control whether a Button is enabled, but the business logic end may interpret that same Boolean State element to mean that a date value somewhere is invalid. In JavaFX this means that you have an Object composed of Observables of some sort. These can be Properties, Bindings, ObservableLists or any their relatives. These Observables are bound to various properties of the Node elements that make up the layout.\nGeneric Reactive programming descriptions talk about \u0026ldquo;streams\u0026rdquo;, which is essentially the function provided by the Observables and the Binding classes.\nThe simple truth is that JavaFX provides all of the tools to build Reactive applications and, in fact, it works best when you use it that way.\nWhy Use a Framework? # Frameworks are designed to limit coupling between the GUI, the business logic and the control logic. Excessive coupling is by far the biggest issue with application design, and it makes it very difficult to understand how an application works, to make changes, find and fix bugs, and to extend the application.\nOne of the ways to evaluate coupling is to look at how much of its functionality a component exposes to the rest of the application, and how much it \u0026ldquo;knows\u0026rdquo; about the functionality of other components of the application. The more of this that you have, the more coupling that you have.\nWhen a component exposes some of its functionality to the rest of the application and that functionality is changed, then you have to look at all of the places that knowledge of that functionality is used, and evaluate how it needs to be changed as well. Often, you\u0026rsquo;ll find that some of those places are in functionality of other components that are also exposed to the rest of the application, so you need to track down where that knowledge is used and change them too. And so on, and so on\u0026hellip;\nThese couplings are also called \u0026ldquo;dependencies\u0026rdquo;. Dependencies also have direction and we can say that, \u0026ldquo;This component is dependent on this aspect of this other component\u0026rdquo;. Coupling becomes even worse when you have multiple dependencies between two components that go in either direction. A good framework should also try to manage the direction of the dependencies, in order to limit complexity of the coupling.\nAt the end of the day, a framework is just a \u0026ldquo;Design Pattern\u0026rdquo;. That is to say, an accepted way of coding something that has already been thought out so that you don\u0026rsquo;t have to \u0026ldquo;reinvent the wheel\u0026rdquo; in your application code. Other programmers can look at your code and say, \u0026ldquo;I recognize this\u0026rdquo;, even if they\u0026rsquo;ve never seen your code before.\nWhy Use MVCI? # Because it works, it dovetails nicely with Reactive JavaFX, and it\u0026rsquo;s easy to understand.\nWe\u0026rsquo;re not going to talk a great deal about the other frameworks here, but there are two questions that need to be addressed:\nWhy not use Model-View-Controller? For one simple reason: MVC does not allow for Reactive programming. You can bind View elements to the Model, but exclusively in a Read-Only mode. Any changes to the Model from the View have to be transmitted through the Controller. It\u0026rsquo;s a basic element of this framework. You can ignore this, but then you\u0026rsquo;re not using MVC any more. Why not use Model-View-ViewModel? MVVM does provide for binding between the ViewModel and the View elements, so that\u0026rsquo;s a step in the right direction. However, there\u0026rsquo;s no Reactive connection allowed for between the Model and the ViewModel. You end up with a lot of methods to handle data transfer between the ViewModel and the Model which creates an enormous amount of coupling. This gets very confusing very quickly. Both of these answers sound a bit like technical nit-picking that you could probably just ignore. In practice though, these represent issues that you end up having to work around - even if you don\u0026rsquo;t realize it. It gets messy very quickly.\nIf you use MVCI, you really don\u0026rsquo;t have to worry about these things. They\u0026rsquo;re just not a factor. MVCI deals with it for you and you\u0026rsquo;re not going to get tangled up in unexpected consequences of your design decisions.\nWhat Does MVCI Look Like? # MVCI has four components: a Model, a Controller, a View and an Interactor. Let\u0026rsquo;s look at what each of these do:\nThe Model The Model is the data representation of the \u0026ldquo;State\u0026rdquo; of the GUI. It\u0026rsquo;s just a POJO with the fields composed of JavaFX Observable types. There\u0026rsquo;s no logic, or any other code that\u0026rsquo;s not directly related to sharing the data fields. The View The View is not just a passive layout, but a complete user interface for the framework. This means that it has all of the logic to handle user clicks and to capture and handle any GUI events. The View is passed a reference to the Model, and it binds the properties of the various Nodes contained in the layout to the properties contained in the Model. The Controller The Controller is responsible for \u0026ldquo;how\u0026rdquo; things happen in the framework. It instantiates all of the other components, provides for integration with other parts of the application, defines \u0026ldquo;actions\u0026rdquo; for GUI events, and handles all of the threading. The Interactor The Interactor is the \u0026ldquo;business logic\u0026rdquo; component of MVCI. This is the place where Domain Objects can exist, and where calls to external API\u0026rsquo;s, persistence layers and other services are made. It\u0026rsquo;s able to see the Model, so it also contains the logic which updates the Model from data retrieved from services via Domain Objects. Here\u0026rsquo;s a diagram of how it all goes together:\nDependencies in MVCI # Since managing coupling is the whole point of a framework, let\u0026rsquo;s look at how the dependencies work in MVCI.\nDependencies almost always manifest themselves in non-private methods, including the constructors. Every time you see a non-private method, you\u0026rsquo;re looking at a potential dependency. It also tells you the direction of the dependency because the other components become dependent on that method. At the same time, the parameters required by a non-private method represent dependencies in the other direction, as these are things that the calling objects need to provide.\nFrom this perspective, you can consider constructors with parameters to be dependencies from the object back to the object that constructs it. Because every class needs a constructor, having one doesn\u0026rsquo;t increase the coupling in that direction at all, but adding parameters creates dependencies back to the constructing class.\nLet\u0026rsquo;s look at where the non-private methods are found in MVCI:\nThe Model You can see from the diagram above that all of the other three components have access to the Model. In fact, this is the main dependency in MVCI as the View, the Controller and the Interactor all have it as a dependency. As a POJO, it\u0026rsquo;s going to consist entirely of a bunch of field declarations, plus all of the accompanying getters and setters to allow other objects to access those fields. I\u0026rsquo;ve never seen a reason to have any constructor parameters in a Model. The Controller We\u0026rsquo;ll look at this a bit later, but the only non-private method in the Controller is something like a getView() method that returns a reference to the View as a Node or a Region (usually Region). In a complex application with multiple MVCI frameworks that need to share data or functionality, it is possible to have constructor parameters in Controllers, essentially creating dependencies on those external frameworks. The Interactor The Interactor gets a reference to the Model passed to it from the Controller via a parameter in its constructor. This is generally the only parameter in the Interactor\u0026rsquo;s constructor. In order to do work, the Interactor needs to have a number of non-private methods that Controller can call. These all create dependencies on the Interactor in the Controller. The View From the perspective of non-private methods, the View as an instance of Region has no dependencies at all (other than those of Region). However, it\u0026rsquo;s usually implemented via a Builder, and that has at least one constructor parameter - the Model. Additionally, the ViewBuilder can have constructor parameters to provide handlers for actions. These put dependencies from the ViewBuilder to the Controller. In order to create the View, the Controller must pass a reference to the Model plus any action handlers required. What Goes Where in MVCI? # Now let\u0026rsquo;s look at this from the other direction. How do you know where to put various pieces of functionality? MVCI is designed to make this extremely easy, so let\u0026rsquo;s look at the main items:\nLayout Layout goes in the View. This includes creating binding between the Model and the properties of the Nodes in the layout. Event Handlers Events are GUI elements, and their handlers go in the View. When EventHandlers need to perform actions that involve something other than the layout, they invoke \u0026ldquo;Action Performers\u0026rdquo; provided by the Controller. Action Performers Action Performers are functional elements that perform some kind of action. These are defined in the Controller. If an Action Performer is needed by the View so that it can invoke it from an EventHandler, then it will be passed to the View via a constructor parameter. Threading In JavaFX, threading is generally implemented using something like Task. Creation and configuration of Task objects and running them in background Threads is handled in the Controller. Business/Application Logic Anything resembling business logic goes into the Interactor. This includes the initialization of Bindings in the Model and linking various Model elements to each other (unless such connections are so rudimentary that they are clearly not business logic). Domain Stuff This goes somewhere else. Create a \u0026ldquo;Service\u0026rdquo;, \u0026ldquo;Broker\u0026rdquo; or \u0026ldquo;DAO\u0026rdquo; to handle it and call it from your Interactor. ChangeListeners These can go in one of two places. If the actions performed by the ChangeListener are entirely related to the View, then put it there. Otherwise it goes in the Controller, which will probably call a method in the Interactor to do the work. The ViewBuilder # The MVCI framework doesn\u0026rsquo;t specifically call for a ViewBuilder, but I\u0026rsquo;d call this a \u0026ldquo;best practice\u0026rdquo;.\nThere\u0026rsquo;s a general rule in JavaFX:\nExtend a class to add new functionality, use a Builder when all you are doing is configuring an existing Node subclass.\nWhat does \u0026ldquo;add new functionality\u0026rdquo; mean? In this case it really boils down to adding new non-private methods. In practice, you\u0026rsquo;re usually not going to add functionality, you\u0026rsquo;re just going to configure a Node or create a layout by adding configured Nodes via getChildren(). So builders are usually the way to go.\nA \u0026ldquo;Builder\u0026rdquo; can be any method that returns a Node subclass. JavaFX provides a handy interface called Builder which just defines a single method, build(). It\u0026rsquo;s generic, so you specify the type for the returned Node subclass.\nIn MVCI we\u0026rsquo;re going to create a Region subtype (like Pane, StackPane, VBox or BorderPane) that we\u0026rsquo;re going to return as an instance of Region. So in the Controller, we\u0026rsquo;ll instantiate a ViewBuilder that implements Builder\u0026lt;Region\u0026gt; and pass any dependencies, like the Model to it in its constructor. Then we\u0026rsquo;ll call ViewBuilder.build() to get the View.\nWhat I usually do is set the ViewBuilder as a field in the Controller and instantiate it in the Controller\u0026rsquo;s constructor. Then I create a delegate method in the Controller like this:\npublic Region getView() { return viewBuilder.build(); } If you\u0026rsquo;re paying even the tiniest bit of attention, you\u0026rsquo;ll realize that this means that there\u0026rsquo;s no reference to the View itself that\u0026rsquo;s maintained inside of the MVCI framework! So there\u0026rsquo;s no formal dependencies to or from the View anywhere inside the framework. Of course, the Bindings used in the layout to create the View create coupling between the View and the Model, but this can be completely managed by ignoring them in the View, and concentrating on the ViewBuilder and its dependencies.\nThe other important thing to note is that the Builder allows us to return a Region instead of whatever actual class was used to create the layout. Region only exposes a small number of methods that are useful for controlling its presentation in another layout. Things like Region.setMaxWidht(), or Region.setPadding(). You can get rid of those by returning Node instead.\nThis is important because it turns your View into a \u0026ldquo;black box\u0026rdquo; screen component that you can use anywhere you can use any other Node or Region. And you can do this without worrying about about what\u0026rsquo;s going on inside it, or any of the other parts of the framework, because they don\u0026rsquo;t matter from this perspective.\nAn Example # Let\u0026rsquo;s look at how you\u0026rsquo;d actually code this up. This is just a simple example with enough data in the Model and enough features to give an idea of how it all goes together and works.\nThe Model # We\u0026rsquo;ll look at the Model first, since it\u0026rsquo;s the simplest class:\npublic class Model { private final StringProperty property1 = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty property2 = new SimpleStringProperty(\u0026#34;\u0026#34;); private final BooleanProperty property3 = new SimpleBooleanProperty(false); public String getProperty1() { return property1.get(); } public StringProperty property1Property() { return property1; } public void setProperty1(String property1) { this.property1.set(property1); } public String getProperty2() { return property2.get(); } public StringProperty property2Property() { return property2; } public void setProperty2(String property2) { this.property2.set(property2); } public void bindProperty3(BooleanBinding binding) { property3.bind(binding); } public ObservableBooleanValue property3Property() { return property3; } public String getProperty3() { return property3.get(); } } This is basically the JavaFX version of a \u0026ldquo;Bean\u0026rdquo;. Each field is final and private, and there are delegate methods for getting and setting the values. Finally, there are getters for references to the properties themselves. Note that there is no logic or any relationships between the fields established in the Model - it\u0026rsquo;s just a wrapper for Observable data.\nThe field property3 is a bit different. Since this is intended to be a read-only value based upon one or more of the other fields, it does not have delegate setter for the value. The getter for the property returns a type of ObservableBooleanValue which means that it\u0026rsquo;s read only, so no program can attempt to set its value via set(). There\u0026rsquo;s a method to allow the Binding to be set on the property. We\u0026rsquo;ll see how this works in the Interactor.\nThe Controller # public class Controller { private final Model model; private final Interactor interactor; private final ViewBuilder viewBuilder; public Controller() { model = new Model(); interactor = new Interactor(model); viewBuilder = new ViewBuilder(model, this::saveData); setProperty1Listener(); } private void saveData(Runnable postActionGuiCleanup) { Task\u0026lt;Void\u0026gt; saveTask = new Task\u0026lt;\u0026gt;() { @Override protected Void call() { interactor.saveData(); return null; } }; saveTask.setOnSucceeded(evt -\u0026gt; { interactor.updateModelAfterSave(); postActionGuiCleanup.run(); }); Thread saveThread = new Thread(saveTask); saveThread.start(); } private void setProperty1Listener() { model.property1Property().addListener(ob -\u0026gt; interactor.updateChangeCount()); } public Region getView() { return viewBuilder.build(); } } The constructor for the Controller is the bootstrap for the framework. It instantiates the Model first, then passes it to the constructors of the Interactor and the ViewBuilder. The Controller doesn\u0026rsquo;t actually do anything itself, but it does control how things are done. The saveData() method handles the background threading for the \u0026ldquo;Save\u0026rdquo; action, and calls Interactor methods to do the various stages of the work.\nThe method getView() is just a delegate to the build() method of the ViewBuilder. It\u0026rsquo;ll create a new view every time you call it, which shouldn\u0026rsquo;t really cause any problems as far as the framework is concerned. You could convert this to a \u0026ldquo;lazy load\u0026rdquo; model if you wanted to limit it to a single instance of the View.\nFinally, just to show how it would work, there\u0026rsquo;s an InvalidationListener installed on Model.property1. You can see how it just calls a method in the Interactor to do the actual work.\nThe ViewBuilder # public class ViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final Model model; private final Consumer\u0026lt;Runnable\u0026gt; actionHandler; public ViewBuilder(Model model, Consumer\u0026lt;Runnable\u0026gt; actionHandler) { this.model = model; this.actionHandler = actionHandler; } @Override public Region build() { BorderPane results = new BorderPane(); results.setCenter(createMainBox()); results.setBottom(createButton()); results.setMinWidth(300); results.setMinHeight(200); return results; } private Node createMainBox() { VBox results = new VBox(10, new HBox(6, new Label(\u0026#34;Value 1:\u0026#34;), createBoundTextField(model.property1Property())), new HBox(6, new Label(\u0026#34;Value 2:\u0026#34;), createBoundTextField(model.property2Property())) ); results.setPadding(new Insets(20)); return results; } private Node createBoundTextField(StringProperty boundProperty) { TextField results = new TextField(); results.textProperty().bindBidirectional(boundProperty); return results; } private Node createButton() { Button button = new Button(\u0026#34;Save\u0026#34;); BooleanProperty saveRunning = new SimpleBooleanProperty(false); button.disableProperty().bind(Bindings.createBooleanBinding(() -\u0026gt; (!model.property3Property().get() || saveRunning.get()), model.property3Property(), saveRunning)); button.setOnAction(evt -\u0026gt; { saveRunning.set(true); actionHandler.accept(() -\u0026gt; saveRunning.set(false)); }); HBox results = new HBox(button); results.setAlignment(Pos.CENTER_RIGHT); return results; } } We\u0026rsquo;re not going to look at this too closely, because this article isn\u0026rsquo;t about creating layouts. The result is a BorderPane with a couple of Labels and TextFields in the centre, and a Button at the bottom. The two TextFields have their text properties bound to the two StringProperties in the Model, and the Disable property of the Button is bound to the BooleanProperty in the Model.\nThe OnAction EventHandler on the Button might need some explanation. One of the big problems with Buttons is that people can double click them - or at least click them while the action is still running. Unless you\u0026rsquo;re OK with that, you need to disable a Button as soon as it\u0026rsquo;s clicked, and then enable it when the action is completed.\nSince we\u0026rsquo;re going to have some binding logic that disables the Button if Model.property1 is empty, we can\u0026rsquo;t directly disable the Button when it\u0026rsquo;s clicked. So we introduce a BooleanProperty that indicates that the action is running, and then we Bind the Disable property of the Button to a combination of the two properties. Then we control that property that indicates the action is running to ensure that the Button stays disabled.\nIf you go back to the Controller, you can see how the Runnable that sets that BooleanProperty back to false is invoked when the Task has completed.\nThe Interactor # public class Interactor { private final Model model; private int changeCount = 0; private DomainObject domainObject; private Service service = new Service(); public Interactor(Model model) { this.model = model; createModelBindings(); } private void createModelBindings() { model.bindProperty3(Bindings.createBooleanBinding(() -\u0026gt; !model.getProperty1().isEmpty(), model.property1Property())); } public void updateModelAfterSave() { model.setProperty1(\u0026#34;\u0026#34;); model.setProperty2(domainObject.getSomeValue()); changeCount = 0; } public void saveData() { domainObject = service.saveDataSomewhere(model.getProperty1() + \u0026#34; --\u0026gt; \u0026#34; + changeCount); } public void updateChangeCount() { changeCount++; } } First, take a look at the constructor, and you can see how the Interactor contains the business logic to Bind the value in Model.property1 to the value in Model.property3. This idea, that the save action shouldn\u0026rsquo;t be allowed if the value in Model.property1 is empty, is business logic - so it belongs here, in the Interactor. No way should this Binding be defined in the View.\nThere\u0026rsquo;s also a method, updateChangeCount(), that supports the InvalidationListener in the Controller.\nFinally, we have the two methods that handle the save. The first, saveData() is the code that runs on the background thread. It can read data from the Model, but it cannot update it (that has to happen on the FXAT), while it can freely update other data stored as fields in the Interactor. The other method, updateModelAfterSave() is intended to run on the FXAT, it can freely read and write data in the Model, as well as all of the other fields in the Interactor.\nNote that both of these methods are intended to run specifically on either a background thread or the FXAT, but they don\u0026rsquo;t have any logic (or knowledge at all, really) about the threads contained within them.\nDomain Stuff # Just so that you don\u0026rsquo;t have to use your imagination to see how the Interactor interacts with services and domain objects, we\u0026rsquo;ve got some of that stuff too.\npublic class Service { public DomainObject saveDataSomewhere(String string) { try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } return new DomainObject(string + \u0026#34; - Saved\u0026#34;); } } The service is very simple, it only has one method, saveDataSomewhere(), and it just waits for 3 seconds and returns an instance of the DomainObject.\npublic class DomainObject { private final String someValue; public DomainObject(String someValue) { this.someValue = someValue; } public String getSomeValue() { return someValue; } } The DomainObject is just a POJO to hold a single data value.\nUsing the Framework # So, how do you get all this stuff on the screen?\nYou need to get the JavaFX engine up and running, and that means using the Application class, and using Application.start() to configure your Stage and Scene:\npublic class MvciApplication extends Application { @Override public void start(Stage stage) throws Exception { stage.setScene(new Scene(new Controller().getView())); stage.show(); } } This is pretty simple, instantiate the Controller and then call getView() to get the View. Put the View in a Scene which is, in turn, put into the Stage. Show the Stage. Voila!\nWhat it Looks Like # At the beginning:\nWhile it\u0026rsquo;s running:\nWhen it\u0026rsquo;s done:\nUsing MVCI with FXML # If, for some reason, you feel compelled to use FXML then you can still use this framework. The trick is to see the FXML file, the FXML Controller and the FXMLLoader together as the View component.\nThe hardest part is that you can\u0026rsquo;t have dependency injection via the constructor with the FXML Controller, since it\u0026rsquo;s instantiated via the FXMLLoader. So what you\u0026rsquo;ll need to do is run the FXMLLoader, pull the FXML Controller out of it and then cast it to whatever class you\u0026rsquo;ve actually made it. Then provide some setter methods to allow you to pass a reference to the Model and any of the action performers that your Controller defines. In the setter for the Model, you\u0026rsquo;ll need to include code to perform all of the bindings to the screen Node properties.\nIf you have little enough experience with FXML that the paragraph above sounds like mumbo-jumbo to you, then I\u0026rsquo;d suggest that you just put aside FXML and code your screens without it.\nOther than that, you can put all of that FXMLLoader logic into Controller.getView(). Not a stitch of code in any other place in the framework needs to be changed to use FXML.\nConclusion # If you\u0026rsquo;re going to use JavaFX as a Reactive platform, which you should, then Model-View-Controller-Interactor is the way to go. It\u0026rsquo;s easy to understand and yet deals with all of the issues that you\u0026rsquo;re likely to encounter in a logic and straight-forward fashion.\nPersonally, I find it so easy to implement MVCI that even when I\u0026rsquo;m writing the simplest of example code I immediately just create the 4 classes that you need and go from there. I don\u0026rsquo;t feel any temptation to skip those classes and chuck everything into one place - there\u0026rsquo;s literally no advantage to that.\n","date":"7 November 2022","externalUrl":null,"permalink":"/javafx/Mvci-Introduction/","section":"JavaFX","summary":"An introduction to a framework that works better with JavaFX than MVC or MVVM.","title":"An Introduction to Model-View-Controller-Interactor","type":"javafx"},{"content":" Projects With Multiple Screens and Functions # One of the questions that seems to come up fairly often is about how to create a project that is composed of a number of different screens and functions.\nHow do you connect them together? How do they share data?\nThis seems at first glance to be daunting. Large applications, by definition, tend to be complicated with lots of shared functionality and cross-talk between components. In practice, it can be straight-forward.\nThis is a fairly long article, but mostly because we\u0026rsquo;re going to look at variety of different situations, and a couple of different approaches to some of them. We\u0026rsquo;re going to concentrate here on Model-View-Controller-Interactor, because its structure fits naturally with JavaFX and makes it easy to visualize how the pieces go together. But there\u0026rsquo;s no reason why the ideas here cannot be applied successfully to MVC and MVVM as well.\nSample Project # All of the situations and approaches described in the article are available in a sample project on GitHub. Not all of the code for any of the scenarios is reproduced in this article, so if you want to get a real feel for how the ideas are implemented, you should download the project and look at the code. Each scenario is organized into its own package, so it should be easy to find what you\u0026rsquo;re looking for.\nThe application is built like a typical demo, with a set of a selector buttons for each scenario. It looks like this:\nGo to the Project on GitHub{: .btn .btn\u0026ndash;info}\n\u0026ldquo;Soup to Nuts\u0026rdquo; # The key is to think of each function as a complete \u0026ldquo;mini-application\u0026rdquo; of its own, with its own MVCI structure. So you\u0026rsquo;ll have a structure that looks like this for each function:\nNow, the only question is, \u0026ldquo;How do you link all of these into a single application?\u0026rdquo; The answer is to connect them via the Controllers.\nIt\u0026rsquo;s really no different from how you would compose a simple, single-screen, application where you have an Application class implementation that looks like this:\npublic class SimpleApplication extends Application { @Override public void start(Stage stage) { stage.setScene(new Scene(new SimpleController().getView())); stage.show(); } } The recurring idea is that you instantiate the Controller somewhere, then you call its getView() method to get a JavaFX Region or Node that you can put into another layout or Scene in your application. This technique can be used as part of a Menu or as an action on a Button, or even just a sub-screen in some other layout.\nA good way to think of this idea is that each of these MVCI constructs is a \u0026ldquo;soup to nuts\u0026rdquo; mini-application that presents itself as a custom Node. Once you\u0026rsquo;ve followed this structure, adding on of these functions to a screen is really no different from putting a TableView or a TextField (or any other Node, really) into your layouts.\nAnother important concept is that you\u0026rsquo;ll generally find the \u0026ldquo;shared\u0026rdquo; functionality of your application living in the \u0026ldquo;Domain\u0026rdquo; section. This is the place where you find services, API calls, brokers and business objects that are used throughout the application. You might have multiple screens and functions that require your application to interact with, say, a customer database, and the actual code that does connects to that database is going to be located in the \u0026ldquo;Domain\u0026rdquo; area. This code is generally things that you\u0026rsquo;ll call \u0026ldquo;brokers\u0026rdquo; or \u0026ldquo;services\u0026rdquo; and they accept and deliver \u0026ldquo;business objects\u0026rdquo;, or \u0026ldquo;domain objects\u0026rdquo; How you invoke those services, and how you use the results from them are going to be contained inside the Interactors. The important thing is to remember that the Interactors are where the \u0026ldquo;business logic\u0026rdquo; for specific functions that use those services are located.\nThe Sample Project # In order to show the different ways that you can integrate MVCI frameworks together I\u0026rsquo;ve created a sample application that has a BorderPane main screen with a set of Buttons that will control the content of the Centre region. Each possible content is composed of it\u0026rsquo;s own MVCI construct that can, in turn, contain another MVCI construct.\nTo make a point here, I\u0026rsquo;ve basically taken the entire content of my SWFX project and dropped it into this sample project. this project uses the awesome swapi.dev web API to lookup Star Wars character names. SWAPI is really useful for this kind of demonstration because it\u0026rsquo;s a real website with nifty content and it doesn\u0026rsquo;t require API keys. Also the SWFX project had the API calls split out from the Interactor so that it behaves as a service inside the application, meaning that the Interactor code really just contains logic related to the screens.\nIn the original SWFX project some of the code was in Kotlin, so I\u0026rsquo;ve converted the rest of it into Kotlin. I think this helps to make the point that the encapsulated content is really autonomous. It\u0026rsquo;s even in a different language! Kotlin might be new to you, but I\u0026rsquo;ll review the code a bit to explain anything that might not be clear.\nDifferent Scenarios # In almost any application that you\u0026rsquo;ll build, you\u0026rsquo;re likely to have a \u0026ldquo;Main\u0026rdquo; MVCI construct that runs the show. That means, of course, that it will have its own View, and that where you\u0026rsquo;re going to insert the View of any subsequent MVCI constructs that you create - unless you\u0026rsquo;re going to put them into their own Scenes (more about this later).\nFor the sake of clarity, we\u0026rsquo;ll refer to the outer MVCI - probably the \u0026ldquo;Main\u0026rdquo; MVCI - as the \u0026ldquo;encapsulating\u0026rdquo; framework, and the sub-functions that are invoked from it the \u0026ldquo;encapsulated\u0026rdquo; functions.\nWhile the basic ideas remain the same for just about any circumstance you might come up with to use this idea, there are some variants that you\u0026rsquo;ll need to think about\u0026hellip;\nFully Autonomous Screens # The first situation that we\u0026rsquo;ll look at is the fully autonomous screen element. As mentioned earlier, we\u0026rsquo;ve basically got the entire content of the SWFX project dropped it into this sample project. Some of it was in Kotlin, so I\u0026rsquo;ve converted the rest of it into Kotlin. From this, you should be able to see how this screen comprises autonomous functionality, and it\u0026rsquo;s just inserted into the larger application via its Controller, which is the only component that\u0026rsquo;s accessed from the encapsulating framework.\nYou can see how similar this looks to the Application example. SwController is the Controller for an autonomous MVCI framework from the SWFX project. It\u0026rsquo;s this class that instantiated to create the MVCI framework, and then the View is grabbed and passed to the ViewBuilder for this layout.\npublic class Function1Controller { Builder\u0026lt;Region\u0026gt; viewBuilder; public Function1Controller() { viewBuilder = new Function1ViewBuilder(new SwController().getView()); } public Region getView() { return viewBuilder.build(); } } And the ViewBuilder for the encapsulating MVCI looks like this:\npublic class Function1ViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final Region swContent; public Function1ViewBuilder(Region swContent) { this.swContent = swContent; } @Override public Region build() { StackPane results = new StackPane(swContent); results.setPadding(new Insets(4)); return results; } } You can see here that this is a whole lot of almost nothing. The StackPane is just there to have it not be absolutely nothing. In fact you, could do it like this:\npublic class Function1ViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final Region swContent; public Function1ViewBuilder(Region swContent) { this.swContent = swContent; } @Override public Region build() { return swContent; } } Which works just as well. From this it should be clear that we\u0026rsquo;re just stuffing the visual component from an autonomous MVCI onto the screen, without interacting with it in any other way.\nYou can actually do away with Function1 entirely, and just instantiate SwController in the MainMvciController. It\u0026rsquo;s left the way it is for consistency with the other examples.\nOne last thing to note here\u0026hellip;where\u0026rsquo;s the Kotlin? There\u0026rsquo;s not one stitch of Kotlin code that you need to look at in this example because it\u0026rsquo;s irrelevant. It\u0026rsquo;s all in the SwController, and we just don\u0026rsquo;t care about how it works. So long as it has a getView() method that returns a Region, that\u0026rsquo;s all we need.\nWhere to Instantiate the Encapsulated Controller? # This is an interesting question, and I\u0026rsquo;m sure there will be different opinions on it. In a simple application, it can be such a trivial decision that it\u0026rsquo;s not worth wasting time on while in a complicated application it can become one of the most critical design decisions that you make.\nThere are two choices:\nIn the Controller, and pass the View to the encapsulating ViewBuilder In the encapsulating ViewBuilder The second option seems simpler. Just instantiate the encapsulated MVCI in the ViewBuilder and put its View in the layout. But there\u0026rsquo;s one big consideration: Side Effects.\nIf this encapsulated MVCI does something, especially update data, then it\u0026rsquo;s not really just View any more, is it?\nThat sounds like we\u0026rsquo;re just nit-picking on semantics. But it can be important. View is where we see user interaction, and just user interaction. If we\u0026rsquo;re adding components that do more than that, then we\u0026rsquo;re adding back coupling in the View that we\u0026rsquo;ve worked so hard to avoid. And that coupling always makes it harder to work with an application.\nIt can get even worse. Let\u0026rsquo;s say that this encapsulated MVCI can independently update data that also appears elsewhere in the View. Now we run the risk that data on the screen is out of date because of something that happened on the same screen! And if you put anything in the View that tries to cope with that, then you\u0026rsquo;re getting even further away from having the View be just a View.\nOn the other hand, if you instantiate the encapsulated Controller in the encapsulating Controller than you can treat that as a Controller. If there\u0026rsquo;s data sharing that needs to be coped with, then the encapsulating Controller can deal with it. Then you just pass the encapsulated View to the encapsulating View, and it can remain just a View.\nFor a fully autonomous example like this one, it basically moot. But in terms of establishing a consistent coding technique you\u0026rsquo;re better off instantiating the encapsulated Controller in the encapsulating Controller and just do it that way every time.\nFunctional Custom Node # The next example is to create a \u0026ldquo;custom component\u0026rdquo; that comprises an MVCI framework. For this we\u0026rsquo;re sticking with the SWFX functionality, but creating another class which will become our custom component.\nThe first challenge here is that we need to have our custom class extend some kind of Node. Which Node you pick depends on how close you want your custom class to be handled like a regular JavaFX Node class. Specifically, whether or not you want to instantiate it with new or if you\u0026rsquo;ll supply some static method that returns a Node class.\nFor this example, I wanted to have it act exactly like any other Node, so it\u0026rsquo;s instantiated with new. This means that it cannot just extend Region, because that doesn\u0026rsquo;t have the properties and methods to be a custom layout. So you need to pick a class that does have these methods. This example class extends StackPane, as that\u0026rsquo;s one of the easiest classes to deal with, and doesn\u0026rsquo;t have a lot of complex layout methods, but yet does enough. In this case, we\u0026rsquo;re only going to use the getChildren() method.\nThe second challenge is to structure it nicely. It is possible to have a separate Controller and ViewBuilder, and that will work perfectly. As a matter of fact, the JavaFX controls do that, but they call the ViewBuilder the \u0026ldquo;Skin\u0026rdquo;. It\u0026rsquo;s a bit different but the same idea. Rather than go down the Skinnable path for this example, it\u0026rsquo;s been simplified here into a single class that acts as Controller and ViewBuilder and has the name SwLookupBox.\nTo keep with the spirit of MVCI, the method that handles all of the custom layout is called buildView():\nopen class SwLookupBox : StackPane() { private val interactor: SwInteractor protected val viewModel = SwModel() init { interactor = SwInteractor(viewModel) children += buildView() } private fun searchForCharacter(postSearchAction: Runnable) { val searchTask: Task\u0026lt;Void?\u0026gt; = object : Task\u0026lt;Void?\u0026gt;() { @Throws(Exception::class) override fun call(): Void? { interactor.lookupPerson() return null } } searchTask.onSucceeded = EventHandler { interactor.updateModelAfterLookup() postSearchAction.run() } val searchThread = Thread(searchTask) searchThread.start() } private fun buildView(): Region = HBox(6.0).apply { children += Labels.prompt(\u0026#34;Name:\u0026#34;) children += TextField().apply { textProperty().bindBidirectional(viewModel.name) } children += Button(\u0026#34;Search\u0026#34;).apply { onAction = EventHandler { isDisable = true searchForCharacter { isDisable = false } } } } } OK, we actually have some Kotlin here. Here are some things that might confuse you, the rest of the code you can probably figure out:\nDeclarations Declarations are backwards from Java, in the form \u0026ldquo;name : type\u0026rdquo;. This goes for variables, parameters and methods (which are called \u0026ldquo;fun\u0026rdquo; in Kotlin). Values can be assigned, and new isn\u0026rsquo;t needed for constructor calls. Object.apply{} The apply method takes a block of code and passes the object to it as this. In Kotlin, you can leave out the this if it\u0026rsquo;s clear that a method call refers to it. children += These lines equate to this.getChildren().add(). Lambdas as The Last Parameter If the last parameter passed to a method or constructor is a lambda, it can be defined in {} outside the regular parameter list. So EventHandler {} calls the constructor for EventHandler and passes a lambda as its only parameter. Since it\u0026rsquo;s really now a JavaFX Node, it\u0026rsquo;s instantiated from within the ViewBuilder for the encapsulating MVCI:\npublic class Function2ViewBuilder implements Builder\u0026lt;Region\u0026gt; { @Override public Region build() { VBox results = new VBox(6, Labels.h3(\u0026#34;Star Wars Lookup\u0026#34;), new SwLookupBox()); results.setPadding(new Insets(40)); return results; } } You probably need to be careful that this approach doesn\u0026rsquo;t become a \u0026ldquo;slippery slope\u0026rdquo; situation, and start to pick and choose which elements of MVCI framework you discard when it\u0026rsquo;s not convenient. But if you really, really, really want your custom stuff to feel like a standard JavaFX Node, this is something to think about.\nScreens that Accept and Return Values # Generally speaking, unless an encapsulated MVCI framework has side-effects, it\u0026rsquo;s not able to do anything unless it shares data with the encapsulating MVCI framework. Think about a control like a TextField, it needs to have a link to/from its Text property to your model to be of value. Encapsulated MVCI frameworks can work the same way.\nVia Encapsulated MVCI # The general idea is that the encapsulated Controller needs to accept one or more properties from the encapsulating controller that it can bind to its own Model. In this case, we\u0026rsquo;re just going to pass the planet property to the encapsulated controller.\npublic class Function4Controller { Builder\u0026lt;Region\u0026gt; viewBuilder; Function4Model model = new Function4Model(); public Function4Controller() { Function4Interactor interactor = new Function4Interactor(model); SwLookupController planetFinderController = new SwLookupController(model.planetProperty()); viewBuilder = new Function4ViewBuilder(model, planetFinderController.getView(), interactor::convertToUpperCase); } public Region getView() { return viewBuilder.build(); } } Let\u0026rsquo;s have a look at the SwLookupController. It\u0026rsquo;s in the starwars package, so it\u0026rsquo;s in Kotlin too:\nclass SwLookupController(val planetNameProperty: StringProperty) { private val interactor: SwInteractor private val viewModel = SwModel() private val viewBuilder: SwLookupViewBuilder init { interactor = SwInteractor(viewModel) viewBuilder = SwLookupViewBuilder(viewModel) { searchForCharacter(it) } planetNameProperty.bind(viewModel.homePlanet) } private fun searchForCharacter(postSearchAction: Runnable) { val searchTask: Task\u0026lt;Void?\u0026gt; = object : Task\u0026lt;Void?\u0026gt;() { @Throws(Exception::class) override fun call(): Void? { interactor.lookupPerson() return null } } searchTask.onSucceeded = EventHandler { interactor.updateModelAfterLookup() postSearchAction.run() } val searchThread = Thread(searchTask) searchThread.start() } fun getView(): Region = viewBuilder.build() } In our SwModel, we already have a property for homePlanet, so all we need to do to connect it to the encapsulating MVCI is to bind homePlanet to the supplied property from the encapsulating MVCI.\nVia Functional Custom Node # Once again, all of the action takes place in the encapsulating ViewBuilder, rather than the Controller, with the custom Node approach:\npublic Region build() { SwPlanetFinder swPlanetFinder = new SwPlanetFinder(); model.planetProperty().bind(swPlanetFinder.planetProperty()); VBox results = new VBox(20, Labels.h3(\u0026#34;Planet Finder Component\u0026#34;), swPlanetFinder, createUpperCaseBox()); results.setPadding(new Insets(40)); return results; } SwPlanetFinder is simply an extension of SwLookupBox with a getter for the planet property:\nclass SwPlanetFinder : SwLookupBox() { fun planetProperty(): ReadOnlyStringProperty = viewModel.homePlanet } Note that this is backwards from the previous approach. The SwPlanetFinder presents a getter for the planet property, and it\u0026rsquo;s the responsibility of the encapsulating View to handle the binding. This also matches the design of the properties for the standard JavaFX Node classes - provide getters for the properties.\nLoading in a new Scene # This scenario comes up all the time with the FXML crowd. There\u0026rsquo;s something about the way that FXML is understood that makes beginners think that they need to load every layout into its own Scene. Then they get lost about how they might possibly share data between two Scenes.\nYou should be getting the idea by now that how you put your View on the screen has absolutely nothing to do with how the various functions are connected to each other. Load your View into a Panel in another View, use it like a custom component, call it from a MenuItem, or put it into its own Scene - it\u0026rsquo;s all the same in terms of connectivity.\nIn this example, we\u0026rsquo;re going to create an encapsulating MVCI that launches an extra window with our search functionality. There\u0026rsquo;s no attempt to do anything slick here, and the code doesn\u0026rsquo;t contain anything to stop you from opening multiple windows (it actually works fine when you do this), and they open up on top of the main window.\nInstead of passing a View to the encapsulating View, we\u0026rsquo;re going to pass an action to open up the new window:\npublic class Function5Controller { Builder\u0026lt;Region\u0026gt; viewBuilder; Function5Model model = new Function5Model(); SwLookupController planetFinderController; public Function5Controller() { Function5Interactor interactor = new Function5Interactor(model); planetFinderController = new SwLookupController(model.planetProperty()); viewBuilder = new Function5ViewBuilder(model, this::launchWindow, interactor::convertToUpperCase); } private void launchWindow() { Stage stage = new Stage(); stage.setScene(new Scene(planetFinderController.getView())); stage.show(); } public Region getView() { return viewBuilder.build(); } } You can see that the content of the new window is nothing special, just the same structure that we\u0026rsquo;ve used before.\nInside the encapsulating View, we\u0026rsquo;ll add a Button to trigger that action:\npublic Region build() { VBox results = new VBox(20, Labels.h3(\u0026#34;Planet Finder MVCI\u0026#34;), createWindowButton(), createUpperCaseBox()); results.setPadding(new Insets(40)); return results; } private Node createWindowButton() { Button results = new Button(\u0026#34;Launch Window\u0026#34;); results.setOnAction(evt -\u0026gt; windowLauncher.run()); return results; } A Bigger Lesson From This # In reality, you\u0026rsquo;ll probably not be loading up Scenes very often. However, the idea that you might launch an Action to link your View element to the encapsulated MVCI can be useful. Imagine that you have a MenuItem that is supposed to start up an MVCI and load it into the GUI - you would do that through an action.\nThis would mean that you would replace the launchWindow() method above with something that instantiates an MVCI Controller, calls its getView() method and puts that onto the GUI somewhere. Then you pass that method reference to the MenuItem.\nScreens that Use the Parent\u0026rsquo;s Interactor # This is where things really start to get interesting. Sometimes you have piece of functionality that\u0026rsquo;s used in a variety of places, so you don\u0026rsquo;t want to repeat the same code over and over. It has some of its own business logic, but how it works depends on the encapsulating function.\nAn example of this might be a \u0026ldquo;Customer Lookup Box\u0026rdquo;. Pretty much the View and some of the logic is going to be the same no matter where it\u0026rsquo;s used. But if you put it into an invoice inquiry screen, then you only want to see customers with Invoices, and if it\u0026rsquo;s a customer rewards screen, you only want to see customers enrolled in the rewards program. Or maybe it\u0026rsquo;s something way more subtle or complicated.\nThis kind of scenario comes up more often than you might expect in larger applications, and how you handle it can make a big contribution to how easy it is to develop and maintain that application.\nWhat you need to do is to find a way to allow the encapsulated MVCI to delegate at least some of its functionality to the encapsulating MVCI. In just about every case, you\u0026rsquo;ll probably need to share data, too. So that\u0026rsquo;s what this example shows.\nHandling Background Threads # This is the trickiest part. You need to run any blocking service requests off the FXAT and on a background thread. But where to do this? In the encapsulated Controller or the encapsulating Controller?\nFirst, let\u0026rsquo;s look at how a basic situation is handled without multiple MVCI constructs. You have a flow that looks like this:\nThe process is triggered via the Event handling in the View which is going to invoke an action defined in the Controller. The Controller is going to instantiate and configure a Task, which all happens on the FXAT, and then start it up on a background thread. Typically, the Task's call() method is just going to invoke a method in the Interactor. That method will have any business logic that\u0026rsquo;s required to get ready to invoke some Service call, call the Service and the deal with any data that comes back. Then the OnSucceeded EventHandler of the Task is going to be invoked on the FXAT (that\u0026rsquo;s how we get back on the FXAT), and it will generally call another method in the Interactor that deals with the new data and updates the Model according to its business logic. Finally, a Runnable passed from the View to the Controller is invoked to perform any wrap-up stuff that\u0026rsquo;s required in the View.\nNow, with an encapsulated MVCI, you\u0026rsquo;ll still have just one stretch of actions that take place on the background thread, but the flow will be a little bit different:\nYou can see that all of the background stuff is \u0026ldquo;owned\u0026rdquo; by the outer MVCI. So that\u0026rsquo;s where the Task needs to go. Everything that happens on the inner MVCI happens on the FXAT. Just knowing this makes it easier to figure out how to implement it.\nThe example for this is in the sample project, but contains too many classes to include the complete code in this article. It has two MVCI constructs (the inner and outer), and a dummy Broker with a dummy domain object so that the entire process can be easily visualized. The data elements in both of the Models is manipulated just so that it\u0026rsquo;s easy to see what code has been executed as the processing runs.\nLet\u0026rsquo;s take a look at how the boxes in the diagram above are implemented in code:\nEvent Handling This happens in the inner View. Some Node, like a Button triggers an Event which is passed to its EventHandler. This will do some GUI stuff and then call the accept() method of a Consumer from the Inner Controller, passing it a Runnable containing the code to reset the GUI after the work is complete. button.setOnAction(evt -\u0026gt; { button.setDisable(true); buttonAction.accept(() -\u0026gt; { button.setDisable(false); }); }); Pre-Processing (Inner) The inner Controller calls a method in the inner Interactor to handle any set-up that needs to be done before passing control over to the outer MVCI. Then the inner Controller calls the accept() method of the Consumer supplied by the outer Controller, passing in a Runnable containing all of the inner MVCI post Task processing. private void performLookup(Runnable postActionGuiStuff) { interactor.preLookupProcessing(); outerLookupHandler.accept(() -\u0026gt; { interactor.postLookupProcessing(); postActionGuiStuff.run(); }); } This code snippet shows all of the inner Controller handling for the lookup. This covers the steps \u0026ldquo;Model Update (Inner)\u0026rdquo;, and \u0026ldquo;GUI Cleanup\u0026rdquo; described below for the inner Controller. {: .small}\nPre-Processing (Outer) The outer Controller calls a method in the outer Interactor to handle any set-up that needs to be done before passing control over to the Task. private void handleInnerLookup(Runnable innerRunnable) { interactor.preProcessLookup(); Task\u0026lt;Void\u0026gt; task = new Task\u0026lt;Void\u0026gt;() { @Override protected Void call() throws Exception { interactor.performLookup(); return null; } }; task.setOnSucceeded(evt -\u0026gt; { interactor.updateModelFromLookup(); innerRunnable.run(); }); Thread taskThread = new Thread(task); taskThread.start(); } This code snippet shows all of the outer Controller handling for the lookup. This covers all of the next three steps described for the outer Controller. {: .small}\nTask Creation The outer Controller creates a Task to handle the background thread activity. It specifies actions to be performed when the Task completes. Then the outer Controller creates a Thread to run the Task and starts it. Pre-Service Logic, Service Call \u0026amp; Post Service Logic The call() method of the Task simply invokes a method in outer Interactor which handles the background work. This would involve any business logic required to prepare for the call to the external Service, and the actual call to that Service. After the Service call, more logic will deal with the data returned from the Service call. public void preProcessLookup() { model.setProperty4(\u0026#34;Starting lookup\\n\u0026#34;); model.setProperty1(\u0026#34;Pending...\u0026#34;); model.setProperty2(\u0026#34;Pending...\u0026#34;); model.setProperty3(model.getProperty3().toUpperCase()); } This is from the outer Interactor. {: .small}\nModel Update (Outer) The setOnSucceeded() method of the Task specifies first a call to another method in the outer Interactor which will update the outer Model using the data returned from the Service call. Any portion of the outer Model shared with the inner MVCI will be automatically updated. public void updateModelFromLookup() { progressMessages += \u0026#34;Updating the Model\\n\u0026#34;; model.setProperty1(domainObject.getValue1()); model.setProperty2(domainObject.getValue2() + \u0026#34;, \u0026#34; + domainObject.getValue3()); model.setProperty4(model.getProperty4() + progressMessages); } This is from the outer Interactor. {: .small}\nModel Update (Inner) The last step of the setOnSucceeded method in the Task is to invoke the Runnable passed from the inner Controller. This Runnable will first call the inner Interactor to handle any updates to the inner Model required by the business logic. public void postLookupProcessing() { model.setVariable2(\u0026#34;All done\u0026#34;); } This is from the inner Interactor. {: .small}\nGUI Cleanup Finally, the last step of the Runnable passed from the inner Controller is to invoke the Runnable passed from the inner View as part of the first step. This will put the GUI into the state expected while the background Task is not running. How it Looks # Here\u0026rsquo;s the screen before the button is clicked:\nRight after the button is clicked, and while waiting for the \u0026ldquo;Service\u0026rdquo; to complete:\nFrom here you can see that the Button is now disabled, and all of the inner and outer pre-processing has been performed.\nAnd when it\u0026rsquo;s all done:\nNow all of the post processing has been completed and the Button is re-enabled. Note that the intermediate status updates from the background process are only reflected on screen (and in the Model) after that process has completed.\nReasons Not to Do This # There are probably lots of genuinely good reasons to use this approach, but it does add a layer of complexity to your MVCI frameworks.\nThe question is whether or not you should go this way.\nOne alternative is to put *ALL of the business logic into the outer Interactor, and remove the inner Interactor, as it will now be empty. The result of this is that your inner framework is more of just a layout than a framework and you can treat it as such.\nGenerally speaking, if your reason for splitting the business logic between two frameworks is founded in something other the specific business logic of the frameworks, you should handle it some other way. For instance, if your reason is to share a database connection, control resource loads or optimize performance, those things should be handled at your Service level, or even lower. Doing this means that you can put all of the business logic for the inner framework inside the inner Interactor, and there\u0026rsquo;s no need to have any dependency on the outer Interactor - meaning that the inner framework becomes largely autonomous.\nConclusion # This is pretty long article about what turns out to be a fairly simple technique. But it works because of two much more important concepts that you should understand:\nLoose Coupling Essentially, this idea of joining together disparate bits and pieces into an application works because the MVCI framework provides very loose coupling. The layouts have no idea where the data models come from, nor how the action providers work. The layouts also have no knowledge of how they are contained on the screen and no dependencies on that at all. From the other end, the containing frameworks have no knowledge of the inner structure of the enclosed MVCI framework. They do know that they need to provide data elements and action handlers that are specified in the constructor of the enclosed Controller - but that just about it. The only other dependency is that the enclosed Controller will have a getView() method. Custom Nodes Another really big underlying idea is that any layout that you construct, and any sub-layouts inside them can be considered as a \u0026ldquo;Custom Node\u0026rdquo;. Even if you just create an HBox and put a couple of Labels inside it, you can think of that as a Custom Node. Even if it\u0026rsquo;s just a Label with some styling, you can still think of that as a Custom Node. And just like any other Object that you create, its inner workings should be private from the rest of your layouts. This last point is really about your perspective as a JavaFX programmer. Don\u0026rsquo;t think of your GUI\u0026rsquo;s as a mish-mash of components configured and glued together - think of it as a hierarchy of custom components working together. And this idea scales upwards - from configured Labels right up to the top of your application structure. Just make sure each component is complete within itself and hides its innards from the rest of your code.\n","date":"2 October 2022","externalUrl":null,"permalink":"/javafx/techniques/mvci/multimvci/","section":"JavaFX","summary":"How to set up a more complex project with multiple screens with varying levels of connectedness.","title":"Multi-MVCI Projects","type":"javafx"},{"content":" Introduction # Model-View-Controller (MVC), Model-View-ViewModel (MVVM) and Model-View-Presenter (MVP) are three of the most common frameworks that you are likely to encounter when working with systems that have a User Interface (UI). It\u0026rsquo;s really important to understand how these frameworks work, what they achieve, and how to implement at least one of them in the environment that you work in.\nI have to say that I struggled with this article for a long time. I worked for years with a pattern that I had assumed was MVC, and felt that it was somehow incomplete. So I added some bits on, and made adjustments that made sense to me. Then I learned that my understanding of \u0026ldquo;Model\u0026rdquo; was not the same as the technical definition. That \u0026ldquo;correct\u0026rdquo; definition of Model makes MVC more complete, but I\u0026rsquo;m not really happy that it maps well to JavaFX or any other modern platform.\nWho am I to argue with the giants of application framework theory? No one, really. But I can say that my understanding of how to build clean and simple JavaFX has come over nearly ten years of practical experience, working at it pretty much 9-5 every weekday. From that experience, constantly trying improve my approach with each new application, I\u0026rsquo;ve arrived at what I think is a better solution.\nWhat\u0026rsquo;s Their Purpose? # These are design patterns. They are standard ways of architecting things to achieve common ends. The advantage to using a design pattern is that they are generally well known and well thought-out, so they are familiar and easy to build and to understand when you encounter them in the real world.\nIn this particular case, these are different approaches to solve the problem of separating the presentation of data to the user from the actual data and the back-end logic. In this way, the UI becomes much less dependent on the back-end structure (and visa versa) so that changes to either the UI or the back-end don\u0026rsquo;t require refactoring the other end.\nThis is a huge concept. Let\u0026rsquo;s say that you have something like a customer inquiry screen for your application. If the people in charge come to you and say, \u0026ldquo;We\u0026rsquo;re going to export all of our customer data out of our main database and use this brand new CRM system instead, from now on you\u0026rsquo;ll have to populate your screen from the CRM system\u0026rsquo;s API\u0026rdquo;, you don\u0026rsquo;t want to have to mess with your GUI code in order to change the data storage.\nLet\u0026rsquo;s say this CRM system stores the data in a little different way. Perhaps it has a separate field for name suffixes, like \u0026ldquo;Jr\u0026rdquo;, \u0026ldquo;Sr\u0026rdquo;, \u0026ldquo;III\u0026rdquo; or \u0026ldquo;Esq\u0026rdquo;. Once again, you don\u0026rsquo;t want to have to change your GUI to accommodate that, adapting the CRM data to your screen is something that your business logic should handle.\nLet\u0026rsquo;s say the CRM system only allows you to update a customer twice in one day. That error processing should be part of the business logic and handled without changing your main GUI. Maybe there\u0026rsquo;s a pop-up message when the save fails.\nIn another scenario, maybe you have several different uses for your logic that require slightly different GUI\u0026rsquo;s. Perhaps you have an inquiry screen. It uses all the same logic and data, but the entry fields are display only. It would be nice if you could create an inquiry only GUI, and then just connect it up to the same back-end and business logic that your data entry screen uses.\nThese frameworks should let you handle all of these situations.\nUnderstanding the Terminology # A small amount of time spent searching the web will show you that there\u0026rsquo;s many different interpretations of what design patterns really mean. Even if you go back to some of the original articles where these patterns were first introduced, there\u0026rsquo;s a lot of ambiguity and room for interpretation.\nSo let\u0026rsquo;s look at the most confusing concept first:\nThe \u0026ldquo;Model\u0026rdquo; # There\u0026rsquo;s an idea that data should be bundled up with code that is related to it, so that the result is a responsive object rather than just a bunch of data in fields. This is a common concept in OOP system design, and you\u0026rsquo;ve undoubtedly seen it many, many times. Even if it\u0026rsquo;s just getters and setters to isolate the internal implementation of the data from it\u0026rsquo;s outward presentation.\nThat\u0026rsquo;s the concept behind \u0026ldquo;Model\u0026rdquo; in all of these design patterns. It\u0026rsquo;s not just the data, but it\u0026rsquo;s the data handling and business logic as well.\nThere\u0026rsquo;s a big issue with the name \u0026ldquo;Model\u0026rdquo;, though. While everybody understands that data and associated logic should be packaged together, \u0026ldquo;Model\u0026rdquo; implies a very data-centric orientation of this element of the framework. This doesn\u0026rsquo;t really encompass things like external API calls, data persistence and overall business logic - all of which are supposed to go in the \u0026ldquo;Model\u0026rdquo;. We\u0026rsquo;ll see how this causes problems.\nDomain Data vs Presentation Data # \u0026ldquo;Domain Data\u0026rdquo; is the under-the-hood representation of the data in your application. Let\u0026rsquo;s say that you have a database with customers and customer orders. You might have a Broker/DAO that returns the customer name, address and email, along with the date of their last order from your database. That information could come from several tables in the database, and it might be complicated to put it together, but it delivers it to you in a class called CustomerInfo. This class, CustomerInfo is \u0026ldquo;domain data\u0026rdquo;.\n\u0026ldquo;Presentation Data\u0026rdquo;, however, is data that\u0026rsquo;s been packaged up, configured and prepared for use in the GUI. Let\u0026rsquo;s go back to CustomerInfo. Perhaps this class has the name divided into firstName and lastName. For your GUI however, you just need a single name element. Something, somewhere has to take the two CustomerInfo fields and transform them according to some business rules into a single data element available to the GUI. Maybe it\u0026rsquo;s \u0026ldquo;firstName lastName\u0026rdquo;, maybe it\u0026rsquo;s \u0026ldquo;lastName, firstName\u0026rdquo; or something else.\nFinally, in Reactive JavaFX, the Presentation Data needs to be in some kind of Observable data type. So in our name example this would be a StringProperty.\nBusiness Logic vs Presentation Logic # Most of the time, it\u0026rsquo;s pretty clear whether a piece of logic needs to be considered the implementation of a business rule, rather than purely an aspect of the View. This would dictate whether or not it belongs in the Model.\nSometimes, it\u0026rsquo;s not so clear. Let\u0026rsquo;s go back to that conversion of firstName and lastName into a single name field, and how it\u0026rsquo;s converted. Is it business logic, or is it simply an aspect of the View?\nThat can depend. In a lot of cases, it\u0026rsquo;s purely View. You\u0026rsquo;ll get the greatest decoupling of the elements by having both firstName and lastName in your Presentation Data, and letting the View decide how to display them. But what if there\u0026rsquo;s a requirement to display a bunch of names in alphabetical order? Now you might be talking about a business rule. Sorted by firstName or lastName? So maybe, in this case, you need to deal with the conversion in the Business Logic.\nTraffic Management # Every framework needs some element to manage how the parts interact with each other, and possibly with other frameworks. There needs to be a component that accepts some signal from the View - possibly containing data - and turning that into an action involving the business logic that\u0026rsquo;s contained inside the Model. There also needs to be some component that understands about background threads, and has the logic to control them.\nHow Do These Patterns Work? # The first thing to understand is that all of these patterns have the same goal, to create independence between the business logic and the presentation that the user interacts with. They all have a great deal of similarity, but they have some profound differences in the details.\nLet\u0026rsquo;s look at each of these patterns and try to figure out the best understanding of what they mean.\nYour Interpretation May Be Different # Before we start, you should note that there are many, many interpretations of how these patterns work. Google any of them and you\u0026rsquo;ll find lots of pages that contradict each other, and the longer you look, the more you will find.\nFor the most part, these patterns were designed long, long ago. Long before web pages, long before Java. If you go back to the earliest discussions about these patterns, you\u0026rsquo;ll find that they\u0026rsquo;re highly theoretical.\nMartin Fowler\u0026rsquo;s page on GUI Architectures is a great modern review, but it\u0026rsquo;s still heavy reading and contains things like this:\nSo it\u0026rsquo;s not surprising that virtually everyone using one of these patterns hasn\u0026rsquo;t actually read one of these technical papers from way back, and they\u0026rsquo;ve picked it up from some web tutorial, written by someone who picked it up from someone else who hasn\u0026rsquo;t read one of the original papers either. And, of course, all of these people are trying to adapt it to their platform after reading about someone else\u0026rsquo;s adaptation on some different platform.\nFor this discussion, I\u0026rsquo;ll try to present a description that matches the original, formal definition as much as possible. However, your understanding of these patterns is very likely to be different to what you\u0026rsquo;ll find in this article.\nWith that in mind, we\u0026rsquo;ll start with MVP and MVC:\nFrom their diagrams, they look very similar, with the only difference being that in MVC the View gets updates directly from the Model. However, the way that they work is very, very different.\nModel-View-Presenter # Model-View-Presenter (MVP) has three components which each have very specific roles:\nThe Model As previously discussed, the Model has all of the data, and all of the business logic contained within it including service calls and persistence logic. It should also make data available in a state ready to be applied to the View. Calls to databases and external API\u0026rsquo;s also go in the Model. The View In MVP the View is supposed to be an entirely passive layout entity containing only static configuration and styling. It contains virtually no decision-making logic of it\u0026rsquo;s own, and it\u0026rsquo;s entire purpose is to put data on the screen, and to receive input from the user. Once it gets input from the user, its only role is to pass it on to the Presenter, which will decide how to handle it. The Presenter The Presenter works hand-in-glove with the View. The Presenter has access (perhaps indirectly) to the View and the inside components of the View and manipulates them to make the View behave as it should. For instance, the Presenter is responsible for placing data from the Model into the View components, and visa versa. The Presenter contains all of the logic for defining how active components like Buttons will behave. MVP uses the View for pure layout and styling. All other aspects of the GUI are handled via the Presenter. It\u0026rsquo;s hard to imagine a system where the Presenter and the View are not tightly coupled. The Presenter has to have detailed information about the internal structure of the View in order to configure it. In this respect, it\u0026rsquo;s probably easiest to treat the Presenter and View as a single component.\nThe Problem with Model-View-Presenter # The problem with MVP is the intense coupling between the View and the Presenter. It\u0026rsquo;s hard to imagine a situation where you could do anything less trivial than move a widget around on the screen, or change its styling without needing to make a corresponding change to the Presenter.\nThe end result is that the View and the Presenter are essentially one object divided into two classes and irrevocably tangle together. This, of course defeats the purpose of using a framework in the first place. This kind of makes MVP useless, in my opinion.\nOne situation where I could see implementing MVP is if you are married to the idea that FXML somehow gives you an automatic step up towards a framework. In that case, if you are careful, you can implement the FXML file as the View, and then the FXML Controller as the Presenter. Of course, you\u0026rsquo;ve still just started to implement MVP - which is probably not worth the effort.\nModel-View-Controller # In the traditional, non-Reactive, implementation of MVC the View is generally seen as only a consumer of the Model\u0026rsquo;s data, and the Controller is responsible for taking any updates from the User, and updating the Model.\nThe Model The Model contains all of the data in the framework. This includes the elements of State, plus any domain objects that are required for the business logic. The Model exposes some \u0026ldquo;presentation ready\u0026rdquo; data elements to the Controller and the View. It also contains all of the logic to interface with external parts of the system such as databases or external API\u0026rsquo;s. The View The View contains all of the elements of the UI and, unlike the View in MVP, is complete unto itself. There is some mechanism to allow the View to be directly updated from the Model. Perhaps the View has a reference to the Model, and can call getters in the Model (when notified by the Controller), or perhaps the Model has a reference to the View and can call setters in the View (probably not a good design). The Controller The Controller handles the actions for the GUI. This includes transporting updates from the View to the Model, and handling \u0026ldquo;events\u0026rdquo; triggered by user actions. MVC couples the View to the Model, since it needs to be able to access data elements from it and from the Controller to the View since it needs to get data from the View.\nThe Problem with Model-View-Controller # The big issue with MVC is when you try to implement it with a reactive GUI. Remember that a reactive application has a data representation of its \u0026ldquo;State\u0026rdquo;, which in MVC is stored in the Model. Then the GUI and the data representation of state are kept in lockstep with each other. The GUI changes to reflect changes in the data, and the data changes to reflect actions in the GUI.\nHowever, in MVC the View is allowed to look at and update from the Model, but it\u0026rsquo;s not allow to update the Model directly. All changes to the Model from the View have to go through the Controller. In JavaFX, this rules out using Bindings from the View Nodes to the Model elements - which is a showstopper.\nNow, if you are using JavaFX in a non-reactive way, then MVC may be just the thing for you. But if you are not using JavaFX in a reactive way, then you\u0026rsquo;re also missing out on a lot of the power of JavaFX - and making a lot of work for yourself.\nModel-View-ViewModel # At first glance MVVM appears to be the best match for modern, reactive UI\u0026rsquo;s as it specifically talks about \u0026ldquo;binding\u0026rdquo; to connect the View to the ViewModel.\nThe ViewModel The ViewModel contains the data in a structure appropriate to bind it to various components of the View. It also contains the functionality to prepare the data in that format (called \u0026ldquo;Presentation Logic\u0026rdquo; in the diagram). In order to do this, the ViewModel needs to access to the data elements stored in the Model, and will probably call methods in the Model to initiate data storage and retrieval. Additionally, the ViewModel handles actions triggered by user \u0026ldquo;events\u0026rdquo; from the View. The Model The Model in this pattern potentially leans more towards dealing with application domain data, as the ViewModel handles some amount of the transformation of this data into presentation data. From that perspective, the business logic is split between the Model and the ViewModel, with the Model handling persistence and external API\u0026rsquo;s. The View Just as in MVC, the View in MVVM is self-contained and includes not just the layout but styling and configuration of the elements. The big difference is that data elements from the ViewModel are bound to the screen elements. This means that the \u0026ldquo;State\u0026rdquo; of the View is accessible to the ViewModel at any given time and there\u0026rsquo;s no need to provide any functionality to \u0026ldquo;scrape\u0026rdquo; the data out of the View to pass it to the ViewModel. At the same time, the ViewModel can update the View simply by changing the value in those data elements bound to the View. The Problem with Model-View-ViewModel # The big problem with MVVM is that the ViewModel isn\u0026rsquo;t allowed to see the domain objects, and the Model isn\u0026rsquo;t allowed to see the presentation objects.\nThis means that the Model can\u0026rsquo;t just pass over a CustomerInfo object to the ViewModel and let it figure out what it needs to take out to populate the elements of the presentation objects. No, the Model has to provide some sort of \u0026ldquo;presentation ready\u0026rdquo; data to the ViewModel. So if the ViewModel needs customerName, then it has to ask for it and the Model pulls it out of CustomerInfo and sends it as a String.\nOn top of that, the ViewModel cannot have any business logic. So if customerName is sourced from 5 different fields in CustomerInfo and put together in some particular way which satisfies business rules, then it has to happen in the Model.\nAnd the same conditions hold true in reverse. If the ViewModel wants to request an update to the Customer database, it can\u0026rsquo;t just pass a new CustomerInfo record to the Model. It has to pass either discrete primitive data elements to the Model, or some DTO-like object instead.\nThe Elephant in the Room # With all of these frameworks, there\u0026rsquo;s one implied rule that needs to be openly talked about:\nDomain Objects Can Only Exist in the Model Think about it. What would be the point of any of this if you had to change the layout code in your \u0026ldquo;Customer Inquiry Screen\u0026rdquo; to accommodate a change in the CustomerInfo object? The same thing goes for the Presenter, Controller or ViewModel. The decoupling requires that CustomerInfo and the all of the business logic that deals with it resides only in the Model. This leads to the following corollary:\nThe Model Has to Present \u0026ldquo;GUI-Ready\u0026rdquo; Data Upstream Since the Model is the only element that can deal with Domain Objects, it follows that it has to contain the logic that extracts data from the Domain Objects, packages it up in a manner that the Presenter, Controller or ViewModel can deal with it, and provide some way to transfer it out of the Model. And this has the following consequence:\nEverybody Encapsulates \u0026ldquo;GUI-Ready\u0026rdquo; Data in a Class # There\u0026rsquo;s a problem that pops up in the class that creates and holds the presentation data - in MVC it\u0026rsquo;s the Model, and in MVVM it\u0026rsquo;s the ViewModel. And that problem is that those classes end up with a massive amount of fields and access methods that get jumbled in with the other functionality of the class. On top of this, all of those public accessor methods become coupling points between that class and the other classes in the framework.\nThink about it, if you\u0026rsquo;re using MVC and you have 30 Property elements that are needed for the View, plus maybe a List or two to support some tables - and if you follow the ordinary practice of having a delegate setter and getter, plus a Property getter for each of those elements then you are going to have 90+ public methods dedicated to just that in your Model. That\u0026rsquo;s in addition to the business logic and fields to hold domain objects and the rest.\nIf nothing else it becomes a mess to deal with, and is a pretty clear violation of the \u0026ldquo;Single Responsibility Principle\u0026rdquo;.\nTo get around this issue, composition is used, and the presentation-ready data is placed into its own class which is then a single field in the Model or ViewModel. If you want to have a single method like Model.getCustomerData() that returns a value that can be used by the Controller or the View, then that return value has to be a class that encapsulates presentation-ready data. And that class, for all intents and purposes, is a \u0026ldquo;Presentation Model\u0026rdquo;.\nWhat this means, as a practical matter, is that virtually every implementation of these frameworks has 4 components: A Model, a View, one of Controller, Presenter or ViewModel, and a Presentation Model.\nThis is Where the Problems Occur # A little bit of time spent searching the Web will find lots and lots of sites explaining how MVC works with examples. Virtually without exception, they all explain the Model has data and associated logic, some of them with diagrams that show the Model directly connected to the database. But then, in their example code\u0026hellip;\nThey Cheat The Model has just Presentation Data in it, without any persistence or business logic and they dummy up a Model in the main() class that has a database call. or\nThey Put Persistence in the Controller The Model has just Presentation Data in it, and the Controller has a simple database call that stuffs the Model with data from the database. In other words, virtually every example that you\u0026rsquo;ll find, consists of a Presentation Model, View and Controller. No \u0026ldquo;Model\u0026rdquo;, as defined by the framework, to be found anywhere. It\u0026rsquo;s probably because they cannot reconcile the idea of having all these elements of GUI data cohabiting and cluttering up a class with business logic.\nTo be fair, this is completely logical and reasonable way to handle the \u0026ldquo;GUI\u0026rdquo; part of an application. It\u0026rsquo;s highly intuitive and makes a lot of sense. In fact, A lot of people think that this is what MVC is.\nBut there\u0026rsquo;s one issue. What about the business logic, persistence and external API\u0026rsquo;s?\nA Better Framework for Reactive Systems: MVCI # As I said earlier, I went on for years misunderstanding the \u0026ldquo;correct\u0026rdquo; structure of MVC. My understanding was the the \u0026ldquo;Model\u0026rdquo; was simply the \u0026ldquo;Presentation Model\u0026rdquo;. In a Reactive design, \u0026ldquo;Presentation Model\u0026rdquo; is the data representation of the \u0026ldquo;State\u0026rdquo; of the GUI.\nIn this interpretation, the Model is simply a POJO with the fields composed of Observable objects, suitable for binding to the various properties of the View elements. It\u0026rsquo;s just a place to gather all of the elements of State together in one object, so that it may be passed around as a single component.\nHowever, with that interpretation, I always felt that there was something missing - the piece that connects it to the back-end of the application. So I needed to add that on, and then build some rules about what goes where and who is responsible for what. The result is something I call Model-View-Controller-Interactor (MVCI) and I\u0026rsquo;ve found it to be ideal for use in a JavaFX Reactive implementation.\nNow, when I go back and look at how those other frameworks are really supposed to work, I can see how they have problems and how my new framework relates to them. And it\u0026rsquo;s similar in many ways, but it\u0026rsquo;s also something completely new and different. And, IMHO, it\u0026rsquo;s better.\nLet\u0026rsquo;s look at the components:\nThe Model # The Model is really just a POJO, and represents the \u0026ldquo;State\u0026rdquo; of the GUI. The fields are, for the most part, Observable objects. This means that they can be Properties, ObservableLists, Bindings or ObservableValues of some sort. In general, every element included in the Model should potentially have some direct relationship to some aspect of the View.\nOriginally, I was pretty stringent about following the JavaFX \u0026ldquo;Bean\u0026rdquo; structure for getters and setters, and having a getter for both the Observable itself and it\u0026rsquo;s contents. I\u0026rsquo;m not sure about the need for that any more, and currently I lean towards just having a getter for the Observables themselves. The cost for this is that code in the Interactor, which is the only place that getters and setters for the Observable contents is used, gets a little bigger as it needs to do a get() or set() on the Observables. It also exposes the Observable nature of the data in the Model to the Interactor, which might not be desirable.\nI really don\u0026rsquo;t think that relationships between the data elements in the Model should be established in the Model itself. These relationships generally reflect business rules of some sort, and that kind of logic belongs in the Interactor at all times. My guide has always been, \u0026ldquo;If you are looking for business logic, it should always be in one place - the Interactor.\u0026rdquo; This means that if you are looking to understand how two data elements are related, you don\u0026rsquo;t have to go hunting through the entire framework to find place where they are linked, it will be in the Interactor.\nThe View # The View handles every aspect of the User Interface and nothing else. Properties of the components of the View are bound to each other and to the Properties in the Model. In this manner, the Model now becomes an accurate representation of the State of the UI.\nIn Reactive JavaFX, the View layout should be static, but it should behave dynamically in response to changes in the Model. What does this mean? It means that all of the components of the layout are instantiated, configured, styled and composed when the GUI is built. As much as possible, favour controlling visibility over inserting/removing elements from the layout.\nLet\u0026rsquo;s take an example:\nSuppose you have an error message that\u0026rsquo;s going to be displayed on the screen via a particularly styled Label. Most likely, the error message isn\u0026rsquo;t going to be displayed when the View is first displayed. Put it in the layout anyways, configure and style it, then Bind its Visible and Managed properties to a BooleanProperty in the Model that indicates an error. Let\u0026rsquo;s say that the error message needs to change its content based on the nature of the error. Bind the Text property of the Label to elements in the Model that indicate the nature of the error.\nThe Controller # The Controller is the \u0026ldquo;core\u0026rdquo; of the framework. It is responsible for instantiating all of the other components and connecting them together. The Controller connects the MVCI construct to the rest of the application as it contains the only methods accessible by any object outside the MVCI construct. Typically, it will have a method with a name like getView() which will return the View which can then be placed inside another layout or Scene.\nThe Controller is responsible for handling all of the \u0026ldquo;actions\u0026rdquo; in the UI. This means that anything more active than simply updating the Model (through bindings in the View) is processed through the Controller. The Controller defines how these actions will be handled, and any threading that might be required. In practice, this means that the Controller will be responding to EventHandlers triggered from the View, and will establish any Listeners on the Model.\nThe Interactor # The Interactor contains all of the business logic of the framework. This is the class where both the Model and the domain objects co-exist:\nBusiness Constraints and Connections Between Model Elements Often there are connections between fields defined in the Model which are related to business rules. For instance, you might have a BooleanProperty that indicates that some combination of other Properties in the Model are invalid. This BooleanProperty would be bound by the Interactor to a Binding that it constructs. This keeps the business logic inside the Interactor. Connections to External APIs If there are going to be connections to external APIs, then they are going to be in the Interactor. Connections to Data Persistence Once again, if you are going to have a direct connection to a persistence layer, then they will be in the Interactor. Connections to Shared Application Libraries In any system with more than one screen, you are likely to have a fair amount of shared business logic or brokers that connect to APIs or persistence libraries. These libraries are likely to return and accept application domain objects. Business Logic for Actions This is probably the main function of an Interactor: To provide the methods that bring together State, domain data and business rules to make the framework actually do something. Let go back the the error message example from the \u0026ldquo;View\u0026rdquo; section above:\nLet\u0026rsquo;s say there\u0026rsquo;s a \u0026ldquo;Save\u0026rdquo; Button on the GUI and the user clicks it. Since the View properties are bound to the Model, the Interactor immediately has access to the View data through the Model. So \u0026ldquo;Save\u0026rdquo; just needs to trigger an action, not transfer any data to the Interactor. The Interactor has some code that checks the data in the Model, maybe does a database lookup and determines that the \u0026ldquo;Save\u0026rdquo; function cannot be run with the current data. So it sets an error flag in the Model to true and composes a StringProperty in the Model with a reason. All done.\nWhy is this Better? # In my opinion, it\u0026rsquo;s better because each component does one job, and that job matches with its name:\nView The View is solely and uniquely responsible for the UX. Nothing more. Controller The Controller is responsible for \u0026ldquo;controlling\u0026rdquo; everything. It\u0026rsquo;s sets it all up, and then acts as traffic controller for any actions that take place, defining how they will be handled and on what thread. This includes actions triggered by the GUI and through ChangeListeners on the elements of the Model. Model The Model is the sole repository of the \u0026ldquo;State\u0026rdquo; of the UI. It\u0026rsquo;s just data and has no logic. Since a reference to the Model is held by both the View and the Interactor, the Interactor can directly interact with the \u0026ldquo;State\u0026rdquo; of the UI without any intermediary. Interactor The Interactor is business logic element and, as such, is solely and uniquely responsible for interacting with the outside world (other than the UX). This structure makes it simple to understand. There\u0026rsquo;s never any doubt about what code goes where.\nIn a way this is very similar to the MVC with State split out into its own element and used Reactively. Now the Model from MVC minus State becomes the Interactor, and the View directly interacts with State to keep the View \u0026amp; Model (State) synchronized at all times. The Controller continues to handle actions from the View, but doesn\u0026rsquo;t contain any business logic and delegates that to the Interactor.\nA Simple MVCI Example # Let\u0026rsquo;s look at a simple example that shows how it all goes together, an application that queries an external API to retrieve weather information for a location.\nWhen it\u0026rsquo;s running, it looks like this:\nThe Controller # Here\u0026rsquo;s the Controller code:\npublic class WeatherController { private final WeatherInteractor interactor; private final WeatherViewBuilder viewBuilder; public WeatherController() { WeatherModel viewModel = new WeatherModel(); interactor = new WeatherInteractor(viewModel); viewBuilder = new WeatherViewBuilder(viewModel, this::fetchWeather); } private void fetchWeather(Runnable postFetchGuiStuff) { Task\u0026lt;Void\u0026gt; fetchTask = new Task\u0026lt;\u0026gt;() { @Override protected Void call() { interactor.checkWeather(); return null; } }; fetchTask.setOnSucceeded(evt -\u0026gt; { interactor.updateWeatherModel(); postFetchGuiStuff.run(); }); Thread fetchThread = new Thread(fetchTask); fetchThread.start(); } public Region getView() { return viewBuilder.build(); } } We can see in the constructor that the Interactor and the ViewBuilder are instantiated with the Model as a constructor dependency satisfied by the newly instantiated WeatherModel. In addition, viewBuilder is supplied a Consumer to handle the \u0026ldquo;Fetch Weather\u0026rdquo; action.\nThe \u0026ldquo;Fetch Weather\u0026rdquo; action requires connection to an external website and is therefore blocking in nature. That can\u0026rsquo;t happen on the FXAT, so the Controller creates a Task to handle the threading required.\nThis controller has exactly one public method other than its constructor, getView(). The method invokes the viewBuilder.build() method to return a Region.\nConnecting it to the Application # We can look at the Application class to see how the Controller supplies the connection from the MVCI framework to the rest of the JavaFX application. There\u0026rsquo;s nothing complex here, as this application just has the one screen, so we can instantiate the Controller and call its getView() method to populate the Scene:\npublic class WeatherMain extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { primaryStage.setScene(new Scene(new WeatherController().getView())); primaryStage.show(); } } Look at how simple this is. There is zero visibility into the specifics of what the MVC-I construct is going to do.\nThe Model # The Model is, as expected, just a POJO with a bunch of JavaFX property \u0026ldquo;beans\u0026rdquo; :\npublic class WeatherModel { private final StringProperty temperature = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty conditions = new SimpleStringProperty(\u0026#34;\u0026#34;); private final StringProperty city = new SimpleStringProperty(\u0026#34;Nice\u0026#34;); private final ObjectProperty\u0026lt;Image\u0026gt; icon = new SimpleObjectProperty\u0026lt;\u0026gt;(); private final ObservableList\u0026lt;String\u0026gt; cities = FXCollections.observableArrayList(); private final StringProperty units = new SimpleStringProperty(\u0026#34;metric\u0026#34;); public String getTemperature() { return temperature.get(); } public StringProperty temperatureProperty() { return temperature; } public void setTemperature(String temperature) { this.temperature.set(temperature); } . . . } I\u0026rsquo;ve trimmed out most the methods as it just repeats for each Property. That\u0026rsquo;s one of the strengths of this framework: The Model is almost always just boilerplate and 90% of the code is of no interest to anyone. You can look at those 6 lines of field declarations and you know everything. You don\u0026rsquo;t need to see the other 60 lines of code with the accessor methods.\nWhat we will see is that each of the elements in the Model has some direct relationship to some aspect of the GUI, which is why it is part of the Model.\nThe ViewBuilder # Here\u0026rsquo;s the ViewBuilder. The build() method returns a Region, which is actually a BorderPane under-the-hood. It only has the centre and bottom zones populated. The centre zone is comprised of a weather image and a GridPane with details. There are a VBox and an HBox to give some structure to the layout.\nThe bottom has a city selection ComboBox and a Button in an HBox. You can see how the onAction() method of the Button invokes the Consumer passed from Controller to fetch the weather, while disabling and enabling the Button at the right times.\npublic class WeatherViewBuilder implements Builder\u0026lt;Region\u0026gt; { private final WeatherModel viewModel; private final Consumer\u0026lt;Runnable\u0026gt; weatherFetcher; public WeatherViewBuilder(WeatherModel viewModel, Consumer\u0026lt;Runnable\u0026gt; weatherFetcher) { this.viewModel = viewModel; this.weatherFetcher = weatherFetcher; } @Override public Region build() { BorderPane results = new BorderPane(); results.setCenter(setUpCentre()); results.setBottom(setUpBottom(weatherFetcher)); results.getStylesheets().add(Objects.requireNonNull(getClass().getResource(\u0026#34;/css/default.css\u0026#34;)).toExternalForm()); results.getStyleClass().add(\u0026#34;weather-box\u0026#34;); return results; } private Node setUpCentre() { ImageView iconImageView = new ImageView(); iconImageView.imageProperty().bind(viewModel.iconProperty()); iconImageView.setFitWidth(200); TwoColumnGridPane gridPane = new TwoColumnGridPane(); gridPane.addTextRow(\u0026#34;Temperature:\u0026#34;, viewModel.temperatureProperty()); gridPane.addTextRow(\u0026#34;Conditions:\u0026#34;, viewModel.conditionsProperty()); gridPane.setMinHeight(160); gridPane.setMinWidth(200); VBox vBox = new VBox(5, TextWidgets.boundStyledText(viewModel.cityProperty(), \u0026#34;heading-text\u0026#34;), gridPane); HBox hBox = new HBox(10, iconImageView, vBox); vBox.getStyleClass().add(\u0026#34;info-box\u0026#34;); vBox.setPadding(new Insets(6)); return hBox; } private Node setUpBottom(Consumer\u0026lt;Runnable\u0026gt; fetchWeather) { Button button = new Button(\u0026#34;Get Weather\u0026#34;); button.setOnAction(evt -\u0026gt; { button.setDisable(true); fetchWeather.accept(() -\u0026gt; { button.setDisable(false); }); }); ComboBox\u0026lt;String\u0026gt; cityCB = new ComboBox\u0026lt;\u0026gt;(); cityCB.getItems().setAll(viewModel.getCities()); viewModel.cityProperty().bind(cityCB.valueProperty()); ToggleButton unitsToggle = new ToggleButton(); return new HBox(10, cityCB, button); } } The Interactor # Now, let\u0026rsquo;s look at the Interactor:\npublic class WeatherInteractor { private final WeatherModel viewModel; private final WeatherFetcher weatherfetcher = new WeatherFetcher(); private WeatherData weatherData; public WeatherInteractor(WeatherModel viewModel) { this.viewModel = viewModel; viewModel.setCities(Arrays.asList(\u0026#34;London\u0026#34;, \u0026#34;Paris\u0026#34;, \u0026#34;Nice\u0026#34;, \u0026#34;Hong Kong\u0026#34;, \u0026#34;New York\u0026#34;, \u0026#34;Las Vegas\u0026#34;)); } public void checkWeather() { weatherData = weatherfetcher.checkWeather(viewModel.getCity()); } public void updateWeatherModel() { viewModel.setTemperature(weatherData.getTemperature()); viewModel.setConditions(weatherData.getConditions()); viewModel.setIcon(weatherData.getWeatherImage()); } } The constructor holds any initialization of the Model that\u0026rsquo;s required. In this case, it\u0026rsquo;s the list of cities that weather can be checked for. It\u0026rsquo;s clear that this list is absolutely \u0026ldquo;business logic\u0026rdquo; for this application, and the responsibility for initializing it belongs here, in the Interactor.\nThe two public methods are involved in the \u0026ldquo;Fetch Weather\u0026rdquo; action, and are called from the Controller via its Task. The first method, checkWeather() is intended to be called from a background thread. The second is intended to be called on the FXAT, and therefore, can update the Model.\nWe\u0026rsquo;re using an internal library, WeatherFetcher, as a \u0026ldquo;broker\u0026rdquo; or \u0026ldquo;service\u0026rdquo; to query the web to get the weather. This broker returns WeatherData, which is a \u0026ldquo;domain\u0026rdquo; object. If this was a bigger application with lots of different functions and screens, then there would be a entire application layer with brokers and utilities to perform functions and apply business logic shared across the entire application. But here we just have a single broker, which we\u0026rsquo;re not going to look into at all since it\u0026rsquo;s not relevant to the MVCI framework.\nThat\u0026rsquo;s an important point. WeatherFetcher could be horribly complex, with thousands of web calls and millions of lines of code. And WeatherData could have hundreds and hundreds of fields with forecasts, historical data, wind speeds, barometric pressures and so on. Most of this is irrelevant to our application, which is just to fetch the current temperature and overall conditions for a handful of specific cities. So, while the code in the WeatherFetcher could be considered to be \u0026ldquo;business logic\u0026rdquo;, it\u0026rsquo;s not business logic related specifically to this application, so it doesn\u0026rsquo;t belong in the Interactor. Which is why it\u0026rsquo;s in a separate service, and we don\u0026rsquo;t even have the code for it included in this example. What we do have is the business logic for calling WeatherFetcher for our cities, and extracting the data that we do need for our application from WeatherData.\nLastly, you can see how the domain object is stored in the Interactor as a field. This way it can be populated on the background thread and then accessed on the FXAT. Obviously, with two threads accessing the same data, there are concurrency issues that might need to be addressed. In this particular example, there is no way that the we can have problems because the structure around the action prevents multiple concurrent Button clicks.\nThe Value in the Framework # Now we have a concrete example, let\u0026rsquo;s look at how this framework adds value to the application.\nIt really just does one important thing: It reduces coupling.\nHow much coupling do we have? # The View has no public methods other than those inherited from Region. Public methods are a primary indication of coupling. Here we have absolutely zero public methods specific to the View, so there\u0026rsquo;s no coupling at all in this manner. This means that there are no dependencies on the View! The View\u0026rsquo;s only dependencies are declared in its constructor. The View is dependant on the Model and the Consumer passed to it from the Controller. The View has no knowledge of the existence of either the Controller or Interactor. The Interactor\u0026rsquo;s only dependency is on the Model. The Interactor has no knowledge of the View or the Controller. It has dependencies on the structure of the Model, but that\u0026rsquo;s it. The Controller has dependencies on the Interactor. Since the Interactor has two public methods that the Controller needs to call, there are dependencies there that the Interactor needs to satisfy. The Controller might have dependencies on the Model. While this example doesn\u0026rsquo;t have any Controller dependencies on the Model, it\u0026rsquo;s possible that it could. For instance, we might put a ChangeListener on some value in the Model. The View has no coupling on the data in the Model, Just its structure. In this example, the List of cities is populated by the Interactor, and then loaded into the ComboBox in the View. The View doesn\u0026rsquo;t have any logic dependant on the contents of that List. If we wanted to create such a dependency, we\u0026rsquo;d have to replace the List with something like an Enum, so that the possible values are now part of the structure of the data, not it\u0026rsquo;s contents. Obviously, in any non-trivial system there has to be at least some level of coupling if the system is going to be functional. In MVCI the Model is the key component of coupling, as all three of the other components of the framework are dependent on it.\nSwapping Out Components # I find that a mental exercise that\u0026rsquo;s helpful to understand the impact of coupling is to imagine replacing the View or the Interactor with something fantastically different, just to see what the impact would be on the system.\nWhat if we replaced the View from a simple screen to a \u0026ldquo;Weather Experiencing Chamber?\u0026rdquo; Imagine you walk into a room and there are a bunch of buttons on the wall, one for each city, and when you hit one it then changes the temperature in the room and blows air, or showers water on you, or turns on a fog machine. Things like that.\nWhat would you need to change in the rest of the system to make it work? Really, nothing. The UX is completely divorced, decoupled if you like, from the mechanics of fetching the data. You still need a list of cites, and you still need to update the Model with the selected city and trigger the action. And you still need to respond to the changes in the Model that hold the weather information.\nNow, let\u0026rsquo;s look at it the other way around. What if we now have a \u0026ldquo;Weather Setting\u0026rdquo; system, instead of a \u0026ldquo;Weather Fetching\u0026rdquo; system. Pick a city, set the temperature and click a Button. Now our system of weather controlling satellites changes the temperature in Paris. In this case, we\u0026rsquo;ll need to change the GUI so that the temperature is an input, not an output. The Interactor can be changed with a new Interactor that has an interface to our satellite network that controls the weather. The Model does not need to change, nor does the Controller. The change to the GUI is minimal, and isn\u0026rsquo;t particularly tied to the functionality contained in the Interactor.\nThis is Scalable # There\u0026rsquo;s nothing that says that the Region returned by the Controller\u0026rsquo;s getView() method has to be the root of a Scene created by Application.start(). It\u0026rsquo;s independent, so you could put it into some other layout. You can build complete \u0026ldquo;View\u0026ndash;\u0026gt;Database\u0026rdquo; components that you can implement anywhere. From a Menu, in a Dialog, or as an element of any layout.\nIt\u0026rsquo;s also possible to create an MVCI with external dependencies by making them constructor parameters of the Controller. Let\u0026rsquo;s say you have a sub-screen that will become a Region in a larger layout. There\u0026rsquo;s a subset of the larger screen\u0026rsquo;s Model that applies to this sub-screen. Pass this subset of the Model to the Controller of the sub-screen in its constructor, and then use that to initialize the sub-screen\u0026rsquo;s Model.\nIn the same way, you can also make functional dependencies in the sub-screen. Let\u0026rsquo;s say that this sub-screen is the piece that has the \u0026ldquo;Save\u0026rdquo; Button. Create a constructor parameter in the sub-screen\u0026rsquo;s Controller to accept a Runnable from parent screen. Depending on the nature of the sub-screen, it may be possible to create it without an Interactor, delegating those operations to the parent screen\u0026rsquo;s Interactor.\nConclusion # In my opinion, MVCI is much more intuitive and logical structure for creating Reactive JavaFX applications than any of the three frameworks generally in use. The \u0026ldquo;Model\u0026rdquo; is now data-centric, as everyone expects, and the business logic and back-end interaction is handled by something with an appropriate name, \u0026ldquo;Interactor\u0026rdquo;.\nMy experience using this framework is that it is a highly effective approach for any Reactive application. It\u0026rsquo;s easy to envision a large system with many screens, menus and functions as a network of MVCI constructs, all connected to each other via their Controllers. At the back-end, they can all share a common library of Brokers, DAO\u0026rsquo;s and Services via their Interactors.\nAt the very least, MVCI takes one of the most common misinterpretations of MVC and supplies the missing component to turn it into a complete framework that achieves the goal of MVC.\n","date":"30 August 2022","externalUrl":null,"permalink":"/javafx/techniques/mvci/other-frameworks/","section":"JavaFX","summary":"Taking a look at the three most common design patterns for building systems with user interfaces.  How are they different?  Which one is best?  Is there anything better?","title":"Unravelling MVC, MVP and MVVM","type":"javafx"},{"content":" Why? # Most of the tutorial articles I\u0026rsquo;ve written here have been of the \u0026ldquo;deep dive\u0026rdquo; nature. I try to go way beyond the information available in the JavaDocs, and to give some insight into how to get the most out of the tools that JavaFX provides. These articles tend to be on the long side, Jekyll tells me that they average about 13 minutes of reading, and there\u0026rsquo;s usually lots of code examples and pontification about clean coding and on and on.\nI realize that these are going to be pretty advanced for someone who\u0026rsquo;s questions are about how to get that first screen up and running, or how to swap scenes with FXML.\nI\u0026rsquo;ve also had the honour to help some students on Reddit with their assignments lately. While I\u0026rsquo;ve tried to be very careful about not doing their homework for them, I\u0026rsquo;ve tried to send them in the right direction to build good projects. I\u0026rsquo;ve been surprised at how many times I\u0026rsquo;ve been able to say, \u0026ldquo;Go and read this article that I wrote about this topic, it should tell you what you need to know\u0026rdquo;.\nBut, like I said, these tend to be more advanced articles with maybe more information than a student needs to read. I think it would be nice to have something aimed at people that don\u0026rsquo;t need the \u0026ldquo;deep dive\u0026rdquo; articles, just some basic information to get them started.\nSo I\u0026rsquo;ve been working for some time now on a series I\u0026rsquo;m calling, \u0026ldquo;The Absolute Beginners Guide to JavaFX\u0026rdquo;. It\u0026rsquo;s all about how to build Reactive applications in JavaFX. Even if you\u0026rsquo;re not a beginner, you may find it of interest.\nYou can find it here: https://www.pragmaticcoding.ca/beginners/intro\nWhat\u0026rsquo;s in it? # This is a huge content drop for me, and it\u0026rsquo;s kept me busy for a long time. It\u0026rsquo;s also an ongoing project, so there\u0026rsquo;s more to come. So far, it\u0026rsquo;s divided up into two parts:\nAn introductory application.Pretty much \u0026ldquo;Hello World\u0026rdquo; in JavaFX. Then we add some user interaction and some styling. Just enough information to get a beginner started in the right direction.\nA CRUD application.This is a step-by-step development of a basic Create-Retrieve-Update-Delete application for a customer database. The database is simulated as a simple Java class, and allows the whole application to work just like it would in real life.\nAt this point, I\u0026rsquo;ve completed the development of the CRUD application up to the point where it\u0026rsquo;s a completely functional \u0026ldquo;Create\u0026rdquo; application for account number and name. Complete means that it has validation in the GUI, background processing for the save function and handles exceptions from the database. It really is complete, just doesn\u0026rsquo;t have a lot of fields on the screen.\nProgramming Approach # First and foremost, this series is about writing Reactive JavaFX applications. So yes, we\u0026rsquo;ll be talking about HBoxes, and TextFields and Labels and all that good stuff, but the focus is on application architecture and Reactive programming techniques.\nI should also point out that the content here, while primarily aimed at showing JavaFX concepts, is also aimed at showing good programming techniques, and how to constantly apply established principles to create clean code.\nWhen will there be more? # I was concerned about finding the right time to publish the parts that have been completed. A series like this has to have enough content to be of use, but it\u0026rsquo;s pretty big, so you don\u0026rsquo;t want to wait until it\u0026rsquo;s all done before publishing it.\nWhen you do publish, there\u0026rsquo;s an implied promise to supply more in a reasonable time frame. Because you don\u0026rsquo;t want to leave people hanging half way through a project. So I\u0026rsquo;m going to try to keep putting up more lessons on a regular basis.\nAs it is, it stops at a logic place, with the completion of the \u0026ldquo;Create\u0026rdquo; section, albeit with a limited number of fields and something less than a stellar look and feel. In the future, I\u0026rsquo;ll publish each part as soon as it is completed which will keep a steady stream of new content, but it might end with some \u0026ldquo;cliff-hangers\u0026rdquo; as it goes on.\nWhat Will Be in the Next Lessons? # The next steps will be to add some more fields, work on some more sophisticated formatting for the GUI and then to add in the \u0026ldquo;Retrieve\u0026rdquo; function.\nThe Companion Project # There\u0026rsquo;s also a companion project on GitHub with all of the code for every single article. So you don\u0026rsquo;t have to do anything more than download the project to try things out for yourself.\nI should also point out that the content here, while primarily aimed at showing JavaFX concepts, is also aimed at showing good programming techniques, and how to constantly apply established principles to create clean code.\n","date":"5 July 2022","externalUrl":null,"permalink":"/javafx/beginners/2022-07-05-introducing_beginners/","section":"JavaFX","summary":"Why? # Most of the tutorial articles I’ve written here have been of the “deep dive” nature. I try to go way beyond the information available in the JavaDocs, and to give some insight into how to get the most out of the tools that JavaFX provides. These articles tend to be on the long side, Jekyll tells me that they average about 13 minutes of reading, and there’s usually lots of code examples and pontification about clean coding and on and on.\n","title":"Introducing the Absolute Beginners Guide to JavaFX","type":"javafx"},{"content":" A Short Introduction # This is a guide to help you learn how to build JavaFX applications the right way. It\u0026rsquo;s not a reference guide, but an instructional guide intended to be read in order. Depending on your learning style, it\u0026rsquo;s probably a good idea to follow along with the guide in your IDE, trying out the examples and experimenting with some of the concepts along the way.\nI\u0026rsquo;ve tried to keep each part short and easily digestible, so that you can stop at the end of any part and pick later from the next part.\nMy hope is that you fully understand the content of each part before moving on to the next. If you are confused about anything you can usually research in on the web, or find it in the JavaDocs for the components involved. If you are really stuck, you can send me an email and I\u0026rsquo;ll help. In any event, each part of this guide is written with the assumption that the content that preceded it has been fully understood.\nWhat We\u0026rsquo;ll Build # To start with, we\u0026rsquo;ll build a \u0026ldquo;throw-away\u0026rdquo; application to learn some basic concepts. This will start out as a simple \u0026ldquo;Hello World\u0026rdquo; application that we\u0026rsquo;ll add some styling and user interaction to. Then we\u0026rsquo;ll put it aside and build a real application, a \u0026ldquo;Create, Retrieve, Update and Delete\u0026rdquo; (CRUD) application for a simulated customer database.\nJavaFX as a Reactive Framework # This tutorial series concentrates on using JavaFX as a \u0026ldquo;Reactive\u0026rdquo; GUI framework.\nWhat is a Reactive framework?\nFrom WikiPedia:\nIn computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change\u0026hellip;\nFor example, in an imperative programming setting, a := b + c would mean that a is being assigned the result of b + c in the instant the expression is evaluated, and later, the values of b and c can be changed with no effect on the value of a. On the other hand, in reactive programming, the value of a is automatically updated whenever the values of b or c change, without the program having to explicitly re-execute the statement a := b + c to determine the presently assigned value of a.\nOkay, but what does this get you?\nIn a word, simplicity.\nNow your GUI is designed purely with the intent to reflect State at any given time in the best possible way. There is no code anywhere in your GUI that concerns itself with application logic or business rules or any of that.\nAt the other end, the business logic is concerned simply with maintaining and reacting to State without any idea of how it impacts the GUI.\nIt\u0026rsquo;s much easier to see it in practice than to describe it. In this series, we\u0026rsquo;ll start right from the beginning utilizing the tools and features in JavaFX that allow Reactive designs to be implemented. You\u0026rsquo;ll see that it\u0026rsquo;s not complicated, and a natural way to build an application with JavaFX.\nCompanion Project # In order to make it easier to follow along at home, all of the source code from this series has been included in a companion project available on GitHub\u0026hellip;\nGo To the Companion Project\nThe source code is divided up into packages, each package containing the code as of the end the part of the series to which it corresponds. So the package ca.pragmaticcoding.beginners.part2 contains the code as described in part2 of this series.\nIn this way, you can see the whole application at each stage of its evolution, you can run it for yourself and see how it works. You can change code. You can break code. You can try your own ideas and experiment as much as you like.\nOn To It! # Let\u0026rsquo;s get started!\n","date":"5 July 2022","externalUrl":null,"permalink":"/beginners/intro/","section":"JavaFX","summary":"Introducing a course for programmers starting out to learn JavaFX","title":"The Absolute Beginners Guide to Reactive JavaFX","type":"javafx"},{"content":" Introduction # I don\u0026rsquo;t really understand where the mania to swap Scenes comes from, but I see tons of posts, videos and questions about how to swap Scenes in JavaFX. Most of the time, it\u0026rsquo;s in the context of FXML, and I suspect that this is because FXML makes it harder to do something more sensible than swap Scenes in a Stage.\nThe Challenge with Swapping Scenes # The \u0026ldquo;hard\u0026rdquo; part about swapping Scenes is that it needs to be done via Stage.setScene().\nWhy is this hard?\nIt\u0026rsquo;s hard because Stage is passed to the start() method in your Application class. If you\u0026rsquo;re making even a half-hearted attempt to organize your application properly, then all of the code that initiates the Scene swap is going to be somewhere else. This means that you somehow have to find a way to have a reference to your Stage somewhere in your layout code.\nThe most obvious (but horribly bad) way to do this is something like this:\npublic class Layout1Builder implements Builder\u0026lt;Region\u0026gt; { @Override public Region build() { Button button = new Button(\u0026#34;Change to Scene 2\u0026#34;); button.setOnAction(evt -\u0026gt; { Scene newScene = new Scene(new Layout2Builder.build()); button.getScene().getWindow().setScene(newScene)); }); VBox results = new VBox(20, new Label(\u0026#34;Welcome to Scene 1\u0026#34;), button); results.setPadding(new Insets(50)); return results; } } Here we call button.getScene().getWindow() which will give you a Window which is the direct ancestor to Stage and which has the setScene() method. This will work, but it breaks the first of the two cardinal rules of JavaFX design:\nDon\u0026rsquo;t reach up into a parent. Don\u0026rsquo;t peak inside a child. As soon as you break either of these rules, you start to cross over into coupling hell. Any separation that you tried to create by organizing your application into separate classes and packages and whatever is now compromised and you might as well have not bothered.\nSo just don\u0026rsquo;t do it.\nThe other problem is that your layout code needs to know about whatever is going to replace it in the Stage. So now we have yet another dependency. This one is because Scene 1\u0026rsquo;s layout code needs to have a reference to another Scene, in order to put it into the Stage. Same thing holds with that other Scene if you want to go back. Almost by definition, these two Scenes have no actual functional relationship to each other since they can\u0026rsquo;t co-exist on the Stage. Yet they now are dependent on each other because of the Scene swapping.\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content=\u0026ldquo;This dependency is just dead weight. It\u0026rsquo;s coupling with no real value that you have to cope with in your code forever.\u0026rdquo; %}\nSo just don\u0026rsquo;t do it.\nHow To Overcome This Challenge # The big problem is that you are making the handling of your Stage dependent on your layout. You need to eliminate that.\nThe best way to do that is a technique called \u0026ldquo;Dependency Inversion\u0026rdquo;, which is going to make the layout dependent on the Application class (the reverse of how it started). Then we\u0026rsquo;re going to make that dependency palatable by using \u0026ldquo;Constructor Dependency Injection\u0026rdquo;.\nWhat these fancy terms mean is that we are going to create a constructor parameter in the layout class which is going to require a functional element that will handle whatever work required to swap Scenes. This functional element can be as simple as a Runnable, and if not that, then a Consumer.\nThe result of this is that the layout is now dependent on some functionality being passed from Application, but that dependency is clearly advertised as a constructor parameter to the layout. So the layout is saying, \u0026ldquo;If you want to use me, you have to supply a Runnable that does something useful.\u0026rdquo; And we name that constructor parameter in some meaningful way so that everyone understands what it\u0026rsquo;s intended to do.\nNow we\u0026rsquo;ve eliminated the dependency of the Application class on the layout classes.\nYou might be saying to yourself, \u0026ldquo;Yes, but the Application is still dependent on the layouts, because that is what triggers the Scene swap\u0026rdquo;.\nThat\u0026rsquo;s not quite true, though.\nFor sure, there needs to be something in the layout that allows the user to trigger the scene swap. But that\u0026rsquo;s a UX (User Experience) dependency, not a programming dependency. Of course, all the requirements of the UX need to be satisfied by the layout, and only the layout, because that\u0026rsquo;s what it\u0026rsquo;s there for. But that dependency is between the layout and the User, and doesn\u0026rsquo;t have anything to do with the relationship between the layout and the rest of the application.\nAn Implementation # We\u0026rsquo;re going to look at how you might implement an approach that does this dependency inversion and constructor dependency injection to allow us to swap between two Scenes by clicking on a Button in each Scene.\nFirst, let\u0026rsquo;s take a look at the Application class:\npublic class SceneSwapDemo extends Application { @Override public void start(Stage primaryStage) throws Exception { final Scene scene1 = new Scene(new VBox()); final Scene scene2 = new Scene(new VBox()); scene1.setRoot(new Layout1Builder(() -\u0026gt; primaryStage.setScene(scene2)).build()); scene2.setRoot(new Layout2Builder(() -\u0026gt; primaryStage.setScene(scene1)).build()); primaryStage.setScene(scene1); primaryStage.show(); } } Nothing complicated there. We need to initialize the Scenes independent of their content at first, since the two Builder classes need a Runnable that references the Scenes in order to create the content. So we get an extra two lines of code.\nThe two Runnables are also very simple. Each one just loads one of the two Scenes that we\u0026rsquo;ve created into the Stage. Scene 1 loads Scene 2, and Scene 2 loads Scene 1.\nNow let\u0026rsquo;s look at the layout builder for Scene 1:\npublic class Layout1Builder implements Builder\u0026lt;Region\u0026gt; { private final Runnable sceneSwapper; public Layout1Builder(Runnable sceneSwapper) { this.sceneSwapper = sceneSwapper; } @Override public Region build() { Button button = new Button(\u0026#34;Change to Scene 2\u0026#34;); button.setOnAction(evt -\u0026gt; sceneSwapper.run()); VBox results = new VBox(20, new Label(\u0026#34;Welcome to Scene 1\u0026#34;), button); results.setPadding(new Insets(50)); return results; } } We are using a Builder here because we\u0026rsquo;re just doing configuration and layout for the VBox that goes into the Scene. We\u0026rsquo;re not adding new functionality to Region (which really means adding new public methods), so there\u0026rsquo;s no reason to extend Region.\nThe value returned from build() is just a VBox with lots of padding and with a Label and a Button in it. The Button is configured to invoke the Runnable passed to the Builder in its constructor.\nand, finally, the layout builder for Scene 2, which should be pretty familiar:\npublic class Layout2Builder implements Builder\u0026lt;Region\u0026gt; { private final Runnable sceneSwapper; public Layout2Builder(Runnable sceneSwapper) { this.sceneSwapper = sceneSwapper; } @Override public Region build() { Button button = new Button(\u0026#34;Change to Scene 1\u0026#34;); button.setOnAction(evt -\u0026gt; sceneSwapper.run()); VBox results = new VBox(20, new Label(\u0026#34;Welcome to Scene 2\u0026#34;), button); results.setPadding(new Insets(50)); return results; } } It starts out looking like this:\nAnd then looks like this when the Button has been clicked:\nImportant Things to Note About This Approach # The first thing to note is that SceneSwapDemo has no knowledge about the workings of contents of either scene1 or scene2. It knows that it needs to provide a Runnable to the builders, and that Runnable should swap the Scenes. But it doesn\u0026rsquo;t know how that Runnable will be used, when it will be used, or how it will be triggered.\nThe sole dependencies are the constructor parameters that are required for the two layout builders.\nOn the other hand, the actual layouts in the Scenes have no knowledge of, and therefore no dependency on, the functionality of the Runnables that they invoke. Nor do they know how the Runnables work nor what they do, nor does it matter to them.\nNeither layout, and neither layout builder, has any knowledge of the existence of - let alone the content of - the other layout. Certainly, no dependency on it.\nThis entire thing is almost ridiculously simple.\nBetter Than Swapping Scenes # I\u0026rsquo;m not a fan of swapping Scenes. The only place I\u0026rsquo;ve ever really seen a need for it is when you have a Login screen. In that particular case, the initial Scene is really just a stop along the way to getting the application up and running and you might as well throw it away when you\u0026rsquo;re done with it.\nGenerally speaking though, you\u0026rsquo;re better off swapping out the root of a Scene, or even better yet, swapping out some element of your layout. This is especially true when your application\u0026rsquo;s screen has some kind of a framework with a global menu, or other things along that line. You can keep those global elements and then swap out selected content. Let\u0026rsquo;s look at some approaches.\nFor myself, I tend to look at both Stage and Scene as application framework and not an active part of the content.\nSwapping Out the Scene Root # The first thing we\u0026rsquo;re going to look at is just having a single Scene and using Scene.setRoot() to change the content of the Scene.\npublic class SceneSwapDemo2 extends Application { private Region root2; @Override public void start(Stage primaryStage) throws Exception { final Scene scene = new Scene(new VBox()); Region root1 = new Layout1Builder(() -\u0026gt; scene.setRoot(root2)).build(); root2 = new Layout2Builder(() -\u0026gt; scene.setRoot(root1)).build(); scene.setRoot(root1); primaryStage.setScene(scene); primaryStage.show(); } } This looks exactly the same as the scene swapping version when it runs.\nDue to the cross-referencing between the two layouts, root2 needs to be declared first before they can be initialized. Using a field feels a bit kludgey, but in a real application you\u0026rsquo;d probably want to get the layout swapping stuff out of start() anyways, and a cleaner way could be implemented.\nSwapping Out a Layout Component # Now we get to something which is probably closer to the way that you\u0026rsquo;d approach this in a real application: Treating the two layouts as custom components which are dynamically swapped inside of a larger layout. For this, we\u0026rsquo;ll need a wrapper layout:\npublic class WrapperLayoutBuilder implements Builder\u0026lt;Region\u0026gt; { Region customComponent2; @Override public Region build() { BorderPane results = new BorderPane(); results.setTop(new Label(\u0026#34;This is The Wrapper\u0026#34;)); Region customComponent1 = new Layout1Builder(() -\u0026gt; results.setCenter(customComponent2)).build(); customComponent2 = new Layout2Builder(() -\u0026gt; results.setCenter(customComponent1)).build(); results.setCenter(customComponent1); return results; } } You\u0026rsquo;ll notice that this is very similar to the root swapper code from the last example. Instead of using Scene.setRoot(), we\u0026rsquo;re using BorderPane.setCenter(). But the logic is the same.\nNow the Application code becomes 100% generic, and trivial, which is nice:\npublic class SceneSwapDemo3 extends Application { @Override public void start(Stage primaryStage) throws Exception { primaryStage.setScene(new Scene(new WrapperLayoutBuilder().build())); primaryStage.show(); } } When it runs, it looks like this:\nOne the button has been clicked once, it looks like this:\nUsing a TabPane # Let\u0026rsquo;s look at doing something different than swapping out elements, let\u0026rsquo;s create connections between components in a layout. Here, we\u0026rsquo;ll put the layouts into a TabPane, and then let the Buttons select the Tabs:\npublic class TabPaneLayoutBuilder implements Builder\u0026lt;Region\u0026gt; { @Override public Region build() { TabPane tabPane = new TabPane(); Tab tab1 = new Tab(\u0026#34;Tab 1\u0026#34;); Tab tab2 = new Tab(\u0026#34;Tab 2\u0026#34;); tabPane.getTabs().addAll(tab1, tab2); tab1.setContent(new Layout1Builder(() -\u0026gt; tabPane.getSelectionModel().select(tab2)).build()); tab2.setContent(new Layout2Builder(() -\u0026gt; tabPane.getSelectionModel().select(tab1)).build()); return tabPane; } } This example might seem a little bit goofy. Why would you have a Button inside Tab content swap to another Tab? Surely users can just click on the Tab? Sometimes you have a workflow, and while you might want users to be able to move around in a freeform manner using the Tabs, you might also have something like a \u0026ldquo;Next\u0026rdquo; Button that walks users through the workflow.\nHere the logic is a little bit simpler since the Button actions reference the TabPane and not the contents of the Tabs. This means there\u0026rsquo;s no issues around referencing variables that haven\u0026rsquo;t been initialized in a lambda. Otherwise it\u0026rsquo;s essentially the same as the BorderPane example in how it works.\nThe Application class is the same as the previous example except it uses TabPaneLayoutBuilder.\nIt looks like this when it\u0026rsquo;s running:\nManipulating Visibility # Personally, I prefer not to manipulate layouts - actually changing the content of layout - after they\u0026rsquo;ve been built. I think it\u0026rsquo;s better to have everything on the screen and control the look using the Visible and Managed properties of the Nodes. So let\u0026rsquo;s take a look at that:\npublic class VisibilityLayoutBuilder implements Builder\u0026lt;Region\u0026gt; { Region customComponent2; @Override public Region build() { BorderPane results = new BorderPane(); results.setTop(new Label(\u0026#34;This is The Wrapper\u0026#34;)); BooleanProperty vBox1Visible = new SimpleBooleanProperty(true); Region component1 = new Layout1Builder(() -\u0026gt; vBox1Visible.set(false)).build(); Region component2 = new Layout2Builder(() -\u0026gt; vBox1Visible.set(true)).build(); component1.visibleProperty().bind(vBox1Visible); component2.visibleProperty().bind(vBox1Visible.not()); results.setCenter(new StackPane(component1, component2)); return results; } } And when it runs, it looks like this:\nHere we don\u0026rsquo;t need have any cross-referencing between the components as Buttons will now manipulate the BooleanProperty which controls the visibility of the two components. With just two components to swap, it\u0026rsquo;s easy to use a BooleanProperty to control the visibility, but you\u0026rsquo;ll need something more elaborate to control 3 or more components - but the idea remains the same.\nAnother aspect of this that I find really appealing is that it\u0026rsquo;s moving much closer to a \u0026ldquo;Reactive\u0026rdquo; design. There\u0026rsquo;s very little empirical code, just the Runnables, and even those simply update the Model (in this case just the BooleanProperty). The UI changes are due to the bindings between the Model and View elements.\nAnother Look at Coupling # We\u0026rsquo;ve just looked at how to swap Scenes and 4 alternatives to swapping Scenes.\nDid you notice that we never made any modifications to Layout1Builder and Layout2Builder after they were written for the Scene swap scenario?\nWhat does that tell us? It tells us that the coupling between the two layouts and the infrastructure that holds them is minimal.\nLook at what we just did. We started out with one approach and then changed it up radically. And then we changed it up radically again. And again\u0026hellip;\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content=\u0026ldquo;At no time did we have to go back to the layouts and change them due to a radical redesign of our application.\u0026rdquo; %}\nThink about what this means for your applications.\nHow many times have you needed to change something - maybe something not so radical - and those changes have rippled through your entire application leading to massive testing and all kinds of new bugs and issues to figure out? That\u0026rsquo;s what coupling does to you.\nBut here we\u0026rsquo;ve avoided all that, simply because we\u0026rsquo;ve isolated the content from the mechanics and the logic of the application by limiting coupling.\nDoing This With FXML # My belief is that the only way you can do this in pure FXML is via the Stage.setScene() or the Scene.setRoot() methods. To do this, you need to create your layout as a \u0026ldquo;Custom Control\u0026rdquo;, as documented at the bottom of this page. This allows you to define a constructor with dependency injection. Now you can instantiate your custom controls with Runnable to swap scenes and you can place each of them as the \u0026ldquo;root\u0026rdquo; of a Scene.\nWhat you cannot do is any of the other, purely layout, techniques with the wrapper layouts defined in FXML - as far as I can tell. This is because you cannot pass constructor parameters to your custom controls through an FXML file.\nI could be wrong about this. I try hard to know as little about FXML as possible. There might be some way to implement custom builders or some other complicated silliness to do the dependency injection, but you probably wouldn\u0026rsquo;t be reading this tutorial if you\u0026rsquo;d already mastered those techniques.\nConclusion # We\u0026rsquo;ve looked at how to swap Scenes in a Stage without having to look up from the layout code into the Stage to mess with its workings. Then we looked at a number of alternate approaches to dynamically changing the content on the screen without swapping \u0026lsquo;Scenes\u0026rsquo;.\nIn the course of doing this, we saw how to use Dependency Inversion and Constructor Dependency Injection to split the mechanics of content swapping away from the layout code and dramatically reduce the coupling in the application.\n{% include notice type=\u0026ldquo;primary\u0026rdquo; content=\u0026ldquo;We created screen layouts that are decoupled from the context in which they are going to be used.\u0026rdquo; %}\nThis gives us immense freedom to use those layouts in a variety of situations without needing to do any customization to the layouts to accommodate the context in which they are used.\n","date":"24 May 2022","externalUrl":null,"permalink":"/javafx/techniques/swap-scenes/","section":"JavaFX","summary":"Taking a look at the three most common design patterns for building systems with user interfaces.  How are they different?  which one is best?  Is there anything better?","title":"How to Swap Scenes Properly","type":"javafx"},{"content":" The JavaFX Layout Techniques You Need to Master # Understanding Nodes and Properties and StyleSheets is one thing, but there are also techniques that you\u0026rsquo;ll need to learn in order to create JavaFX applications.\n","date":"24 May 2022","externalUrl":null,"permalink":"/javafx/techniques/layouts/","section":"JavaFX","summary":"These are the essential layout techniques you need to learn with JavaFX.","title":"Layout Techniques","type":"javafx"},{"content":" Introduction # If you\u0026rsquo;re old enough and you live in North America, you probably remember Ronco and their late night \u0026ldquo;Infomercials\u0026rdquo;. One of the most memorable of these was for their \u0026ldquo;Showtime Rotisserie\u0026rdquo;. Take a look, and make note of the catch phrase:\n\"Put the chicken in the oven, follow the instructions, and... SET IT AND FORGET IT!\" But what does this have to do with programming in JavaFX?\nMore than you\u0026rsquo;d think, because this is exactly the process you should follow when you\u0026rsquo;re creating a screen and the individual elements that make up a screen:\nCreate the widget Configure the widget Bind the widget SET IT in the layout AND FORGET IT! How Does This Work? # The most important idea here is that \u0026ldquo;State\u0026rdquo; and \u0026ldquo;View\u0026rdquo; are not the same thing. In fact, they are quite different:\nState State is the data related to a UI. Generally it is going to be reflected in the information shown on the screen, or the presentation of that data on the screen. In JavaFX, state is usually held in Observable objects of some sort. View The View is the visual presentation on the screen, and the component of your system that the user will directly interact with. In JavaFX, the components that make up the View are generally bound to components of State. In this way, changes to the State will be immediately reflected in the View, and changes made in the View are immediately reflected in State. Doing this allows the rest of your application to access, modify and react to changes in State without any dependency on the nature of the View.\nIn practice, it looks like this:\nWhy is This Important? # Coupling.\nIt\u0026rsquo;s all about coupling or, rather, not coupling.\nA View can be a complicated thing. There are all sorts of visual and user experience factors that need to be dealt with and potentially you might need to account for accessibility issues or different client devices. Putting code in there that calls out to business logic, or providing hooks to allow external classes to update data in the View directly quickly builds up the complexity of your View and clutters it with stuff that\u0026rsquo;s not, strictly speaking, \u0026ldquo;View\u0026rdquo;. Not to mention that you\u0026rsquo;re going to need to write some code to get the data out of your View and expose it to external classes.\nBut, as you can see from the diagram above, the View and the Business Logic are ignorant of each other, so neither one can become bogged down with the complexities of the other. And changes to one will not require changes to the other.\nJavaFX provides an incredibly complete toolkit to get rid of all of that complexity. It\u0026rsquo;s called \u0026ldquo;Binding\u0026rdquo;.\nWith Binding you can create an external class to hold all of the elements of State as Observable object fields. Then you pass that class to your View and it can bind the individual elements of State to the various components and their properties in the View. Once you\u0026rsquo;ve done that, you can just about forget about the inner workings of your View when you\u0026rsquo;re writing your business logic.\nThis Idea Scales! # You can think of this concept like a fractal diagram. At a big picture level, you have a UI with one or more screens involved with it. Together those screen comprise a single View, and would have a single State. But each of those screens is a View itself, and has an associated State. Maybe some of those screens have sections which are written as custom components, each of which can be considered to be a View with their own State. And when you look into the layouts of any of these screens you see sub-layouts and containers, each of which can be considered a View with an associated State.\nFinally, you get down to the individual Nodes in the layouts. Each of those can be considered a View, and each of them would have their own associated State.\nNo matter what level you\u0026rsquo;re looking at, the process is always the same. Create the View, configure the View and bind it to State, then set it in a layout and FORGET IT. {: .notice\u0026ndash;primary}\nState Data is Not View Data # This is a subtle but important distinction.\nAll of the data in State is in State because it is needed for the View, but it\u0026rsquo;s not the data of the View.\nLet\u0026rsquo;s look at some examples to understand what this means.\nData in State is in its native format. For instance, if you have a data element which is a whole number, it should be represented in State as an Integer type. The View might need to transform it into some other type in order to display it on the screen - probably a String, but maybe an Enum. Maybe the View needs to change it into a decimal between 0 and 1.0 so that it can be shown in a ProgressBar. Leave it as an Integer in State, and let the View deal with the presentation.\nData in State is organized to be independent of the View. Think about a name. How should a name be displayed? Last name, comma, first name? First name, space, last name? Just last name? Just first name? Name should probably be stored in State as two fields; first name and last name. That way the View alone can be responsible for how the presentation is done.\nData in State is identified by business meaning. As an example, let\u0026rsquo;s imagine that some value needs to be shown in a different colour if it\u0026rsquo;s in an error state, so you create a separate field in State to indicate this. What kind of data should it be, and what do you call it? Well, you don\u0026rsquo;t make it type Colour and call it XyzFieldColour. Something like a Boolean type called AbcValueExceedsLimit, would be much better. Nor would you make it a Boolean called XyzInRed. That wouldn\u0026rsquo;t make any sense to the Business Logic module - and what would happen if the View was changed so that it in purple? Let the View interpret the meaning and decide about the presentation.\nNotice a trend? State is designed to convey meaning to the data, the View is designed to handle presentation of the data. Keep these two roles strictly separated. {: .notice\u0026ndash;primary }\nHow Do You Do This? # At the Top Level # First, use some framework that separates View, State and Business Logic. I\u0026rsquo;ve found that my MVCI structure works well.\nWith MVCI there is a Controller which instantiates the State (the \u0026ldquo;Model\u0026rdquo;) and passes a reference to both the View and the Interactor (the Business Logic) in their constructors. The Model is just a POJO with the fields made up of JavaFX Observable objects.\nAs for the \u0026ldquo;Set It and Forget It\u0026rdquo; part, the Controller exposes a single method called getView() which returns a Region. Whatever class instantiates the Controller (very often something like Application which creates the Stage and Scene), then calls this method to get a very generic layout class which it can place into a Scene or another layout.\nThe result is that the entire MVCI construct presents itself as a generic Region to the rest of your application\u0026rsquo;s GUI. Whatever information it needs to bootstrap itself is supplied through constructor parameters or pulled through an external API in the Interactor. Your application\u0026rsquo;s GUI instantiates it, sets it in a Scene or layout, and FORGETS IT.\nInside the Layout # The best practice here is to stick to the \u0026ldquo;Single Responsibility\u0026rdquo; Principle, and \u0026ldquo;Don\u0026rsquo;t Repeat Yourself\u0026rdquo; (or, \u0026ldquo;DRY\u0026rdquo;), and things should just naturally fall in place.\nLet\u0026rsquo;s say that your main layout is a BorderPane. You\u0026rsquo;re going to have some quantity of zones in the BorderPane that you\u0026rsquo;ll want to populate and configure. Don\u0026rsquo;t stick it all in one, giant method. Have a top level method that just has one line of code for each zone, and that line calls some other method to construct the layout that goes into that zone. Something like this:\nprivate Region buildLayout() { BorderPane results = new BorderPane(); results.setTop(createTop()); results.setCenter(createCentre()); results.setBottom(createBottom()); return results; } Now buildLayout() has just one responsibility - to create the main layout.\nAlso note that buildLayout() doesn\u0026rsquo;t return BorderPane, it returns Region. There\u0026rsquo;s not much you can do with Region other than put it in a Scene or layout and set some constraints on its size. You certainly can\u0026rsquo;t muck about with its contents or call setRight() on it. This pretty much forces whatever calls buildLayout() to, \u0026ldquo;Set It and Forget It\u0026rdquo;.\nThe same thing goes for createTop(), createCentre() and createBottom(), all of which are going to return either Node or Region. You can see that buildLayout() simply calls those methods, takes the results and sets it in the BorderPane and then forgets them.\nLike I said earlier. This concept is fractal in nature. If createCentre() is going to create a BorderPane itself, then I\u0026rsquo;d expect it to look very much like buildLayout() in its structure. And so on\u0026hellip;\nBTW: Don\u0026rsquo;t give those methods names like, createCentre(). Call them something meaningful, like createDataEntryBox() or whatever. Anything but, createCentre(). {: .notice\u0026ndash;warning }\nAnother important point here is that the BorderPane has been named, \u0026ldquo;results\u0026rdquo;. This has a huge psychological impact as it really says, \u0026ldquo;I have no meaningful existence as an object outside of the context of this method\u0026rdquo;. That makes it very clear that you are going to \u0026ldquo;Set It and Forget It\u0026rdquo;.\nAt the Node Level # Individual Node declaration tends to be more about configuration than layout, but the ideas are very much the same. Let\u0026rsquo;s look at a simple example:\nprivate Node createNameBox() { Label prompt = new Label(\u0026#34;Name:\u0026#34;); prompt.getStyleClass().add(\u0026#34;prompt-label\u0026#34;); Label name = new Label(); name.textProperty().bind(state.nameProperty()); name.getStyleClass().add(\u0026#34;data-label\u0026#34;); return new HBox(5, prompt, name); } There\u0026rsquo;s a few things to make note of here:\nThe returned HBox is never instantiated as a variable. This is the end goal for \u0026ldquo;Set It and Forget It\u0026rdquo;. This method returns Node, not HBox. No calling method can use it as an HBox. These two Labels are only instantiated as variables because they need to be configured. This method is violating the \u0026ldquo;Single Responsibility Principle\u0026rdquo;, as it both performs layout and configures the Nodes. This last point is important, because it suggests some significant improvements which are going to get us even closer to \u0026ldquo;Set It and Forget It\u0026rdquo; - remember how I said this would follow naturally?\nLet\u0026rsquo;s do this:\nprivate Node createNameBox() { return new HBox(5, createStyledLabel(\u0026#34;Name:\u0026#34;, \u0026#34;prompt-label\u0026#34;), createBoundStyledLabel(state.nameProperty(), \u0026#34;data-label\u0026#34;)); } private Node createStyledLabel(String contents, String style) { Label label = new Label(contents); label.getStyleClass().add(style); return label; } private Node createBoundStyledLabel(ObservableStringValue contents, String style) { Label label = new Label(); label.TextProperty().bind(contents); label.getStyleClass().add(style); return label; } Now the configuration code is completely divorced from the layout code, so there\u0026rsquo;s no need to have any local variables in createNameBox(), and it\u0026rsquo;s become just one line.\nIf you think about it a little bit, you\u0026rsquo;ll see that createStyledLabel and createBoundStyledLabel have absolutely zero code in them which is specific to this, or any, particular layout. You could split them off into a static utility class somewhere and never have to think about the those Labels as Labels ever again.\nKotlin has \u0026ldquo;scope\u0026rdquo; functions that really bludgeon you over the head with this idea of \u0026ldquo;Set It and Forget It\u0026rdquo; because you can completely dispense with explicitly instantiating variables for these Labels:\nfun createBoundStyledLabel(contents : ObservableStringValue, style: String) : Node = Label().apply{ styleClass += style textProperty().bind(contents) } In truth, the apply function has an implicit receiver of this, which can then be omitted when referencing fields or methods of the Label in the scope of apply. But the impact is still huge - it\u0026rsquo;s never given a name and it\u0026rsquo;s a Label only in the scope of the apply.\nGeneral Guidelines # Single Responsibility and DRY Will Get You There # JavaFX is chock full of repeated boilerplate that you need to get out of your layout code. Try to keep configure the amount of configuration code (things like binding and setting styles) in your layout code down to a minimum. Write helper/builder methods with parameters to handle repeated configuration operations.\nAs you do this, you get closer and closer to \u0026ldquo;Set It and Forget It\u0026rdquo;.\nAvoid Nodes as Fields # Declaring a Node object as field in your View is essentially announcing that you\u0026rsquo;re planning on referencing it as a global variable throughout the class. That\u0026rsquo;s pretty much the opposite of, \u0026ldquo;Set It and Forget It\u0026rdquo;.\nSo don\u0026rsquo;t do it.\nThink about why you want to make it a field. What aspect of that Node do you want to access from all over your View? Maybe you could move that information into your State?\nDon\u0026rsquo;t Extend, Use Builders # The straight-ahead approach to build a View is to take a class like BorderPane and extend it.\nDon\u0026rsquo;t do that.\nCreate a ViewBuilder class which implements Builder\u0026lt;Region\u0026gt;. Call its build() method to create your view, and return it as a Region.\nThe important point here is that once your application has received a Region there\u0026rsquo;s not much it can do with it except put it in a layout and forget it.\nViews and ViewBuilders Have no Public Methods # \u0026hellip;other than build(), and their constructors.\nEvery new non-private method that you add to a View or ViewBuilder increases the coupling with the other components of your application. This is the opposite of what you want to achieve.\nWho\u0026rsquo;s going to call these methods?\nProbably your business logic. And that\u0026rsquo;s bad because it means that your business logic is now becoming entangled with the implementation of the View.\nBuilders and Builder Methods Return Node or Region # The higher you can go up the tree structure of JavaFX objects with your return types, the less functionality you\u0026rsquo;ll expose to the rest of your application, and the more freedom you\u0026rsquo;ll have to design the inner workings of that View without worrying about compatibility with any other part of your application.\nDon\u0026rsquo;t Put Business Rules in Your View # This is one of the primary guidelines to decide \u0026ldquo;What goes where?\u0026rdquo;\nImagine that you have a screen with some kind of warning icon on it somewhere. That icon is invisible until one of a subset of choices in a ComboBox is selected. It\u0026rsquo;s tempting to say, \u0026ldquo;This is View, I can bind the Visible property of the icon to the Value property of the ComboBox\u0026rdquo;.\nBut is it View?\nNot really, the conditions that determine if the warning icon should be shown are really business rules. Somewhere, you need some code that says, \u0026ldquo;These particular choices are important\u0026rdquo;. And that code is business logic. So it can\u0026rsquo;t go in the View.\nFar better is to create a Boolean property in State that indicates that whatever condition that would merit a warning has been met. Usually, that kind of a property is going to be a Binding, and the internal logic of the Binding would be set by your business logic class.\nIt\u0026rsquo;s important that programmers looking for business logic know where to go to find it. So keep it in one place.\nYou\u0026rsquo;re more likely to break this rule when it\u0026rsquo;s all contained within a single Node. For instance, if you want to change the colour of a Label when it\u0026rsquo;s value meets certain criteria. It\u0026rsquo;s so easy to just create a binding that looks at the value and restyles the Label based on that value. But those conditions are business logic.\nIn truth, the State for that Label actually contains two properties; one for the value and another to convey that some business condition has been met. Once again, that second property is likely to be a Binding that should be defined in your business logic class.\nBinding Properties # Bind Node Properties to State First # All of your Nodes should be as independent of each other as possible. So create as much of those dependencies with State as you can, even if this means that you have many Nodes and their properties bound to the same State property.\nCreate a Property Field in the View, and Bind to It # Imagine that you\u0026rsquo;ve identified some common aspect of your View that needs to be bound together. It\u0026rsquo;s not State, because it is completely internal to the presentation of the information in the GUI. As an example, let\u0026rsquo;s say that you want to have a \u0026ldquo;Page Down\u0026rdquo; Button activated whenever a Pane displays a scroll-bar. Rather than binding the Enabled property of the Button to the Visible property of the scroll-bar, create a Boolean property field or variable, call it \u0026ldquo;PageDownAvailable\u0026rdquo;, and bind it to the scroll-bar Visible property. Then bind the Disable property of the Button to it.\nThis is really the best route when the Nodes involved exist in different scopes.\nBind Nodes Properties to Node Properties as a Last Resort # And only do this when the relationship is strictly View. For instance, bind the MaxWidth of an HBox, to the Width of another HBox. Generally, you\u0026rsquo;re only going to do this when the two Nodes involved have been instantiated in the same scope.\nConclusion # Nothing in this article is particularly complex, or difficult to master.\nBut none if it is obvious when you\u0026rsquo;re starting out with JavaFX, and I\u0026rsquo;ve never seen any article or tutorial that lays it out clearly.\nAnd this approach works. It works really well because it strips out a huge amount of complexity in your design. It\u0026rsquo;s really amazing how even complicated systems can become almost trivial to execute in JavaFX when you take the right approach - and that all starts by drawing an uncrossable line between presentation and business logic.\nI\u0026rsquo;m not going to try and pretend that there\u0026rsquo;s anything innovative or unique about the concept of creating a \u0026ldquo;Presentation Model\u0026rdquo; and binding it to the View. It\u0026rsquo;s an idea that\u0026rsquo;s been around for decades.\nBut the \u0026ldquo;Set It and Forget It\u0026rdquo; philosophy takes this idea and turns it into a practical design pattern that you can implement at every level of your GUI application. I think it\u0026rsquo;s worth adopting.\n","date":"14 March 2022","externalUrl":null,"permalink":"/javafx/techniques/setitforgetit/","section":"JavaFX","summary":"The best way to structure an application is to separate the presentation of your data from the logic of your application.  That can be hard to do.  But not if you take an approach where you configure your screen elements, put them in the layout and then discard any reference to them.","title":"Building Views - The \"Set It and Forget It\" Approach","type":"javafx"},{"content":" Introduction # Hex Maps, or hex boards are a popular platform for tabletop games, especially wargames and Role Playing Games. They are uniquely useful because hexagons fit nicely into a grid and there are 6 directions from each hexagon (or just, \u0026ldquo;hex\u0026rdquo;), compared to just 4 with a grid made up of squares.\nBut for programming, they can be a little confusing, and some of the operations which are easy on a square grid, and easy from a visual perspective on a hexmap, are just that little more complicated to program. For instance, even when painting a grid you need to offset each column of hexes a little bit vertically from the columns beside it. Hexes run in a straight line up and down, but zig-zag when you go across a row.\nFortunately though, the process of getting a hexmap on the screen with JavaFX is relatively simple, and the Reactive nature of JavaFX allows you to divorce the mechanics of the game-play from the visual presentation of the hexmap.\nThis project is a rough skeleton of an application that puts a hexmap on the screen, and then allows you to modify the hexagons by manipulating data associated with each hexagon.\nThe Application # This application isn\u0026rsquo;t really designed to \u0026ldquo;do\u0026rdquo; anything. It simply paints the hexmap on the screen from data, provides a \u0026ldquo;DetailView\u0026rdquo; to allow the background colour and terrain features of each hex to be modified and then has a small animation that moves a cybertank around on the hexmap to show how game mechanics would interface with the map.\nThat\u0026rsquo;s just about enough functionality to give you a good idea of how the whole application hangs together, how different elements of the GUI connect to each other, and how game logic and mechanics can interact with the hexmap from it\u0026rsquo;s own environment.\nIn truth, if you wanted to writer your own game, you could do worse than clone this project, strip out all of the parts that don\u0026rsquo;t fit with your own application and start from what\u0026rsquo;s left. It\u0026rsquo;s a pretty solid foundation.\nSome of the visual concepts were lifted from the HexTML website, especially the terrain features which had been neatly packaged up into a \u0026ldquo;Sprite\u0026rdquo;.\nThe tank silhouette was created by Stephan Beal, from a set that he made publicly available.\nThe Map # The temptation here is to treat the map as a graphics drawing exercise and figure out how to draw a bunch of lines on a Canvas that will give the hex grid. Or maybe to draw a lot of hexagonal polygons onto the Canvas.\nYes, you need need to get hexagons on the screen. But rather than treat it as a drawing exercise, treat it as a layout exercise.\nIn this application, the map is actually made up of a number of hexagonal Regions, each of which is a StackPane. Then CSS is used to add a SVG path to form the hexagonal shape of the StackPane. The map itself is just a Pane, and each of the StackPanes is given an X and Y translation that will put it in the correct spot.\nThe CSS looks like this:\n.hex-tile { -fx-shape: \u0026#34;M 300,150 225,280 75,280 0,150 75,20 225,20 z\u0026#34;; -fx-border-color: -border-colour; -fx-border-width: 1px; } It doesn\u0026rsquo;t matter how big the hexagon described in the SVG shape in the CSS actually is, the scaling is going to be set in the program that instantiates and configures the StackPane. Just as long as the hexagon has the correct proportions so that it can be tiled up into columns and rows without having any gaps, then it\u0026rsquo;s good.\nAs you can see from the screen snap above, each column slightly overlaps the columns to the left and the right. Use a little bit of geometry and you\u0026rsquo;ll be able to figure out that each column is 3/4 of the width of a hexagon apart from the next. Then each column is offset vertically by 1/2 the hexagon height from the ones on either side. Creating a grid of hexagons isn\u0026rsquo;t really that hard, as the absolute position of each hexagon can be calculated mathematically given the column and row location of the hexagon, and their width and height.\nTaking this approach means that you\u0026rsquo;ll have a grid of hexagonal StackPanes with minimal effort. In this project, actually populating the map only takes about 8 lines of code!\nWith that conquered, the bulk of the application deals with connecting what goes on inside those StackPanes with the game mechanics.\nThe Application Approach # As usual, Model-View-Controller plus Interactor (MVC-I) seems to be the best way to construct this application.\nIn this case, though, there are several layers of MVC-I. The application as a whole has an MVC-I structure, and each hex has its own MVC-I structure. Lastly, the DetailView has an MVC-I structure as well.\nThere are two parts about this which are a little tricky:\nConnecting the DetailView and the HexTiles # It would have been really nice to be able to directly connect the elements of the DetailView directly to the elements in the TileModel. The problem is that the entire TileModel needs to be swapped out when a new Tile is clicked on. So this means that Bindings that connect the TileModel to the DetailView need to be severed when another Tile is clicked, but the Binding between the TileModel and the hex elements needs to remain intact. This turned out to be problematic.\nInstead, it was easier to think of the DetailView as working like a CellView in a TableView or a ListView. There needs to be a mechanism to move the actual data in and out of the construct.\nThe approach taken was to create a DetailModel which is dedicated to the DetailView and who\u0026rsquo;s properties are bound bidirectionally to the value properties of the Nodes in the DetailView. Then the DetailInteractor is given the job of copying data to and fro between the TileModels and the DetailModel at the appropriate times.\nImplementing Game Mechanics # All of the game mechanics reside inside the HexMapInteractor. HexMapInteractor has access to the HexMapModel which, in turn, has a list of all of the TileModels. This means that HexMapInteractor can manipulate all of the hexes by making changes to their models.\nHere\u0026rsquo;s the most important point: All of the game mechanics - like keeping track of the score, determining how far apart two units are on the map, who\u0026rsquo;s turn it is, determining the computer moves - all of those things are outside of the MVC of the GUI. Sure, the score may be displayed on the screen, but it\u0026rsquo;s the HexMapInteractor\u0026rsquo;s job to update the HexMapModel to reflect the score at the appropriate time.\nWhat this means is that the code that you write that actually runs the game, while it can access data from the Model and have elements that are triggered from the GUI, is completely separate from the GUI itself.\nHave your own idea about how to store the locations of the cybertanks? Go for it. That\u0026rsquo;s totally up to you. But when the time comes to update the map and move one of the cybertanks, you have to pick a tile and update its model to indicate who\u0026rsquo;s in it, then clear it out of the model of the tile where it used to be.\nApplication Notes # I decided to implement the background colours and terrain features as two separate elements, just because it was easy. This means you could have hills on a water background, which wouldn\u0026rsquo;t make any sense. But there you go.\nLet\u0026rsquo;s look at how these are implemented:\nTerrain Features # The terrain features are the little, grey images that can be put into each hex to indicate whether it has trees, or cactus or hills, or whatever. These are in an image known as a \u0026ldquo;Sprite\u0026rdquo;. A Sprite is just an image holding a number of smaller images arranged into a row, column or grid. They are generally used for animation, which is done by manipulating a ViewPort onto the image that shows one of the smaller images at a time. Cycling through the smaller images can end up looking like stop-action animation and show a character walking or running, or any other action. The idea is the same no matter what visual framework you are using. It turns out to be easy in JavaFX.\nIn this case, we\u0026rsquo;re just using the Sprite as an easy way to save the terrain feature images in a single package that we can access via an index.\nHere\u0026rsquo;s the complete code for the TerrainSprite:\npublic class TerrainSprite extends ImageView { private final ObjectProperty\u0026lt;Integer\u0026gt; imageIndex = new SimpleObjectProperty\u0026lt;\u0026gt;(0); public TerrainSprite() { initialize(); } private void initialize() { Image image = new Image(Objects.requireNonNull(TerrainSprite.class.getResource(\u0026#34;/images/sprites-terrains.png\u0026#34;)).toExternalForm()); setImage(image); double cellSize = image.getWidth(); setPreserveRatio(true); setOpacity(0.8); setViewport(new Rectangle2D(0, 0, cellSize, cellSize)); imageIndex.addListener(observable -\u0026gt; setViewport(new Rectangle2D(0, cellSize * imageIndex.get(), cellSize, cellSize))); } public ObjectProperty\u0026lt;Integer\u0026gt; imageIndexProperty() { return imageIndex; } } It boils down to an ImageView with an added ImageIndex Property that will be bound to a property in the TileModel. The InvalidationListener will reset the ViewPort on the ImageView when the index changes. The image itself is just a giant vertical strip of square component images.\nThis is a nice, easy way to translate a simple Integer into an image, and encapsulate the whole thing an independent class.\nTerrain Colours # Generally, there are three ways to maintain the background colour of Region in JavaFX. CSS, inline styling, or using the BackgroundFill class programmatically. In this case, where the colour is going to be changed by the user, CSS styling would probably mean programatically changing the StyleClass, or using PseudoClasses to handle the background. Either of these solutions would become clumsy for a demonstration program. In-line styling could work, but really doesn\u0026rsquo;t have any advantages over simply writing some code to change the background colour by using BackgroundFill.\nThis is done via an Enum that has both a label and a colour field. Arguably, this is a breach of the separation between data/model and the View, as the Model should only hold the information that would allow the View to determine how to represent it on the screen. By putting the colour in the Enum, this shifts the determination of the colour out of the View and into the Model.\nHowever, for demonstration purposes, this is a shortcut method that keeps the code as simple as possible.\nHex Tile Structure # As previous mentioned, each hex is just a StackPane with a hexagonal shape. It could be any layout class that makes sense to you, a VBOX, a Pane, a BorderPane\u0026hellip; anything.\nEach hex has three components inside it. A co-ordinate Label, a TerrainSprite, and an occupying unit ImageView. In game terms, this last element is generally referred to as a \u0026ldquo;counter\u0026rdquo;, but calling it that in program code would instantly lead to confusion, as \u0026ldquo;counter\u0026rdquo; is generally used as an alternative to \u0026ldquo;index\u0026rdquo;. So \u0026ldquo;Occupier\u0026rdquo; or \u0026ldquo;OccupyingCounter\u0026rdquo; became the term, even though it sounds clumsy. Naming things is hard!\nSince it\u0026rsquo;s a StackPane, they are just \u0026ldquo;stacked\u0026rdquo; on top of each other in the middle. The coordinate Label is forced down to the bottom of the StackPane, so that it\u0026rsquo;s always visible, even when there\u0026rsquo;s a TerrainSprite or Occupier in the hex.\nTo be honest, it would probably be best to put all of the possible Occupier images into a Sprite, just like TerrainSprite. That would also make it easier to swap out different sets of counters for different game scenarios. However, just to show a different way of doing it, an Enum was used, in much the same way that the background colours were set.\nThe ComboBoxes # The two ComboBoxes in the DetailView are interesting because they have custom CellViews set up to allow a graphical view of the data.\nThe colour selection ComboBox has a CellView that has a rectangle with the colour pulled from the TerrainType enum, along with the label from the same enum. Another part that\u0026rsquo;s not immediately obvious from the JavaDocs is the call to ComboBox.setButtonCell() to display the selected item at the top in the same format.\nThe terrain element is stored in the TileModel as a simple Integer, and the TerrainComboBox returns just an Integer. It has a custom CellView to turn the integer into an ImageView using TerrainSprite.\nFinally, the DetailView has an EventFilter set up on it to capture any ActionEvents that are triggered from inside of it. Both of the ComboBoxes will trigger an ActionEvent when a selection is made, but the there\u0026rsquo;s no point in having two different EventHandlers, one for each ComboBox, since it\u0026rsquo;s not significant if both values are applied to the active hex each time.\nScalability # If you look closely at the code, you\u0026rsquo;ll see that every element on the hexmap itself has it\u0026rsquo;s size bound (directly or indirectly) to the HexMapModel HexWidth property. If you change the value in this property while the application is running, the size of the map, the hexes and the contents of the hexes will change.\nPulling it All Together # It\u0026rsquo;s important that all of the components work together, yet remain separate and loosely coupled. To do this, virtually all of the coupling is done through the models. The HexMapModel has a reference to all of the TileModels, as they are held in a List, and it has a reference to the currently \u0026ldquo;active\u0026rdquo; tile, held in a Property.\nWhen a hex is clicked, there\u0026rsquo;s an EventHandler that invokes a Runnable that is passed to the hex from the TileController. That Runnable invokes the accept() method in a Consumer that was passed to it from the HexMapBuilder, which got it from the HexMapController.\nThat Consumer invokes a method in the HexMapInteractor that updates all of the TileModels, ensuring that the hex that was just clicked is the only one with it\u0026rsquo;s Selected property set to true. This consumer will also invoke set the HexMapModel ActiveTile property to be the model of the hex that was just clicked.\nThe DetailController is passed a reference to the HexMapModel ActiveTile property, to which it adds an InvalidationListener. Whenever this listener runs, it updates the DetailModel with the values from the newly activated hex.\nIn this way all of the hexes are MVC-I constructs that are completely unaware of any of the other elements of the GUI, including all of the other hexes. They each only have coupling through their TileModel and the ClickHandler, both of which are declared as constructor parameters.\nIn the same way, the DetailView is an MVC-I structure which is unaware of any of the other elements of the GUI. It has a single dependency, which is the ActiveTile property in the HexMapModel, which is also passed to it in its constructor.\nThis still has a little bit of extra coupling, since the DetailInteractor needs to be aware of the structure of the TileModel in order to move data to and from it. This could be removed by instantiating the DetailModel as an element of the HexMapModel, and passing it to the DetailController along with a Runnable. Then a method in the HexMapInteractor, which can see both the DetailModel and the TileModel of the active hex, could do the data transfers. The cost of this is that the plumbing gets quite a bit more complicated.\nGame Mechanics # In order to show how the game mechanics would integrate with the GUI while remaining separate from the GUI, there are two examples provided.\nLoading the HexTiles # If you look at how the HexMap is originally created, the HexMapViewBuilder is given the HexMapModel, which has an ObservableList of TileModels in it. The HexMapViewBuilder then loops through this list, creating each hex MVC-I and positioning its View on the screen according to it\u0026rsquo;s Location. There\u0026rsquo;s no code anywhere in the HexMapViewBuilder which has nested loops that run through rows and columns.\nAs a matter of fact, in order to set the dimensions of the containing Pane, HexMapViewBuilder has to stream through the List of TileModels to find the maximum values of row and column in all of the Locations.\nWhere do the TileModels come from?\nThe actual layout of the tiles is part of the game mechanics. So there\u0026rsquo;s code in HexMapInteractor that creates the List of TileModels. In this application, we do just have a pair of nested row and column loops. In an actual game application, however, you\u0026rsquo;d probably have a configuration file or a database call that would supply a set of tiles with locations and terrain information.\nAdditionally, in an actual game, your configuration information would probably contain lots of other information about each tile. This might be movement costs, restrictions about which kinds of units can occupy them, attack and defense modifiers \u0026ndash; lot\u0026rsquo;s of information that is important to the game mechanics, but not information that would not be put into the TileModel, as it has no relationship to the display of the hex on the screen.\nJust to make the point about the HexMapInteractor actually initializing the data for the load of the screen, there\u0026rsquo;s an extra step that puts a purple cybertank in one of the TileModels.\nAnimating a Cybertank # The application needed to reflect how the GUI would interact with the game mechanics when it was running. So there\u0026rsquo;s a Button on the screen which launches some activity. In this case, moving a cybertank around the board.\nThe idea here is that there is some amount of game logic that figures out what the cybertank is going to do. That logic runs, and generates some output that needs to be reflected in the GUI. Both of those aspects of the game mechanics are reflected in this application, although it\u0026rsquo;s extremely simplified to make it easy to follow.\nThe heart of the process is in the HexMapController, which uses a Task to do the path determination on a background Thread, and then an Event to accept that path and execute it in a JavaFX Animation. Here\u0026rsquo;s the HexMapController code:\nprivate void animateTank() { Task\u0026lt;List\u0026lt;Location\u0026gt;\u0026gt; tankTask = new Task\u0026lt;\u0026gt;() { @Override protected List\u0026lt;Location\u0026gt; call() { return interactor.calculateTankPath(); } }; tankTask.setOnSucceeded(evt -\u0026gt; interactor.animateTank(tankTask.getValue())); Thread tankThread = new Thread(tankTask); tankThread.start(); } And here\u0026rsquo;s the method in HexMapInteractor that \u0026ldquo;calculates\u0026rdquo; the cybertank path. You can see that it\u0026rsquo;s just a hard-coded list of Locations:\npublic List\u0026lt;Location\u0026gt; calculateTankPath() { return List.of(new Location(5, 10), new Location(5, 9), new Location(5, 8), new Location(6, 7), new Location(7, 7), new Location(8, 6), new Location(9, 6), new Location(10, 6) ); } And now the method that performs the Animation:\nprivate void moveTank(Location newLoc) { for (TileModel tileModel : model.getTileModels()) { tileModel.setOccupier(CounterType.NONE); } model.getTileModels().stream() .filter(tileModel -\u0026gt; ((tileModel.getLocation().equals(newLoc)))) .findAny() .ifPresent(tileModel -\u0026gt; tileModel.setOccupier(CounterType.CT3)); } To make sure that this is clear: That list of Locations for the cybertank path is game mechanics data. It\u0026rsquo;s organized in a manner that facilitates the running of the game. In an actual game application, there will be all kinds of data that stores information about the location of all of the units in the game, hex details, status of all of the units, the score. Any number of things.\nWhen it comes to showing the information on the GUI there needs to be some process that looks at that game data and translates it into the data models for the GUI.\nConclusion # A quick count of the 21 classes in the project comes up to about 600 lines of code, which seems like a modest amount considering how complete it looks when running. It certainly seems to show that building a hex map based game isn\u0026rsquo;t really a big challenge from a visual point of view, and not something that should stop you from trying it.\n","date":"6 February 2022","externalUrl":null,"permalink":"/javafx/projects/2022-02-09-hexmap/","section":"JavaFX","summary":"Introduction # Hex Maps, or hex boards are a popular platform for tabletop games, especially wargames and Role Playing Games. They are uniquely useful because hexagons fit nicely into a grid and there are 6 directions from each hexagon (or just, “hex”), compared to just 4 with a grid made up of squares.\n","title":"How To Build a Hex Map Based Application","type":"javafx"},{"content":" Introduction # People seem to get stuck when it comes to turning their JavaFX application into something useful; something that connects to the outside world, or saves and retrieves its data using a database, or interacts with some other application. I\u0026rsquo;ve seen this question asked from both directions:\nHow do I connect my JavaFX application to an SQL Database? I have a screen but I need to be able to save and read information.\nor:\nI have an application that does this interesting thing, but I\u0026rsquo;d like to add a JavaFX front end onto it. How do I do this?\nThese are reasonable questions. How do you do this:\nAnswer: Use a Standard Framework # The simple answer is to use a standard framework that will allow you to split your application into two parts. One part is the GUI, and the other is whatever your application actually \u0026ldquo;does\u0026rdquo;. Personally, I prefer Model-View-Controller, since it works really well with JavaFX as a Reactive environment. But that just describes the GUI part of your application.\nAt some point, you have to connect your GUI with the part of your application that does the work - which we\u0026rsquo;ll call the \u0026ldquo;domain\u0026rdquo; code. You need an interface that allows the GUI and the domain code to interact in a particular way. I call that the \u0026ldquo;Interactor\u0026rdquo;.\nNow your application has three parts:\nThe GUI (Model, View and Controller) The Interactor The Domain Code Why Do This? # This first time I was shown an MVC structure I struggled to understand what the benefit was. How does this make it any easier to build an application? I think that there are two key advantages to using this framework.\nFirst, it makes your programming easier to manage because it reduces coupling. Coupling occurs when the parts of application have too many dependencies on each other. It can be insidious, building up a little at a time, until it becomes impossible to change one part of your application because then you have to change something else, and then something else, and so on. Using a framework like MVC-I forces you to keep those pieces apart from each other, right from the start, and gives you clear rules about how much each part is allowed to know about the other parts. In the end, your application structure is much easier to understand.\nSecondly, it takes away the blank page syndrome. You\u0026rsquo;re not starting from zero, and even if it feels a little bit like, \u0026ldquo;paint by numbers\u0026rdquo;, at the start it makes very easy to get a bare skeleton up and running - sometimes in just minutes.\nThe Interactor # The heart of the question of, \u0026ldquo;How do I make my JavaFX application do something?\u0026rdquo;, really comes down to the role of the Interactor.\nThe Interactor is the only part of your system which has any knowledge about the other two parts. It\u0026rsquo;s the place where domain objects and the Model can exist together, and you can have code which is aware of both of them.\nThe Interactor is also the place where your specific application logic goes. While it has knowledge of the domain, its job is to support whatever function within the application that the MVC part of the framework supplies. It\u0026rsquo;s a part of a process that has the GUI as one end-point, and some \u0026ldquo;external\u0026rdquo; domain at the other end, and it contains all of the rules and logic that make the process work.\nExample Application - A Simple Weather App # Here we\u0026rsquo;re going to look at a simple application that has all of the components that you\u0026rsquo;ll need:\nA Reactive MVC GUI Application logic in an Interactor A weather fetching service A domain object An external API Before we go any further, let\u0026rsquo;s see what it looks like when it\u0026rsquo;s running:\nIt\u0026rsquo;s pretty simple. You pick a city from a small list in the ComboBox then click \u0026ldquo;Get Weather\u0026rdquo; and it retrieves the information. There\u0026rsquo;s only 3 pieces of data: the temperature, a description of the conditions, and an image associated with the conditions.\nAll of this information comes from a web API from openweathermap.org.\nAPI Token # If you want to run this application, you\u0026rsquo;ll need an API Token from openweathermap.org. You can get one for free from their site, and the link is on the \u0026ldquo;Pricing\u0026rdquo; page, under the \u0026ldquo;Free\u0026rdquo; column. You\u0026rsquo;ll probably be redirected to a login/sign-up page before you get there.\nGet An API Token\nWhen you get a token (it\u0026rsquo;s just a string of characters), you\u0026rsquo;ll need to copy it into a resource file in the project. Under \u0026ldquo;resources\u0026rdquo;, create a directory called \u0026ldquo;data\u0026rdquo;, and then create a file in it called \u0026ldquo;api.data\u0026rdquo;. Then just copy your token into it on the first line. Check the constructor of WeatherFetcher in the source code if any of this is unclear.\nThe application doesn\u0026rsquo;t have particularly robust error handling, but if it cannot find the token it will give you a message that it had a problem. If the token is invalid, you just won\u0026rsquo;t get any weather reports in the GUI.\nApplication Components # You should take a look at the project on GitHub. It\u0026rsquo;s not very big, and I\u0026rsquo;m only going to include a small part of the code in this article.\nGitHub Project\nThe ViewBuilder # The ViewBuilder is an implementation of the JavaFX Builder utility interface, returning a Region. Using a Builder here is better than the only other real alternative, which would be to extend one of the layout classes, like BorderPane, and configure it through its constructor.\nIn this case, the Region returned is a BorderPane. The centre of the BorderPane is an HBox holding the weather image, and a VBox that has the city name, and a GridPane with the weather information. The bottom of the BorderPane is an HBox with the city selection ComboBox and the Button which triggers the data load.\nAll of the styling in the View is done through CSS, which is included in the project.\nThere are a couple of static convenience classes that instantiate and configure some of the Nodes in the View. This helps to keep the ViewBuilder concerned with layout only, keeps it from getting clogged up with JavaFX boilerplate.\nTo allow the Button in the View to actually do something, the ViewBuilder is supplied a Consumer\u0026lt;Runnable\u0026gt; by the Controller through a constructor parameter. This is really just a callback containing a callback. We\u0026rsquo;ll look at this a little later.\nFinally, the Model is passed to the ViewBuilder through its constructor, and the Model properties are connected to the View elements as they are instantiated. From this point on, any changes in the Model will be automatically reflected in the View.\nThe Controller # The Controller is responsible for instantiating the other MVC components and the Interactor. In this respect it\u0026rsquo;s the \u0026ldquo;centre\u0026rdquo; of the application. The Main class for the application instantiates the Controller in order to bootstrap the GUI, and then calls its getView() method to get the root for the Scene.\nThe Controller is also responsible for handling FXAT threading. This is important in this kind of application, because virtually anything that needs to talk to the domain code will need to run off the FXAT, and then the effects of that domain code need to be applied back to the model by code running on the FXAT. Usually, this needs to be coordinated with both the View and the Interactor, so the Controller is the right place for that to happen.\nThe WeatherFetcher # This is the \u0026ldquo;domain code\u0026rdquo; of this application. It\u0026rsquo;s small, just a single class that is able to perform a couple of HTTP \u0026ldquo;GET\u0026rdquo; transactions with openweathermap.org, use some JSON parsing to extract the information and package up the results in a domain object - WeatherData.\nIt essentially becomes a service, wrapping the calls to openweathermap.org in it\u0026rsquo;s own logic and the bundling the response into a locally defined domain object.\nThe Interactor # This is where the \u0026ldquo;application logic\u0026rdquo; resides. The Interactor is instantiated by the Controller, and it is passed the Model as a parameter in its constructor. It has two methods, checkWeather() and updateWeatherModel(). The checkWeather() method invokes a method in the WeatherFetcher, and then stores the WeatherData object that is returned in a private field. The updateWeatherModel() method takes the data stored in that WeatherData object and puts it into the Model.\nAlthough the Interactor has no knowledge, and no logic, that is aware of the FXAT or any other threading issues, the design is such that checkWeather() will be called on a background thread, while updateWeatherModel() will be called on the FXAT.\nInvoking the Weather Fetch # Let\u0026rsquo;s look at the View first, and the set-up for the Button:\nprivate final Consumer\u0026lt;Runnable\u0026gt; weatherFetcher; . . . Button button = new Button(\u0026#34;Get Weather\u0026#34;); button.setOnAction(evt -\u0026gt; { button.setDisable(true); fetchWeather.accept(() -\u0026gt; { button.setDisable(false); }); }); You can see that weatherFetcher is a Consumer\u0026lt;Runnable\u0026gt;. So it\u0026rsquo;s a piece of code that is going to accept another piece of code as a parameter. Essentially, it\u0026rsquo;s a callback.\nWhy do this?\nWhen the Button is clicked, the first thing that we need to do is to disable it, so that it cannot be clicked again while the fetch is happening. But then we need to re-enable it when it\u0026rsquo;s all done. The problem that we have is that there is no mechanism that allows us to \u0026ldquo;wait\u0026rdquo; for the fetch to complete, because that would hang our GUI. So what we have to do is invoke the fetch, and then give the fetcher (whatever it may be) some code to execute when the fetch is completed.\nIn this case, that code just re-enables the Button.\nNow, let\u0026rsquo;s look at the what weatherFetcher does. It\u0026rsquo;s defined in the Controller:\nprivate void fetchWeather(Runnable postFetchGuiStuff) { Task\u0026lt;Void\u0026gt; fetchTask = new Task\u0026lt;\u0026gt;() { @Override protected Void call() { interactor.checkWeather(); return null; } }; fetchTask.setOnSucceeded(evt -\u0026gt; { interactor.updateWeatherModel(); postFetchGuiStuff.run(); }); Thread fetchThread = new Thread(fetchTask); fetchThread.start(); } Here we are defining a JavaFX element called a Task. A Task is JavaFX class which is designed to facilitate running code on a background thread and then get back on the FXAT. It\u0026rsquo;s possible to have a Task return a result, but in this case we don\u0026rsquo;t need it to, as the Interactor is going to store the results in a field.\nThe next thing to note is the call to setOnSucceeded(). Whenever you see a method called setOn...() in JavaFX, that means that you\u0026rsquo;re going to be passing it an EventHandler. The most important thing about this is that EventHandlers always run on the FXAT. The EventHandler passed to setOnSucceeded() is executed when the Task ends in any way other than throwing an exception. So this call to setOnSucceeded() tells us how the Controller is going to get the processing back on the FXAT after it\u0026rsquo;s done running on the background thread.\nIn this case, we want to do two things on the FXAT once the background thread has completed. We want the Interactor to update the Model with the data it just fetched, and then we want to run that code that came from the Button - the one that re-enables the Button.\nFinally, the Controller initializes a new Thread for the Task, and starts it.\nYou should be clear that fetchWeather() itself runs on the FXAT. This means that the Task is configured on the FXAT, the Thread is initiated and started from the FXAT, but everything in fetchTask.call() is executed on that new Thread.\nJust to be more explicit, all of this means that interactor.checkWeather() is run in a background thread, while interactor.updateWeatherModel() is run on the FXAT.\nJust to be even more explicit: This is the only place in our application code that has any knowledge about the FXAT and the only place where threads are addressed at all. This is one of the main responsibilities of the Controller.\nConclusion # This is about as simple as you can make an application that has a domain layer but yet it has all of the basic components that you need to deal with. You could clone this project and start changing the names of the classes and the methods and it probably wouldn\u0026rsquo;t be a bad start.\nYeah, But What If I Really Want To Use FXML? # I wouldn\u0026rsquo;t. It doesn\u0026rsquo;t do anything for you. However, if you must\u0026hellip;\nRemember that the FXML Controller is NOT an MVC Controller. You need to think of the FXML, FXML Loader and FXML Controller together as the \u0026ldquo;View\u0026rdquo; in MVC. And for that matter, the FXML file isn\u0026rsquo;t a Model either, even though it feels like data.\nWhat you\u0026rsquo;ll need to do is to create an MVC Controller that instantiates the View by calling the FXML Loader and then passing your Model into the FXML Controller to create the Bindings that connect it to the Nodes in the GUI. If you can do that, then the rest of your application structure should be just as described in this article.\nBut once again, I wouldn\u0026rsquo;t bother with FXML.\n","date":"6 February 2022","externalUrl":null,"permalink":"/javafx/projects/weatherfx/","section":"JavaFX","summary":"Maybe you’ve figured out how to build a working GUI that looks good.  Now, how do you connect it to the rest of the world and make it do something?","title":"How to Build a JavaFX Application That Does Something","type":"javafx"},{"content":" The Introduction of Optional # When Optional came out there was a lot of noise about how it was supposed to replace Null and help do away with NullPointerExceptions. So people started using Optional instead of Null in places where it really wasn\u0026rsquo;t appropriate, didn\u0026rsquo;t make sense, and certainly didn\u0026rsquo;t make code any cleaner or easy to understand.\nYou’d see things like this:\nif (Optional.ofNull(variable).isPresent()) then { someCode(); } Which, of course, isn\u0026rsquo;t any clearer than just checking if variable is Null. It\u0026rsquo;s possible this might have led to a backlash against Optional, and it seems to me that it’s not used today as much as it should be. So let’s look at what Optional really does.\nThe Purpose of Optional # To better understand how Optional should be used, we\u0026rsquo;ll use an example that could be a real world programming situation:\nAn Example # Imagine that we have a system with customer accounts and invoices. Somewhere, there’s a persistence layer that will look up a customer account and instantiate a domain object for the account. In most systems I’ve seen, it’ll have a signature something like this:\npublic CustAccount getAccount(String accountNumber) In this system, if we have an invoice and it has an account number referenced on it, then that account has to exist on file. If not, then we have data corruption and that’s a real problem. But if someone enters an account number into a screen to do a lookup, then they may have just typed in an invalid account, and that’s not necessarily a problem.\nThese two different situations need to be resolved differently, and the situation is all about the context of the code that calls the getAccount() method. Usually, systems are written so that getAccount() just returns Null if the account isn’t on file, and it’s up to the calling code to remember to check for Null, and decide what it means if the answer is Null.\nBut that’s not really how you are supposed to use Null, and Java has always had an alternative that seems to be rarely used, and that’s Checked Exceptions.\nChecked Exceptions # Checked Exceptions are a way of declaring to any calling routines that your method might legitimately not be able to return a result, and it forces them to deal with that possibility. Of course, many programmers just put an empty catch clause (or put a stackdump in it), or throw the exception all the way up to the top where it’s mishandled.\nSo, in order to set this up properly, we really need two methods in our persistence layer, one for the case where the account needs to be on file, and one where it might not be there. Something like this:\npublic CustAccount getAccountIfPresent(String accountNumber) throws MissingAccountException public CustAccount getAccount(String accountNumber) MissingAccountException is a checked exception. If getAccount() cannot find the account on file, then it throws some sort of RuntimeException, maybe something called DataCorruptionException. Neither method is capable of returning a Null value.\nThe real problem with getAccountIfPresent() is that it’s cumbersome and forces awkward code structures around the try-catch blocks and scoping of variables. So almost nobody ever uses this. They just pass Null when the account can’t be found.\nThe problem with that, though, is that there\u0026rsquo;s no way tell a consumer of getAccount() that Null is a reasonable return value or what it means without checking the code itself to see how the result is determined. And that\u0026rsquo;s a problem.\nOptional to the Rescue # This is where Optional comes in. With Optional you can do away with the checked exceptions and lock the answer in a wrapper that requires the calling method to deal with the possibility that the answer might be missing when you take it out. So the method signature would change to this:\npublic Optional\u0026lt;CustAccount\u0026gt; getAccountIfPresent(String accountNumber) With this, there’s only one way to get that CustAccount out of the Optional without handling its possible absence That’s the Optional.get() method, which should only be used inside an if statement that has already checked Optional.isPresent() first. Until Java 9, if you had Optional in a stream, you needed to use something like this:\nstream().filter(Optional::isPresent).map(Optional::get).... But now the Optional class has it’s own stream() method, which does the filter and get in one step. So you can do this:\nstream().flatMap(Optional::stream)... All of which means that there\u0026rsquo;s no longer any place that the Java language forces you to use Optional.get() any more.\nBrian Goetz has said in a famous StackOverflow response:\nQuote (Public service announcement: NEVER call Optional.get unless you can prove it will never be null; instead use one of the safe methods like orElse or ifPresent. In retrospect, we should have called get something like getOrElseThrowNoSuchElementException or something that made it far clearer that this was a highly dangerous method that undermined the whole purpose of Optional in the first place. Lesson learned. (UPDATE: Java 10 has Optional.orElseThrow(), which is semantically equivalent to get(), but whose name is more appropriate.))\nMany programmers first experience with Optional is when they start using the Streams facility, and encounter it with the .findAny(), or findFirst() methods. These methods return Optional because it\u0026rsquo;s entirely possible that your stream, especially after filtering, is empty and there may legitimately be no stream elements returned by these methods.\nLearning From Kotlin # Kotlin handles this concept in a much more natural manner and it\u0026rsquo;s instructive to take a look at it to understand Optional better.\nMost Variables Cannot be Null # In Kotlin, the default for a data type is that it cannot be null. When you declare a variable, it needs to be initialized right away. If it\u0026rsquo;s a field, it needs to be initialized in the constructor or in the init{} block that runs right after the constructor.\nThis forces you to think about how your fields and variables are created and used. Unlike Java the default instantiation isn\u0026rsquo;t Null, and if you want to allow Null you have to put in effort to implement it.\nThink about it. Nobody really wants Null. It\u0026rsquo;s a pain, and the reason that you have to deal with it can often be because somebody else didn\u0026rsquo;t take the time to deal with it properly in some method you rely on.\nNullable Variables # Kotlin allows you to declare a variable as \u0026ldquo;Nullable\u0026rdquo;. You do it like this, putting a \u0026ldquo;?\u0026rdquo; after the type name:\nvar myNumber : Int? No big deal, right?\nThink again.\nmyNumber isn\u0026rsquo;t an Int, it\u0026rsquo;s a Nullable Int or an Int?. You can\u0026rsquo;t use it like an Int. You can\u0026rsquo;t do:\nmyNumber = myNumber + 2; and you can\u0026rsquo;t do this, either:\nif (myNumber \u0026lt; 20) {} Because those are operations that work on Int, which myNumber is not.\nNullable Methods # Methods (or \u0026ldquo;functions\u0026rdquo; in Kotlin) can return Nullable types. So you could declare something like this:\nfun myFunction():Int? { } But then you have the same constraints that a variable of type Int? would have. It\u0026rsquo;s not an Int and cannot be treated as one.\nNull Safe Operations # Kotlin supplies the \u0026ldquo;Safe Call Operator\u0026rdquo; - ?. If you want to do something with a nullable type, you put that ? after the variable name. Then the method call will only happen if the variable isn\u0026rsquo;t Null. Something like this:\nval myString : String? = myNumber?.toString() If myNumber is Null, the toString() won\u0026rsquo;t be called and the result will still be Null.\nThe \u0026ldquo;Elvis\u0026rdquo; Operator # Here\u0026rsquo;s something that you cannot do:\nval myString: String = myNumber?.toString() Why not?\nBecause myNumber?.toString() is not a String, it\u0026rsquo;s a String?. You cannot assign it to a String you cannot perform String operations on it (at least not without the Safe Call Operator). Essentially, myNumber is an Int locked away in a safe, and you cannot take it out without telling the compiler what to do if it\u0026rsquo;s Null.\nEnter the \u0026ldquo;Elvis Operator\u0026rdquo;:\nval myString: String = myNumber?.toString()?: \u0026#34;I\u0026#39;m empty\u0026#34; If myNumber is not Null, then myString will have whatever myNumber translates to as a String. If it is Null then it will be assigned \u0026ldquo;I\u0026rsquo;m empty\u0026rdquo;.\nThe Dangerous Alternative # The last way to get a value out of a nullable type is the !! operator. The Kotlin documentation describes it this way:\nQuote The third option is for NPE-lovers: the not-null assertion operator (!!) converts any value to a non-null type and throws an exception if the value is null.\nYou can think of it as the \u0026ldquo;Look ma! No hands!\u0026rdquo; operator. Or the \u0026ldquo;Please sir, may I have another NullPointerExeception\u0026rdquo; operator. You can see how the Kotlin documentation clearly discourages its use. Even the choice of !! means it just jumps out of the code at you.\nFor the sake of completeness, this is how you would use it:\nval myString : String = myNumber!!.toString() This will throw an NPE if myNumber is Null. It\u0026rsquo;s really the only way to get an NPE in Kotlin.\nHow This Relates to Optional # I think you can see Kotlin nullable types relate to Optional in Java. The Safe Call Operator is fairly close to Optional.map(), the Elvis Operator is similar to Optional.orElse() and the !! operator corresponds to Optional.get().\nThe big difference between Kotlin and Java in this respect is that in Java the use of Optional is optional. It\u0026rsquo;s mandatory in Kotlin. Sure, you could thumb your nose at the \u0026ldquo;man\u0026rdquo; and say, \u0026ldquo;I\u0026rsquo;m just gonna make all my variables nullable\u0026rdquo;. But you\u0026rsquo;d pretty quickly see that your code had become an unreadable nightmare of Safe Call Operators and convoluted logic.\nYou couldn\u0026rsquo;t do this:\nvar index : Int? = 0 . . var counter: Int? = index++ because the \u0026ldquo;++\u0026rdquo; operator doesn\u0026rsquo;t work in Int?. So you\u0026rsquo;d have to do this:\nvar index : Int? = 0 . . var counter: Int? = index?.let{it++} Eventually you\u0026rsquo;ll decide not to make all of your variables nullable, and then you\u0026rsquo;ll have to decide which ones should be nullable. Ultimately you\u0026rsquo;ll end up having to decide why a variable should be nullable, and what it means when it is nullable. At that point, it\u0026rsquo;s not really about nullable any more, and it\u0026rsquo;s more about the contexts where data and answers might not be available.\nConclusion # Understanding this purpose of Optional makes it much easier to use it appropriately. Ideally, you shouldn\u0026rsquo;t ever be creating an Optional unless it\u0026rsquo;s the return value of a method. Optional shouldn\u0026rsquo;t be a field in a class, and Optional shouldn\u0026rsquo;t be a parameter passed to a method. Further, there needs to be logic somewhere that unpacks the Optional and handles the situations where it\u0026rsquo;s empty in a contextually appropriate manner.\nThe motivation for Optional isn\u0026rsquo;t to avoid Null. Yes, it does this, but that\u0026rsquo;s a side-effect. Really, Optional is about handling situations where data might be missing, for whatever reason. It\u0026rsquo;s about telling the consumers of a method that, \u0026ldquo;Hey! There may not be a result here\u0026rdquo;, and forcing them to deal with it without having to look into your code to understand what Null means, or even if you method might return it.\n","date":"6 February 2022","externalUrl":null,"permalink":"/java/optional/","section":"Programming","summary":"A look at how Optional works, and how it isn’t really about Null values - at least not if you were doing it right before Optional.","title":"Optional Isn't About Avoiding Null","type":"programming"},{"content":" Wordle # This simple game is everywhere right now (the beginning of 2022) and it seems like everyone is playing it. As I was writing this article, it was announced that Wordle was sold to the New York Times, who will host it in the future.\nIf you don\u0026rsquo;t know about Wordle you should go and take a look at it here{:target=\u0026quot;_blank\u0026quot;}. The website has all of the information that you need to understand how the game works, and you can play it (once a day) to see how it looks when it\u0026rsquo;s running.\nThe game mechanics are very similar to \u0026ldquo;Mastermind\u0026rdquo;, where you get clues about which letters are correct, and which are letters in the word but not in the correct place. It\u0026rsquo;s different in these respects:\nThe indicators are on the guessed letters themselves You know exactly which letters are correct You know exactly which letters are in the wrong place The guess has to be an actual word in the English language You only get six guesses All of these differences make the game a little easier than \u0026ldquo;Mastermind\u0026rdquo;, but the requirement to use real words makes it feel tougher even though this factor limits the field of possible answers.\nFinally, Wordle generates just one target word each day, for the entire world, and you only get to play it once per day.\nWordleFX # WordleFX is written to be an absolute clone of Wordle in terms of look and game play. You get one word per day, and it\u0026rsquo;s the same word as on the website. It doesn\u0026rsquo;t lock you out if you\u0026rsquo;ve lost and restart it, or just restart it part-way through so you can start over. So it has no feature to share your results - that wouldn\u0026rsquo;t be fair.\nWordleFX wasn\u0026rsquo;t really created to be replacement for playing Wordle on the Web. It\u0026rsquo;s intended to be a demonstration of how Reactive JavaFX can be used to create a highly interactive and interesting interface in an application. It\u0026rsquo;s also intended to be a demonstration of how CSS styling can be used as an integral part of a user interface.\nThe Code # This article doesn\u0026rsquo;t contain a lot of code samples. The entire application is about 350 lines of code in 10 classes. Seven of those classes have less than 40 lines of code. No class is more than 80 lines long, so it should be fairly easy to wrap your mind around how the application works.\nWordleFX is written in Kotlin because \u0026hellip; Kotlin. There\u0026rsquo;s nothing too crazy in here, and if you understand Java should should be able to scratch your head a little bit and figure out what most of the code is doing. Maybe you\u0026rsquo;ll decide that Kotlin looks really cool and you\u0026rsquo;ll want to learn it - that would be awesome.\nProbably the strangest thing to Java programmers will be \u0026ldquo;Scope Functions\u0026rdquo;, especially apply which is used copiously in WordleFX to configure Nodes. You can find out more about apply here.\nAnd no, you don\u0026rsquo;t need TornadoFX to write JavaFX applications in Kotlin.\nSo go take a look at the source code in GitHub if you\u0026rsquo;re interested:\nGitHub Project\nAppearance of the Application # As part of this exercise, both light mode and dark mode have been implemented entirely in the CSS. CSS styling is crucial to this application, and most of the user feedback is achieved by manipulating pseudo classes in the style sheets.\nThe application looks like this:\nAnimations # One of the biggest challenges in writing this application is to handle the animations, which really give the game its feel.\nThere are four animations:\nWhen a letter is selected, the tile \u0026ldquo;flashes\u0026rdquo; briefly. When the guess results are revealed, the tiles flip over to show their colour. When an invalid word is entered and checked, the entire row of tiles wiggle briefly. When an invalid word is entered and checked, a toast with an error message fades in, displays briefly and then fades out. Here\u0026rsquo;s what the tiles look like in motion:\nDesign Notes # Reactive Design # The design of WordleFX is 100% Reactive. There is a Model, which is the application \u0026ldquo;State\u0026rdquo;, and all of the components interact with each other indirectly by manipulating or responding to changes in the elements of \u0026ldquo;State\u0026rdquo;.\nIncluding Animations and Pseudo-Class Changes # Animations are necessarily imperative in nature. They are actions that are triggered and happen at a particular moment in time. This can be particularly challenging when animations are involved with changes in the GUI presentation of some element, as those changes cannot simply be bound to the Model since they need to co-ordinate with an animation.\nAt the same time, most of the GUI presentation changes are achieved through Pseudo-Class changes, which are also not Reactive in nature and require Listeners in order to work. It\u0026rsquo;s possible to include the Pseudo-Class change on the Node in the animation, or to co-ordinate it with the invocation of the animation.\nModel-View-Controller-Interactor # WordleFX is built with an MVCI structure, which is MVC with an added component, the \u0026ldquo;Interactor\u0026rdquo;. All of the game play logic is contained within the Interactor, which responds to events from the GUI and updates the Model. The View is composed of two major components; the game field containing the letter tiles, and the virtual keyboard.\nThis is all tied together by the Controller which instantiates everything and supplies Runnables and Consumers to the virtual keyboard to invoke methods in the Interactor. It also installs a Keyboard event handler on the entire View so that the user can use either the virtual keyboard or a real keyboard.\nLow Coupling # One of the key goals of any good application design is to reduce coupling as much as possible. In WordleFX, you\u0026rsquo;ll only find the following coupling:\nLayout Coupling Any layout is comprised of container classes (parents) which hold other Nodes (children) inside them. This kind of coupling is unavoidable, but its impact is reduced by ensuring that parents have as little knowledge of the nature of their children as possible. Virtually all child objects are returned from their builder functions as instances of Node or Region. Coupling To the Model The goal of MVC is have - as much as possible - the coupling happen exclusively with the Model and only the Model. Public Interactor Methods Every public method in a class adds to coupling but for the Interactor this is unavoidable, as the Controller needs to be able to call specific functions. Additionally, in pure MVC the Interactor functions would be inside the Controller. The MVCI structure physically isolates the business logic into the Interactor, which is an improvement in clarity, but then it needs to be exposed to the Controller in order to work. Other than the parent-\u0026gt;child relationships, every element of the GUI in WordleFX is completely independent and has no relationship, or knowledge of, any other element of the GUI.\nAn Example of How this Works # In WordleFX, the controller instantiates the Virtual Keyboard, supplying it Runnables and Consumers which will invoke methods in the Interactor in its constructor. The Virtual Keyboard needs these functional elements to handle the individual letter clicks, to handle the backspace key, and to process the guess when the \u0026lt;Enter\u0026gt; button is clicked. The only other dependency that the Virtual Keyboard needs is a map of LetterStatus keyed on each letter of the alphabet.\nWhat each of these functional elements does is not known to the Virtual Keyboard. It\u0026rsquo;s job is simply to invoke the appropriate Consumer or Runnable when each button is clicked. At the same time, the Controller has no knowledge of how the Virtual Keyboard will implement these functional elements.\nThen the Controller passes the Virtual Keyboard to the ViewBuilder in its constructor. The ViewBuilder accepts it as an instance of Region, and has no knowledge of its function or its true nature. As a Region it simply inserts it into it\u0026rsquo;s layout as a child.\nFinally, the ViewBuilder passes the View back as a Region. The Controller then installs a KeyEvent Listener on it, to handle the keystrokes from the physical keyboard. The View has no role in this other than to be a target for the Events which, in turn, have no need to know anything about the View.\nFrom this you can see that the main part of the View has absolutely nothing to do directly with any user input. All of the \u0026ldquo;Action\u0026rdquo; type of interactions are set up by the Controller.\nAs a matter of fact, you could remove the KeyEvent listener, and the application would run just fine - albeit without any physical keyboard input. Conversely, you could replace the Virtual Keyboard with an empty Pane, and the application would still work with physical keyboard input - however in this case you\u0026rsquo;d lose the clues in the colours of the Virtual Keyboard buttons.\nImplementation Notes # Tile Status # There are 5 possible states that a tile can be in:\nEmpty The tile has nothing in it. Unchecked A letter has been placed in the tile, but it has not been checked against the solution word Wrong A letter has been placed in the tile, it has been checked and that letter does not appear in the solution word Present A letter has been placed in the tile, it has been checked and found to be present in the word, but not in that position Correct A letter has been placed in the tile, it has been checked and found to match the letter in that place in the solution word These statuses have been created in an Enum called LetterStatus. Since the determination of Pseudo-Class colours is virtually 100% controlled by the value of a Node\u0026rsquo;s related LetterStatus, the code for updating PseudoClass based on LetterStatus has been included in the LetterStatus class.\nDoes this break some design rules, having GUI elements tied to an element in the Model? I\u0026rsquo;m not sure, but it avoids \u0026ldquo;feature envy\u0026rdquo; by having some other class performing logic based solely on the contents of LetterStatus.\nDark Mode # WordleFX supports the Wordle Dark mode, as well as the Light mode. Mostly this is just to demonstrate how sweeping changes to the look of the application can be made by organizing the CSS properly and using Pseudo Classes.\nDark mode looks like this:\nAll of the colours in the CSS are defined and named in the .root section of the CSS. The pseudo-class for \u0026ldquo;dark-mode\u0026rdquo; modifies those definitions:\n.root{ -white-colour: white; -wrong-colour: #787c7e; -text-fill-colour: black; -empty-border-colour: #d3d6da; -unlocked-border-colour: #787c7e; -background-colour: white; -key-background-colour: #d3d6da; -toast-background-colour: black; -toast-letter-colour: -white-colour; -present-colour: #c9b458; -correct-colour: #6aaa64; } .main-screen: dark-mode { -white-colour: #d7dadc; -wrong-colour: #3a3a3c; -text-fill-colour: -white-colour; -empty-border-colour: #3a3a3c; -unlocked-border-colour: #565758; -background-colour: #121213; -key-background-colour: #818384; -toast-background-colour: -white-colour; -toast-letter-colour: black; } The \u0026ldquo;main-screen\u0026rdquo; selector is applied to the VBox which contains the entirety of the window, and when the pseudo-class is applied to it the definitions of these colours will change for all of its child Nodes, thereby enabling Dark mode.\nThe trigger for the mode change is the \u0026ldquo;Gear\u0026rdquo; icon in the top right corner of the screen.\nIkonli FontAwesome # WordleFX uses the Ikonli library for integrating FontAwesome into JavaFX to handle icons. This library provides a class FontIcon which you can use just like Text in an application. In WordleFX, the \u0026ldquo;gear\u0026rdquo; icon in the top right corner of the screen, and the \u0026ldquo;backspace\u0026rdquo; icon on the Virtual Keyboard are FontIcon.\nAnimations # All of the animation code is contained in a separate file of package level functions called \u0026ldquo;WordleAnimations.kt\u0026rdquo;. All of these functions simply accept a Node as a parameter, and would work equally on any kind of Node passed to them.\nAll of these animations require multiple steps to complete, and most of them use SequentialTransition to chain several Transitions one after the other.\nFlipping Tiles # In Wordle, the tiles flip one after the other, possibly with each tile starting to flip before the previous tile has finished (this is how it\u0026rsquo;s implemented in WordleFX).\nHow do we keep each tile independent but still have the tiles flip sequentially?\nThe answer is, they aren\u0026rsquo;t really flipping sequentially! Each one has a delay, courtesy of PauseTransition which is run at the beginning of the sequence. The length of the delay is based on the Column property of the LetterModel which is installed on the tile. So all of the flip sequences are running virtually in parallel, but the first step in each one is a pause which varies from 0 to 2000 milliseconds.\nHere\u0026rsquo;s the code:\nfun flipTile(node: Node, column: Int, letterStatus: LetterStatus) { SequentialTransition(PauseTransition(Duration(column * delay)), RotateTransition(Duration.millis(speed), node).apply { byAngle = 90.0 axis = Rotate.X_AXIS setOnFinished { LetterStatus.updatePseudoClass(node, letterStatus) } }, RotateTransition(Duration.millis(speed), node).apply { byAngle = -90.0 axis = Rotate.X_AXIS }).play() } The other thing to notice about this is that the call to update the PseudoClass happens in the EventHandler at the end of the first RotateTransition. So, in Light mode, the tile is white until it rotates 90 degrees down when it\u0026rsquo;s essentially edge on and invisible, the colour changes, and it rotates back up again. The colour change makes it look like it\u0026rsquo;s doing a single 180 degree rotation.\nWiggling Invalid Words # The animation itself is fairly straight-forward. Move the Node 5 pixels to the left, run repeated TranslateTransition to move it 10 pixels to the right and back, and then put it back where it started at the end of the TranslateTransition:\nfun wiggleRow(node: Node) { node.translateX = -5.0 TranslateTransition(Duration.millis(50.0), node).apply { byX = 10.0 cycleCount = 6 isAutoReverse = true setOnFinished { node.translateX = 0.0 } play() } } The big question about this was how to trigger it in a Reactive manner. All of the tiles are independent, but the wiggle needs to happen based on the entire row, and triggered when the Interactor decides the word is invalid.\nIn truth, the animation doesn\u0026rsquo;t care if the tiles are wiggled together or in a group. The effect would be the same if 5 transitions (one per tile) were triggered in a millisecond of each other, or if the whole row is wiggled as one Node.\nThe real challenge was to implement it in State, so that it would work.\nOriginally, the tiles were implemented on the screen in a GridPane. That would have meant adding a new component to the LetterModel to make each tile aware of what row it was on (column was added to LetterModel to facilitate the flip delay). However, since the tiles were all identical in shape, there really wasn\u0026rsquo;t any need to have them in a GridPane, and it was just as effective to put them in HBoxes, all contained in a VBox. This meant that there was now a screen Node that corresponded to a single guessed word.\nNow, how to trigger the wiggle?\nFirst Implementation # Since you can only have one active guess at a time, and you can move on to the next while the current word is invalid, I decided to implement an IntegerProperty in the Model called InvalidRow. Originally, it would be set to 99, which wouldn\u0026rsquo;t correspond to any row on the screen. Then it would be changed to the current row if the word was found to be invalid.\nThe HBox for the word row looked like this:\nprivate fun createRow(row: Int) = HBox(5.0).apply { alignment = Pos.CENTER children += IntRange(0, 4).map { letterBox(model.letters[row][it]) } model.invalidRowProperty().addListener { _ -\u0026gt; if (model.invalidRow == row) { wiggleRow(this) } } } This felt really kludgey. But on the other hand, it seemed very direct and simple to understand.\nThere was one problem, however\u0026hellip;\nWhat if the word was invalid the second time that it was checked?\nIn this case, the InvalidRow property would already be set to the current row, and the InvalidationListener wouldn\u0026rsquo;t be triggered. It was possible to get around it though.\nHere\u0026rsquo;s the code from the Interactor that handled word validity:\nif (data.isWordValid(guess.map(LetterModel::letter))) { . . . } else { model.invalidRow = 99 model.invalidRow = model.currentRow } This worked, but really set off alarm bells. Essentially what this code does is treat a State element like a trigger, instead of a representation of \u0026ldquo;state\u0026rdquo;. The invalid row didn\u0026rsquo;t actually change. Also, there\u0026rsquo;s no guarantee that JavaFX isn\u0026rsquo;t going to detect that value wasn\u0026rsquo;t really changing, and not trigger the invalidation.\nA Small Improvement # It made a little more sense like this:\nmodel.invalidRow = 99 if (data.isWordValid(guess.map(LetterModel::letter))) { . . . } else { model.invalidRow = model.currentRow } And you could argue that before the word is actually checked it\u0026rsquo;s not invalid and the change to State reflects this.\nBut it still seemed round-about and kludgey.\nThe Final Version # A more direct way makes sense, even though it complicates the Model a little bit. The Model was changed to remove invalidRow and replace it with this:\nval wordValidity = List(6) { _ -\u0026gt; SimpleBooleanProperty(true) } val wordsValid: BooleanBinding = run { var results = SimpleBooleanProperty(true).and(wordValidity[0]) wordValidity.forEach { results = results.and(it) } results } The BooleanBinding that does an and on each of the elements of wordValidity is needed because the toast display of the error message when an invalid word is checked needs to know if any of the words is invalid. It will now listen to wordsValid.\nThe word checking part of the Interactor isn\u0026rsquo;t really that different, it now just updates the wordValidity element that corresponds to the row being checked:\nmodel.wordValidity[model.currentRow].set(true) val guess = model.letters[model.currentRow] if (data.isWordValid(guess.map(LetterModel::letter))) { performCheck(guess) model.currentRow++ model.currentColumn = 0 setAlphabet() } else { model.wordValidity[model.currentRow].set(false) } In the Model, this feels like more work, but in terms of the Interactor and the View, it\u0026rsquo;s now much more direct and easy to understand.\nChecking Words # Wordle has two lists, one is a list of candidate words from which today\u0026rsquo;s word will be selected. The second is a list of every five letter word in the English language that isn\u0026rsquo;t a candidate word. Presumably the candidate words were cherry picked to be more common and therefore fair to the average player. It\u0026rsquo;s hard to imagine anyone actually guessing a word like \u0026ldquo;zanze\u0026rdquo;. However, any word from either list is considered a \u0026ldquo;valid\u0026rdquo; word, and can be entered as a guess.\nThe JavaScript for Wordle simply has both lists hard-coded into the source code. WordleFX has them split out into text files in the project resources. They\u0026rsquo;re read when the application is started, and stored as lists.\nThe code for determining the result for each letter in each guess is the most complicated part of the application. This is because if a guess contains repetitions of a letter which appears in the solution fewer times than in the guess, it only gets one \u0026ldquo;Correct\u0026rdquo; or \u0026ldquo;Present\u0026rdquo; result for each time the letter appears in the solution.\nFor instance, if the solution is \u0026ldquo;SCRAM\u0026rdquo; and the user enters \u0026ldquo;MUMMS\u0026rdquo; as a guess. Then only the first \u0026ldquo;M\u0026rdquo; should get a \u0026ldquo;Present\u0026rdquo; result, and the second and third should get \u0026ldquo;Wrong\u0026rdquo;. If the solution is \u0026ldquo;GUMMY\u0026rdquo; for the same guess, then the second and third \u0026ldquo;M\u0026rdquo; should get \u0026ldquo;Correct\u0026rdquo;, and the first should get \u0026ldquo;Wrong\u0026rdquo;.\nLet\u0026rsquo;s look at the code for checking a word:\nprivate fun performCheck(guess: List\u0026lt;LetterModel\u0026gt;) { val presentLetters = model.word.filterIndexed { index, c -\u0026gt; guess[index].letter != c } as MutableList\u0026lt;Char\u0026gt; model.wordGuessed = (presentLetters.size == 0) guess.forEachIndexed { column, letterModel -\u0026gt; val letter = letterModel.letter letterModel.status = LetterStatus.WRONG if (model.word[column] == letter) { letterModel.status = LetterStatus.CORRECT } else if (presentLetters.contains(letter)) { letterModel.status = LetterStatus.PRESENT presentLetters.remove(letter) } } } The MutableList, presentLetters is created by filtering out every letter in the solution that has been guessed correctly. This leaves only letters which are candidates for the \u0026ldquo;Present\u0026rdquo; result. If presentLetters is empty, then it means that every letter in the solution has been guessed correctly and the game is over.\nNow, for each letter in the guess, we check to see if it\u0026rsquo;s a match against the corresponding letter in the solution and give it a \u0026ldquo;Correct\u0026rdquo; status if it is. If it\u0026rsquo;s not, then we only need to check to see if it\u0026rsquo;s in the presentLetters list. If it is, then we mark it as \u0026ldquo;Present\u0026rdquo; and the delete that letter from presentLetters. This way, we can only get one \u0026ldquo;Present\u0026rdquo; status for each occurance of a letter in the solution.\n","date":"29 January 2022","externalUrl":null,"permalink":"/javafx/projects/wordlefx/","section":"JavaFX","summary":"The original Wordle is JavaScript in a browser, here’s an implementation of it in Reactive JavaFX written in Kotlin.","title":"Wordle in Kotlin JavaFX","type":"javafx"},{"content":"","date":"17 December 2021","externalUrl":null,"permalink":"/categories/agile/","section":"Categories","summary":"","title":"Agile","type":"categories"},{"content":"There\u0026rsquo;s a lot of programmers out there attending daily \u0026ldquo;Stand-Ups\u0026rdquo;, Sprint Planning meetings and Retrospectives, watching \u0026ldquo;Burn Down\u0026rdquo; charts, and worrying about \u0026ldquo;Velocity\u0026rdquo;. All the time thinking, \u0026ldquo;What a lot bureaucratic nonsense, just let me program!\u0026rdquo;\nThe truth is that there\u0026rsquo;s just one simple purpose of Agile:\nThat\u0026rsquo;s it.\nEveryone Learns During the Project # Think about it.\nAt what point in a project do you know the least about everything you need to know in order to succeed? A what point do you know the most?\nThe start of the project is when your knowledge is at its absolute lowest point. Some of the things the developers don\u0026rsquo;t know yet:\nThe feature set The constraints What tools you are going to use What the technical challenges will be How the product should interact with users How the product will alter the business process And it\u0026rsquo;s not just the programmers that are suffering from this lack of knowledge. The customers (whomever is going to be using your new system) really don\u0026rsquo;t know what they want. They can\u0026rsquo;t, because they don\u0026rsquo;t know what the Team can do.\nThe One Fundamental Truth About Building a System # Over the years, I\u0026rsquo;ve built tons and tons of applications for in-house use by the companies I\u0026rsquo;ve worked for. I have found that there\u0026rsquo;s one supreme and inescapable factor that no one ever told me about:\nI\u0026rsquo;ve sat in countless meetings with programmers and users; and participated in countless discussions about requirements and user interfaces and business processes. And in pretty much all of those meetings it really felt like the users understood what the product was going to be. I\u0026rsquo;ve listened while they\u0026rsquo;ve picked over details of requirements, argued amongst themselves about priorities and business processes, and generally behave like they get it.\nIn virtually all of those cases, it\u0026rsquo;s been pretty clear that all of the developers are on the same page. They may disagree about some details, but they have a shared understanding of the main concepts of whatever was we were discussing. They are pondering the implications of the design, how they\u0026rsquo;ll wrestle the information out of the database, what the performance bottlenecks might be. But they\u0026rsquo;re all picturing the same thing.\nThen, later - sometimes days, sometimes weeks or months later - it becomes clear that each of the customers had completely different ideas about what the product would look like. Most often, this is when they actually get a chance to use the product.\nMy experience is that you cannot avoid this.\nDetailed requirements won\u0026rsquo;t fix it. Users don\u0026rsquo;t read them (not really), even if they have to sign off on them. If they do read them, they won\u0026rsquo;t actually understand them properly. Mock-ups don\u0026rsquo;t work.\nCustomers will look at them and get a surface understanding of what a screen might look like, but they won\u0026rsquo;t actually think about what it means, and what it implies about the system\u0026rsquo;s design. Prototypes fail.\nThere seems to be a mental loophole - that this isn\u0026rsquo;t actually the real product - that lets the customers off the hook about really engaging with a prototype. Honestly, mock-ups are quicker and get you just as far. So what can you do?\nDeliver an actual, working product as soon as possible # It doesn\u0026rsquo;t have to be - actually, it shouldn\u0026rsquo;t be - the final, complete product. Maybe it\u0026rsquo;s just a single screen, or even a menu, or a report. Something where you can say, \u0026ldquo;Unless you want changes, this is it. This is what you\u0026rsquo;ll be using\u0026rdquo;.\nThere\u0026rsquo;s something about that flips a switch in the people\u0026rsquo;s minds. Suddenly, they start wondering why the data is presented a certain way, why some element that they\u0026rsquo;ve never told the developers about is missing, and maybe how this horrible thing is never going to work for them.\nMore than once I\u0026rsquo;ve had a team told, \u0026ldquo;If it can\u0026rsquo;t do this vital thing I never told you about, I might as well just go back to using a pencil and paper!\u0026rdquo;\nThink about how much better it is to be told that a week or two into a project, and not six months later when you\u0026rsquo;ve baked all kinds of assumptions into your design.\nThis is One of the Problems Agile Helps to Solve # The Agile Manifesto doesn\u0026rsquo;t talk about Scrum or Kanban, or Daily Stand-ups or any of that. It talks about a set of values and finding a better way to build software.\nThere\u0026rsquo;s a second page, with \u0026ldquo;Agile Principles\u0026rdquo;. The first three are these:\nOur highest priority is to satisfy the customer through early and continuous delivery of valuable software.\nWelcome changing requirements, even late in development. Agile processes harness change for the customer\u0026rsquo;s competitive advantage.\nDeliver working software frequently, from a couple of weeks to a couple of months, with a preference to the shorter timescale.\nThis is describing an \u0026ldquo;iterative, incremental\u0026rdquo; approach to development which leads to an evolutionary design of the system. It\u0026rsquo;s also the only part of the Manifesto that comes close to describing a process of building software.\nProgress == Working Software # There\u0026rsquo;s one other principle which ties this all together:\nWorking software is the primary measure of progress.\nThis is a critical concept. A Gantt chart or a \u0026ldquo;Requirements Specification\u0026rdquo; document isn\u0026rsquo;t \u0026ldquo;progress\u0026rdquo;. It\u0026rsquo;s not a result. It\u0026rsquo;s a throw-away artifact on the way to progress. If you spend 6 months gathering and documenting requirements, building a system design and making a schedule, how much progress have you made? Zero!\nHow can this be?\nThink about it. How valuable is a Requirements Specification if it\u0026rsquo;s wrong? Not much, right? What\u0026rsquo;s the chances that a document created right at the beginning of a project, before any users see any working software is going to be reasonably correct?\nWhat would happen if you built the first little bit of the application based on the requirements and the customers said, \u0026ldquo;Sorry, that\u0026rsquo;s not what we need\u0026rdquo;?\nWhat if there\u0026rsquo;s some new revelation in that feedback, and that new information ripples through the whole system design making little changes everywhere?\nNow, how much progress do you think was contained in your Requirements Specification? How much of that first 6 months was wiped out after the first demo of working software after 2 weeks of programming?\nHow This is Different From Waterfall # The idea behind Waterfall is to collect all of the requirements up-front, document them and get customer sign-off indicating that this is, in fact, an accurate description of exactly what they want. Then the requirements are analyzed and a system design is created. From that, the technical specifications for programming are written up, estimates around the programming time are determined and a schedule is drawn up. Programming begins and the system is built, after which it is tested and then released to the customer for acceptance testing. Finally, it is deployed and goes into maintenance mode.\nThis is a very standard project management practice which is used in a many industries with a great deal of success. It tends fails catastrophically for many, if not most, software projects.\nWhy?\nVirtually all software projects have some aspect of exploration and experimentation in them, of doing something that no one has done before. Otherwise, it would be cheaper to buy it off-the-shelf or to hire someone who\u0026rsquo;s done in many times before.\nIt turns out that the idea that you could somehow spend weeks or months collecting detailed requirements and having them verified by the customer at the beginning of the project is flawed. The idea that you can create a perfect design up-front, and then schedule the programming from that design just doesn\u0026rsquo;t work in real life.\nHere\u0026rsquo;s What Usually Happens # In a real-life Waterfall systems project, you can expect the follow things to happen:\nThe customer knows they\u0026rsquo;re locked in on the requirements after they sign off, so they defensively ask for everything that can possibly think of because they cannot add it later. The customer gives up on the requirements document after the first hundred pages. The customer nit-picks a few details in the requirements but misses some really important implications. The customer has no hope of understanding the design documents, if they ever see them. The programming specification follows the design which is based on flawed/incomplete requirements. There\u0026rsquo;s a Gantt chart! Six months into the project, programming starts. Programmers encounter difficulties from the very first module they build: Task #2 starts a bit later than the Gantt chart says. Task #3 starts even a bit later than that\u0026hellip; Programmer testing eventually consists of, \u0026ldquo;It compiles!\u0026rdquo;. QA will catch the obvious bug. It will come back as a new bug-fix task with new time allocated. Programming looks like it\u0026rsquo;s sticking a little closer to the Gantt chart. Something actually takes less time than the Gantt chart said, use up all the time anyways. Programmers discover huge flaws in the design. Nothing can be done about this. The Project Manager spends all of their time stretching the Gantt chart out, sweating over the Critical Path. If there are demos throughout the project, anything that the customer sees that seems wrong needs to become a \u0026ldquo;Change Request\u0026rdquo;: Change Requests flow through some approval process. Change is highly resisted. Heated arguments centre around whether something is a \u0026ldquo;Bug\u0026rdquo;, an \u0026ldquo;Enhancement\u0026rdquo; or a \u0026ldquo;Change\u0026rdquo;. Somehow this is important. There is talk of \u0026ldquo;Phase II\u0026rdquo; Eventually, possibly, something is delivered. Unfortunately: It\u0026rsquo;s bigger than it needs to be. It\u0026rsquo;s missing critical stuff nobody thought of at the beginning. Some of that missing critical stuff is going to be very difficult to add, since the system was designed without it. It\u0026rsquo;s late. It\u0026rsquo;s missing stuff that was jettisoned to meet the deadline that wasn\u0026rsquo;t met. The code quality is low because every step of the programming was rushed to stick to the Gantt chart. Nobody wants to go through this again with Phase II - but what\u0026rsquo;s the choice? As bad as this sounds, this would actually have been considered a pretty good outcome 20 years ago. Everyone that was around back then had heard of a least a few projects that made it to within 6 weeks of the \u0026ldquo;Go Live\u0026rdquo; date, and then suddenly asked for another 6 months and $1M to complete the project. And then got shut down. It was common.\nSurveys back then estimated that more than 50% of all IT projects were either cancelled or considered failures. The number of IT projects that came in, \u0026ldquo;On time, on scope and on budget\u0026rdquo;, was laughably small.\nWhen Waterfall failed, it was usually blamed on bad requirements gathering at the beginning. Which is absolutely true. The fallacy is that somehow - maybe with more time and better skilled staff - you can actually create complete and accurate requirements on day one for a software system that hasn\u0026rsquo;t been built yet.\nThis is the Real Point of Agile # Most of us lived in this world of failure. There had to be a better way, and we needed to start doing something different in order to succeed.\nAgile works better because it is based on two critical assumptions:\nBetter decisions are made when there is more information available, and information grows over time. All decisions should be deferred as long as reasonably possible. Non-developers are only be fully engaged in the process when they can interact with working software. Not that any of this makes building systems easy, but it does describe a process which has a much better chance of success.\nWhat About Scrum? # Even though it doesn\u0026rsquo;t work very well, corporations lean towards Waterfall because it gives an impression of control and predictability that fits in well with the way that they are organized. Agile scares them because it admits that these are often illusionary when it comes to software development.\nSo, here comes Scrum. Scrum provides two vital things:\nStructure for the Team # Scrum creates a framework which allows a team to organize within a corporation which isn\u0026rsquo;t Agile; that evaluates employees individually instead of as a team, and that doesn\u0026rsquo;t really understand the complexity of the work that they do.\nThe sharp division of responsibilities between the developers and the customer, with the emphasis on ownership of the feature set on the customer side (the term, \u0026ldquo;Product Owner\u0026rdquo; is not an accident) leaves the technical implementation completely under control of the developers.\nThere\u0026rsquo;s also an awareness that work is work, whether it\u0026rsquo;s a bug fix, a new feature, a change or an enhancement. Nobody argues about whether something is a bug fix any more, it goes on the Product Backlog, gets prioritized, estimated and scheduled just like any other work.\nThe cadence of regular Sprints creates a structure around which a team can learn to collaborate and organize a large project into small, manageable pieces. The lock-down of priorities which occurs at the beginning of a Sprint insulates the Team from organizational noise and forces the company to respect the Team\u0026rsquo;s need for focus while they work.\nStructure for the Organization # Scrum provides a framework that allows Agile to be more easily integrated with the controls of organization that isn\u0026rsquo;t Agile.\nMost organizations understand commitments and deadlines, and have a need to keep the leadership informed about progress and delays. While Agile appears to take away much of the ability to budget and plan, Scrum gives back transparency. Lots of transparency.\nNow there\u0026rsquo;s a Product Backlog with a list of everything left to do. The most important stuff is at the top, and probably has some kind of rough estimate around it. The Team estimates and makes commitments to complete a certain amount of work every Sprint. How much they can get done in a Sprint even has a name, \u0026ldquo;Velocity\u0026rdquo;, and it can be measured! We can use Velocity and the Product Backlog to predict things like completion dates!\nWhat About the Stand-Ups? # The Daily Stand-Up meeting is supposed to be about each team member informing the rest of the Team about how they are doing on the work. It\u0026rsquo;s not about reporting progress to the Scrum Master or management, but to each other. It\u0026rsquo;s supposed to be held standing up to keep it short and, therefore, focused and meaningful.\nIf the Team hates the Stand-Up, even if it\u0026rsquo;s short, it\u0026rsquo;s a pretty good sign that they\u0026rsquo;re not really collaborating. Probably, they\u0026rsquo;re dividing up the work for the Sprint into tasks and \u0026ldquo;assigning\u0026rdquo; them to team members who then go off and work alone. That\u0026rsquo;s what most teams do. It\u0026rsquo;s easy, but it\u0026rsquo;s not the way it\u0026rsquo;s supposed to work.\nIdeally, the entire team should share the responsibility for all of Sprint deliverables collectively and individually. The Daily Stand-Up should be everyone\u0026rsquo;s opportunity to make sure that their teammates are staying on track, to spot problems and help if they can. If a team member comes to the Stand-Up day after day, reporting that they are still working on the same task that was supposed to take 3 hours, then the Team should right on top of that. Is there anything they can do to help? What\u0026rsquo;s that going to mean for the rest of the Sprint deliverables?\nIt\u0026rsquo;s possible that a team could become so seamless at collaborating that the Daily Stand-Up is no longer necessary, but a team like that would probably find real value in the Stand-Up and keep it.\nCan You Alter or Abandon Scrum? # Certainly, a team can outgrow Scrum. And Scrum is just one way of doing Agile development, not the only way.\nIt\u0026rsquo;s also possible that an organization might become comfortable enough with the results from a team to stop worrying about burndown charts and velocity. It\u0026rsquo;s a matter of trust built up through success.\n","date":"17 December 2021","externalUrl":null,"permalink":"/programming/agile/","section":"Programming","summary":"Stand-ups, Retrospectives, Sprints, Scrum Masters.  Is it just bureaucratic nonsense that gets in the way of good programmers doing their thing?  It shouldn’t be.","title":"What's the Point of Agile?","type":"programming"},{"content":"Kotlin has been described as, \u0026ldquo;The language that Java would have been if it had been designed 25 years later\u0026rdquo;. It\u0026rsquo;s starting to pick up popularity, and has had a boost from being endorsed by Google for Android development.\nBut you\u0026rsquo;re not an Android developer, so should you learn, and use, Kotlin?\nIf you are a Java programmer and \u0026hellip; # You\u0026rsquo;re fed up with Java boilerplate and verbose syntax You think that Lambdas are the greatest thing since Generics You think that Streams are the greatest thing since Lambdas You think that Optional is cool, but at the same time awkward and clumsy You want to move towards more functional programming Then Kotlin is probably for you. And if you\u0026rsquo;re a Java programmer who isn\u0026rsquo;t all of those things above, then maybe Kotlin would be a good way to expand your horizons and learn about new ways to program.\nIt\u0026rsquo;s Not a Steep Learning Curve # Kotlin is very closely related to Java, and there\u0026rsquo;s almost nothing that you can do in Kotlin that you couldn\u0026rsquo;t do in Java as well. So this means that the concepts which are central to Kotlin aren\u0026rsquo;t going to be all that foreign to you if you come from Java. This is especially true if you\u0026rsquo;ve really become comfortable with lambdas, streams and optional; as all of these concepts are central to Kotlin. Unlike Java, where these features were added on decades after the language was invented, Kotlin was designed with them as an integral, integrated part from the very beginning.\nIt probably takes just a couple of hours to learn enough about Kotlin to be able to read and understand most of the Kotlin code you\u0026rsquo;re likely to encounter in real life. And it seems to take just a few days of working in Kotlin to became comfortable writing new code in Kotlin.\nIf you are using Intellij Idea as your IDE, there\u0026rsquo;s even a menu item to convert your Java classes to Kotlin. That\u0026rsquo;s a great start. It doesn\u0026rsquo;t produce great Kotlin code, and it definitely looks like Java code written Kotlin, but it doesn\u0026rsquo;t take too much effort to polish up the converted code so that it looks a lot more like it was written in Kotlin from the start.\nI\u0026rsquo;ve found that you can just take all of the concepts and approaches you\u0026rsquo;d use in Java and just write them in Kotlin and you\u0026rsquo;ll get a working program. Then if you go over your code, looking for the bits that seem awkward and clumsy, you can refactor them by researching the concepts with web searches until they look natural.\nWhat\u0026rsquo;s so Great about Kotlin, Anyways? # Less Code is Better Code # Over decades of programming I\u0026rsquo;ve come to understand that, all other things being equal, the less code you have to write, the better. Why?\nThere\u0026rsquo;s less code to read, so less code to understand. There\u0026rsquo;s less places for a bug to hide in. Only the bits that make your code unique need to be written. Kotlin helps you to write less code. It strips away a LOT of the extra coding that you need to do with Java that, to be honest, no one reads anyways.\nLet\u0026rsquo;s Look at Fields as an Example # In Java we have Fields (or \u0026ldquo;Instance Variables\u0026rdquo;, if you like) in our classes. But we are taught (correctly) that fields are part of the private inner workings of a class and should not be exposed to the world. Exposing them would tie our inner class design down to those client classes that are peeking into our class and making decisions based on its internal design.\nSo we make all of our fields private, and we create two public methods for each field, a getter and a setter. These methods are named following a convention which puts the word \u0026ldquo;get\u0026rdquo; or \u0026ldquo;set\u0026rdquo; in front of the field name, with the first letter capitalized. Typically, it would look like this:\n{% gist a55821e44ddb909e290a05491bfaad4f %}\nShould you decide to change the way that intField is stored, maybe store it as a String, you can keep the external interface constant by providing the conversion operations as part of the getter and setter for that field.\nNow, Kotlin was developed knowing that this is the best way to share your class\u0026rsquo;s data without exposing its inner structure to the outside world. So Kotlin doesn\u0026rsquo;t have \u0026ldquo;Fields\u0026rdquo;, but it has \u0026ldquo;Properties\u0026rdquo;. Think of a Property as an object wrapper around a field which has its own getter and setter. Kotlin automatically creates the class level getter and setter for the field, delegating them to the getter and setter of the encapsulating property. The default getter and setter for a property just do what you would expect them to do.\n{% gist aee9580aad1e0d3a7347eb742f82ddda %}\nYou can see that it takes just 1 line of code to do what takes about 8 in Java.\nBut it gets better! No one would actually write Kotlin code like that.\nIn your Kotlin code, you can replace direct calls to the getter and setter with direct references to the property. It will then invoke the appropriate getter or setter for you. Additionally, the setter is defined as an operator function, so you can just put the property reference on the left side of an \u0026ldquo;=\u0026rdquo; operator and it will turn it into a call to the setter for you. Like this:\n{% gist f0df4dbfb6a5835e093c2983223fb394 %}\nAs a Java programmer this looks scary! It looks like it\u0026rsquo;s doing exactly what we were all taught to never, never, ever do - expose the field and directly access it from an external class. But it\u0026rsquo;s not. It\u0026rsquo;s following all of the rules, to the letter, but just hiding all of the boilerplate so that it doesn\u0026rsquo;t get in the way.\nThe ironic thing is that the resulting code looks exactly the way you would have written your Java before somebody told you not to!\nMutable, Immutable and Nullable # Variables in Kotlin are intialized with the \u0026ldquo;val\u0026rdquo; or \u0026ldquo;var\u0026rdquo; keyword and only allowed to be null when explicitly declared as nullable (with a \u0026ldquo;?\u0026rdquo; at the end of the type). Variables declared with the \u0026ldquo;val\u0026rdquo; keyword are essentially the same as \u0026ldquo;final\u0026rdquo; variables in Java.\nKotlin does everything it can to encourage you to use \u0026ldquo;val\u0026rdquo; over \u0026ldquo;var\u0026rdquo; whenever possible. So much so that \u0026ldquo;var\u0026rdquo; could be considered to be a \u0026ldquo;code smell\u0026rdquo; in Kotlin. What\u0026rsquo;s the big deal?\nUsing vals in your code makes you think about alternative, immutable, functional code. […] Removing vars leads to refactoring. The refactoring leads to new coding patterns. New coding patterns leads to a shift in your approach to programming. This shift in approach leads to transformative code that has fewer defects and is easier to maintain.\n— Beginning Scala, David Pollack\nI have found this to be true. When I look at a block of code and think, \u0026ldquo;This seems clumsy\u0026hellip;wrong\u0026rdquo;, it\u0026rsquo;s usually got a \u0026ldquo;var\u0026rdquo; in it somewhere. Flipping it around and finding an approach that solves the problem without using \u0026ldquo;var\u0026rdquo; often results in something that is elegant and clean compared to my original code.\nNullable variables are everything that Optional in Java should be \u0026hellip; without the clumsiness of Optional. It\u0026rsquo;s an integral part of the language that you need to master, not an add-on capability that few people understand properly.\nGenerally speaking a variable gets a null value in one of two ways, it\u0026rsquo;s a var initialized to null which never gets updated to a real value, or it\u0026rsquo;s a val instantiated via a function that returns a nullable value. It\u0026rsquo;s declared with a \u0026ldquo;?\u0026rdquo; at the end of the class name, like \u0026ldquo;String?\u0026rdquo;, or \u0026ldquo;Int?\u0026rdquo; or \u0026ldquo;CustomClass?\u0026rdquo;. Just like Optional, to get that value out of the variable, you need to provide a way for your program to deal with possibility that there\u0026rsquo;s nothing there.\n{% gist 5059ba75f5b94a77e2ea406f0814f24d %}\nThe assumption is that the external database function returns a nullable Customer value. Both functions do the same thing. The second shows how the val variables can actually collapse into just function calls.\nThe \u0026ldquo;Elvis Operator\u0026rdquo; (\u0026quot;?:\u0026quot;) is used like the orElse() method of Optional. If you want to do something like Optional\u0026rsquo;s map() function, you use the \u0026ldquo;safe operator\u0026rdquo; (\u0026quot;?.\u0026quot;) and then invoke some method of the variable. In the first function, let is an external \u0026ldquo;scope\u0026rdquo; function which takes a lambda argument, passing the value to it as it.\nKotlin also has an !! operator, which works in the same way as Optional.get(). Using !! is generally considered an anti-pattern, and is the only way you can get an NPE with Kotlin.\nScope Functions # How many times have you instantiated a variable, just to perform some configuration on it by calling it\u0026rsquo;s methods, and then passing it on to some other class as a parameter? Something like this:\n{% gist fdbc1af6c4b89a7bf1ac17342961d67e %}\nBut Kotlin has an apply scope function which takes a lambda which receives the value as this and returns this. Inside the scope function, you can treat the this as implied when it makes sense. So that code above becomes:\n{% gist b05ceb242a9f98d350404bf6d07dc752 %}\nSo now we don\u0026rsquo;t even need a variable reference to the object at all, we can just pass the results of the constructor through the apply and directly off to another object\u0026rsquo;s method. In a way, you can use the apply to turn a class\u0026rsquo;s configuration methods into decorators.\nThere are 5 different kinds of scope functions in Kotlin. You can read about them here .\nBy the way, if the multi-line lambda breaks up the flow of your code a bit much, you can very easily turn it into a named function in Kotlin:\n{% gist 1029617ae1412e59a021ab82584942ba %}\nThere\u0026rsquo;s a LOT More # Any attempt to list all of the features of Kotlin, and how they improve upon kludgy Java implementations would just result in a TLDR blog. What you really need to know, though, is the Kotlin takes virtually all of the modern concepts added to Java and integrates them right into the language so that they feel natural and spread everywhere.\nLambdas are a really good example of this. You can get away without lambdas in Java, but you couldn\u0026rsquo;t write much of anything in Kotlin without them. They\u0026rsquo;re a crucial concept to the language, and using them in Kotlin feels completely natural.\nWhat Are the Downsides to Using Kotlin? # From a technical perspective, there are very few. You can freely co-mingle Java and Kotlin in you projects, and you can call any Java library from Kotlin. With a very small amount of care, any of your Kotlin code can be called from Java, and you can use any Kotlin libraries with Java.\nKotlin compiles to run in the JVM, and you can tell the Kotlin compiler to generate code compatible with whatever version of the JVM you want. So it\u0026rsquo;s easy to maintain compatibility between your Java and your Kotlin code in your project.\nAlthough Kotlin has been around for almost a decade now, it\u0026rsquo;s nowhere near as old as Java, and doesn\u0026rsquo;t have the wealth of resources that you are going to find with Java. The programmer community is smaller, so there just aren\u0026rsquo;t as many people to answer your questions on sites like StackOverflow.com, and you\u0026rsquo;re less likely to find that somebody has already asked your question so the answer is just there waiting for you.\nAnother thing you\u0026rsquo;ll need to deal with is that, even though Kotlin can use just about any Java library you might find, virtually all of the examples and the documentation are going to be in Java. You\u0026rsquo;ll probably also find that any questions and answers you find on sites like StackOverflow.com are going to be in Java, too.\nIt\u0026rsquo;s also possible that some of the tooling that you\u0026rsquo;re using right now with Java can\u0026rsquo;t be used with Kotlin, and you may not be able to find a Kotlin compatible replacement. This will undoubtedly improve over time.\nIf you are working on a preexisting code-base with a lot of old Java code, there are going to be practical issues around converting everything to Kotlin. Especially in the absence of a comprehensive test suite. But if you\u0026rsquo;re opening up a class for refactoring or modification, you might have the opportunity to convert it to Kotlin in the process. The resulting code will almost certainly be smaller, cleaner and easier to extend and maintain.\nA Final Thought # One thing about learning Kotlin, though; it will change you. A phrase that you\u0026rsquo;ll hear a lot is \u0026ldquo;idiomatic Kotlin\u0026rdquo;, which means the programming equivalent of \u0026ldquo;speaking like a native\u0026rdquo;. You don\u0026rsquo;t hear about \u0026ldquo;idomatic Python\u0026rdquo; or \u0026ldquo;idomatic BASIC\u0026rdquo; too much, but it seems like \u0026ldquo;idiomatic Kotlin\u0026rdquo; is a key concept of learning the language.\nThere\u0026rsquo;s something about Kotlin, maybe its focus on fixing the bad parts of Java, that makes your bad Kotlin code stick out like a sore thumb. You see it, and you can\u0026rsquo;t help but think, \u0026ldquo;That just doesn\u0026rsquo;t look right!\u0026rdquo;. And, generally speaking, fixing that bad code means writing it in a more idiomatic Kotlin way.\nDo that often enough, and that idiomatic Kotlin way becomes your way to write code, and it changes how you approach writing code.\nAnd then you have to go back to Java to work on some legacy code\u0026hellip;\n","date":"11 December 2021","externalUrl":null,"permalink":"/programming/kotlin/kotlin-should-you/","section":"Programming","summary":"Kotlin has been described as, “The language that Java would have been if it had been designed 25 years later”. It’s starting to pick up popularity, and has had a boost from being endorsed by Google for Android development.\nBut you’re not an Android developer, so should you learn, and use, Kotlin?","title":"Kotlin - Should You?","type":"programming"},{"content":"","date":"24 August 2021","externalUrl":null,"permalink":"/categories/javafx-kotlin/","section":"Categories","summary":"","title":"Javafx Kotlin","type":"categories"},{"content":" JavaFX Properties # In JavaFX, properties and other observable classes are used to implement reactive programming techniques - which is absolutely the best way to design a JavaFX application. Ideally, you should implement your data elements as properties and then use the Binding library to connect the data to your GUI.\nJavaFX Properties in Java # In Java, JavaFX properties should be placed into a Model with a \u0026ldquo;bean\u0026rdquo; structure. This means that you should have the following 3 methods in your Model for each property that you define:\nA delegate getter for the property value A delegate setter for the property value A getter for the property itself There is a naming convention that you should follow, and you can see it in the code snippet below:\n{% gist 3a2e8ae215eaee024386a9867a5e67f1 %}\nPerhaps the most important part is that the getter for the property should have the name \u0026ldquo;{Property Name}Property\u0026rdquo;. There are some built-in parts of JavaFX which will use reflection to access the property and will need need this structure.\nif you are using Intellij Idea, it will create the three methods for you once it\u0026rsquo;s identified the lonely field declaration as an issue.\nThe Property is Final # Notice that the property in the example is has the \u0026ldquo;final\u0026rdquo; qualifier. This is because the property itself, viewed as a container, should never be changed. The contents can change - that\u0026rsquo;s the point - but you should never change the reference of the property itself or you\u0026rsquo;ll lose any bindings or listeners on the property.\nKotlin Getters and Setters # Somewhat confusingly for JavaFX programmers, Kotlin calls class fields (or \u0026ldquo;instance variables\u0026rdquo;), \u0026ldquo;properties\u0026rdquo;. This isn\u0026rsquo;t technically precise, but if you want to know more you can read the Official Kotin Page about it. Properties in Kotlin are wrappers for fields which are hidden from direct access with default getters and setters each property. Kotlin syntax allows the calls to the getters and setters to look like direct references to the properties. Like this:\n{% gist e7e3c38f85227842558e3ebc79f6c9ff %}\nIt\u0026rsquo;s possible to override the default getter and setter for a property, as in this code:\n{% gist 34b03c5530a07313dca2f4c27b4c5785 %}\nIf you run this code, you\u0026rsquo;ll see the following output:\nin the setter in the getter Nickname: Shorty So you can see that, although it looks like you\u0026rsquo;re referencing the field directly, you are actually calling the getter and the setter defined for the property.\nPerhaps even more importantly, though, is that you can access the getter and setter from Java through calls to testClass.setNickName() and testClass.getNickName(). Kotlin automatically handles this for you. By the same token, you can access getters and setters in Java classes from Kotlin using direct references to the fields.\nThe Goal for JavaFX Properties in Kotlin # When creating JavaFX properties in Kotlin, we want them to look externally exactly like they would in Java. Meaning that they have to have the same JavaFX \u0026ldquo;bean\u0026rdquo; structure with the same method names. Of course, in Kotlin the getter and setter need to still work same way that they would for any other Kotlin property - looking like direct access.\nIn this case, however, the Kotlin approach makes things appear to be a little more complex.\nDealing With the \u0026ldquo;Final\u0026rdquo; Qualifier # In Kotlin, variables can either be immutable or mutable, which is set by the use of the \u0026ldquo;val\u0026rdquo; or \u0026ldquo;var\u0026rdquo; keywords when the variable is defined. Immutable variables, defined with the \u0026ldquo;val\u0026rdquo; keyword cannot have setters. This is an issue because, while you can always override the default getter to delegate to the property getValue() method, you can\u0026rsquo;t even define a setter for a \u0026ldquo;val\u0026rdquo; variable.\nThis makes perfect sense, but it means that we need to use a slightly roundabout route to implement a JavaFX \u0026ldquo;bean\u0026rdquo;.\nAn Approach That Works # A good answer seems to be a fairly common design pattern in Kotlin, and that\u0026rsquo;s to use a hidden backing property. In this case, the hidden backing property is the actual JavaFX property type that we are going to use. There\u0026rsquo;s also a public property, which has the same type as the data contained in the JavaFX property, and which delegates it\u0026rsquo;s getters and setters to the hidden properties, get() and set() methods.\nFinally, a method is added to the Model to return the JavaFX property itself, using the same naming convention that you would use in Java.\nThe effect is that the publicly visible property will never actually have any data assigned to it, since it\u0026rsquo;s setter is overridden to put the data into the hidden backing JavaFX property. It only exists to present a public interface to the methods in the JavaFX property.\nThe result looks like this:\n{% gist f6d3bd9dfc261b6d411d7945172d5833 %}\nAnd the output looks like this:\nNickname: Shorty Property: ObjectProperty [value: Shorty] It seems to be the convention in the examples I\u0026rsquo;ve seen to use \u0026ldquo;_\u0026rdquo; prefix for hidden properties, as seen in that same official Kotlin page - in the section on Backing Properties . So I\u0026rsquo;ve followed that here.\nYou should also note that, even though it seems quite verbose, this is actually a little less code than you\u0026rsquo;d need to implement a JavaFX bean in Java. Which is pretty normal for Kotlin.\nAn Added Bonus # If you\u0026rsquo;re using Intellij Idea, like I do, then you might find the following like template useful:\n{% gist d301cb308d2b4741220c6146cf3ba69c %}\nJust give it a short name (I use \u0026ldquo;fxprop\u0026rdquo;) and it\u0026rsquo;ll make your life a lot easier.\nI\u0026rsquo;ve only implemented it as ObjectProperty\u0026lt;{Type}\u0026gt; because that works best. StringProperty might be useful, but the Number based properties like IntegerProperty and DoubleProperty have type issues when you get into complicated stuff. So they seem easier to use, but ObjectProperty\u0026lt;Int\u0026gt; is almost always a better choice than `IntegerProperty``.\n","date":"24 August 2021","externalUrl":null,"permalink":"/programming/kotlin/kotlin_properties/","section":"Programming","summary":"An approach to implementing the JavaFX Property “bean” structure for observable objects in an idiomatic Kotlin fashion.","title":"Kotlin: JavaFX Properties","type":"programming"},{"content":"Comments in code are supposed to help other programmers understand your code so that they can build on it, improve it and, sometimes, fix it. Are the comments that you\u0026rsquo;re leaving in your code helping anyone to do that?\nFor nearly 40 years now, I\u0026rsquo;ve made my living as a programmer, and that means reading other people\u0026rsquo;s bad code. Including my own. The truth is that most of us won\u0026rsquo;t always have the luxury of \u0026ldquo;greenfield\u0026rdquo; programming and that means that, sooner or later, you\u0026rsquo;re going to have to do maintenance work on some code that someone else wrote years ago.\nI\u0026rsquo;ve done this a lot. I\u0026rsquo;m good at it. I\u0026rsquo;ve probably looked at millions of lines of other people\u0026rsquo;s code, trying to figure out what it does, how to fix it, and how to enhance it. It can be hard - harder than writing new code - and I\u0026rsquo;ve known quite a few good programmers who never managed to master it. For a lot of my career I\u0026rsquo;ve been the on-site expert on the systems, and there hasn\u0026rsquo;t been anybody to go to if I couldn\u0026rsquo;t figure something out. So I\u0026rsquo;ve had no choice but learn how to read other programmers\u0026rsquo; code, and to understand what it\u0026rsquo;s doing and how it\u0026rsquo;s doing it.\nI\u0026rsquo;m Probably Going to Delete Your Comments # The first thing I do when I see a program with a lot of comments in it is to delete the comments.\nWhy would I do that?\nWell, first of all, I\u0026rsquo;m not trying to be mean about it, or disrespect the programmer who took the time to write all of the those comments that they were sure would be helpful later on.\nWhen I open up a program to do some maintenance on it I\u0026rsquo;m extremely focused on what I need to do. I\u0026rsquo;m not interested in critiquing somebody\u0026rsquo;s code, or making a philosophical point.\nI\u0026rsquo;m just there to understand it just enough so that I can do what I need to do. Sometimes, that means doing cleanup on the code so that it\u0026rsquo;s in a state that I can work with it efficiently. And that may mean deleting most, if not all, of the comments.\nMost importantly, the comments get in the way.\n: They take up screen space that reduces how much executable code I can see at once. That\u0026rsquo;s a big factor when you\u0026rsquo;re trying to understand some complicated logic or follow some spaghetti code. For instance, I like to be able to see an entire \u0026ldquo;if\u0026rdquo; statement all at once - the condition, along with both of the \u0026ldquo;then\u0026rdquo; and \u0026ldquo;else\u0026rdquo; clauses. If the comments are causing the last lines of the \u0026ldquo;else\u0026rdquo; clause to drop off the bottom of the screen, there\u0026rsquo;s an easy way to fix that. Delete the comments.\nBeyond that, I don\u0026rsquo;t trust comments.\n: Nobody should. By definition they don\u0026rsquo;t execute, so there\u0026rsquo;s no guarantee that they are correct. What if the program has changed since they were written and nobody bothered to update the comments? What if the comments were never correct? The truth is that you don\u0026rsquo;t know, you can never know, and you\u0026rsquo;re better off understanding the code itself than relying on dubious comments - and all comments are dubious.\nWhen I really need to understand some complicated program code, I usually do the following:\nDelete the comments Delete any commented-out code Reformat the code so that it\u0026rsquo;s easier for me to read Rename variables and methods that aren\u0026rsquo;t clear Pull giant blocks of code out into their own methods Split long conditions and expressions into smaller parts and assign them to variables Put brackets around clauses in conditions and long expressions Fix anything else that confuses me Unless I\u0026rsquo;m really reworking an entire program, I probably won\u0026rsquo;t commit any of these changes back into the code base. The idea is just to get the code into a state where I can understand it, then I\u0026rsquo;ll usually revert those changes and make my fix or enhancement to the original version. I\u0026rsquo;m not looking to turn a small fix into a testing nightmare.\nYou may disagree with all of this. Fair enough, but it\u0026rsquo;s a safe bet that any programmer who\u0026rsquo;s had a lot of experience in maintenance programming, and who\u0026rsquo;s good at it, would have developed a system something like this. They\u0026rsquo;re certainly not going to read your comments. And if we\u0026rsquo;re not going to read your comments, why are you putting them in your code?\nIf Not Comments, Then What SHOULD You Do? # Basically, you should write your code so that it looks like it would after I\u0026rsquo;ve done the things listed above.\nReally though, what this means is that you should write your code so that it\u0026rsquo;s clear and easy for anyone in the future to understand what it\u0026rsquo;s doing without comments.\nThis often boils down to developing two crucial skills: naming things, and putting your code in the right place.\nNaming Things # For some reason, naming things is super hard. People have even invented schemes like \u0026ldquo;BEM\u0026rdquo; to help programmers figure out how to name stuff. Following rules can help, but the only thing that really matters is that the particular name of a particular thing in a particular context makes it easy to understand what the code does.\nThere\u0026rsquo;s really only one rule about naming things in programs: Pick the simplest name which makes it easy for someone to understand the most important thing about what an element is, and how it will be used.\nHere are some things to think about when naming anything:\nNames Should Be Meaningful\nA name should explain what something is, what it\u0026rsquo;s for or what it does. It needs to be only specific enough to differentiate it from other things like it.\nAs an example; you might have a method for retrieving a customer record from a database using an account number that HAS to return a value (like if the account number is referenced in an invoice). You could call it \u0026ldquo;fetchCustomerRecord\u0026rdquo;. But you might have a different method that returns on Optional of the customer record for those cases where the account number might no be on file (if it comes from a user typing in an account number). You might call that method, \u0026ldquo;fetchCustomerRecordIfPresent\u0026rdquo;.\nNames Should Be Simple\nNever forget that people need to be able to read your code, and people have trouble coping with long or complex names for things. As soon as a name starts to look like machine code, people\u0026rsquo;s brains tend to stop processing them. So, if possible, don\u0026rsquo;t use leading or trailing underscores. Don\u0026rsquo;t use dollar signs, or percentage signs or carets or other weird, meaningless symbols in your names unless you have to.\nDecide if you\u0026rsquo;re are going to use acronyms to help keep the names short, decide whether or not you want them to be all upper case inside your names and then be consistent. Acronyms can be OK if they\u0026rsquo;re commonly understood in the business domain and aren\u0026rsquo;t going to cause confusion in your code base. All caps can be problematic if you are using camel case in your names, as they can obscure the beginning of the next word.\nNames should only be as long as they need to be. Longer names are problematic with formatting your code, and that can be a significant barrier to people understanding your logic.\nContext and Scope Are Important\nGenerally speaking, a name only has to make sense within the scope and context that it\u0026rsquo;s going be used in. For instance, using a variable name like \u0026ldquo;results\u0026rdquo;, or \u0026ldquo;returnValue\u0026rdquo; in a method that returns a value is generally helpful, even though it doesn\u0026rsquo;t appear to describe what\u0026rsquo;s held in the variable. That\u0026rsquo;s because it describes the most important thing about that variable in that context - that this is the value that\u0026rsquo;s going to be returned by the method. If I look at a method and see variable instantiated on the very first line called, \u0026ldquo;results\u0026rdquo;, I know that this is the element I have to pay close attention to.\nIn the same vein, private methods in a class can have names that only make sense within the context of other methods in the class. That\u0026rsquo;s because the name of the method is intended to explain what\u0026rsquo;s happening in the code that calls it. So a method name like, \u0026ldquo;createModelFromInvoice\u0026rdquo;, can be fine because in any place that it might be called, \u0026ldquo;model\u0026rdquo; and \u0026ldquo;invoice\u0026rdquo; are probably going to be clearly understood.\nPutting Your Code in the Right Place # When code is in the right place, it\u0026rsquo;s so much easier to understand.\nImportant This is a vital concept: when code is in the right place, often you won\u0026rsquo;t even need to understand it.\nWhy? Because when it was in the wrong place, it was right in the middle of the code you did need to understand. Once it\u0026rsquo;s moved to somewhere else and properly named, you may not even need to look at it any more.\nThe Single Purpose Principle # Every chunk of code that you write should do one thing, and one thing only.\nThat \u0026ldquo;chunk\u0026rdquo; might be a block of code, a method or a class. A good way to think about this is to try to have each \u0026ldquo;chunk\u0026rdquo; designed so that it can be described completely in a simple phrase. If the thing that it does is complicated, then you can probably divide it up into smaller pieces, each of which in turn can be described completely in a simple phrase. Repeat as necessary.\nAs an example think about a method called calculateTotalSale(), which should have a simple description of \u0026ldquo;calculate the total sale amount\u0026rdquo;. So far so good. But if part of that method has to:\ndetermine taxes by looking up the state look up the tax rate separate out the taxable from non-taxable items calculate shipping calculate handling charges total it all up Now \u0026ldquo;calculate the total sale amount\u0026rdquo; doesn\u0026rsquo;t completely describe the method, does it?\nThe best thing might be to pull the sales tax code out of calculateTotalSale() and put it into its own method, calculateSalesTax(), which \u0026ldquo;calculates the sales tax\u0026rdquo;. But does it just \u0026ldquo;calculate the sales tax\u0026rdquo;? Not really. It\u0026rsquo;s probably best to pull out the state lookup code into a method called fetchStateTaxRate(), and then create another method called determineTaxableItems(), and so on. What\u0026rsquo;s left in calculateSalesTax() is just the logic that puts it all together, calling the other methods, which each have a single purpose, to do the work that their names describe.\nAnd now, when someone comes along and needs to implement some kind of change, maybe a new rounding method, they don\u0026rsquo;t need to understand all the details of how sales tax rates are determined, because it\u0026rsquo;s not sprinkled in with all of the other code that you do need to look at to make the change.\nThe Information Expert Principle # The Information Expert Principle is one of the patterns defined in GRASP.\nProblem: What is a basic principle by which to assign responsibilities to objects? Solution: Assign responsibility to the class that has the information needed to fulfill it.\nIt\u0026rsquo;s not unusual to see code like this:\nint thresholdValue = 12; . . . double answer = 0d; if (object.getSomeValue() \u0026gt; thresholdValue) { answer = (object.getValue1() + object.getValue2()) / object.getValue23(); } else { if (object.isSomething()) { answer = object.getValue22() * object.getValue17(); else { answer = 87.45; } } Everything after the setting of the the thresholdValue uses information from object, and probably should be the responsibility of object to calculate. It shouldn\u0026rsquo;t be where ever it currently is sitting.\nFar better would be to create a method in whatever class object is and pass it the threshold as a parameter. Something like this:\npublic double calculateSomething(int thresholdValue) { double answer = 0d; if (someValue \u0026gt; thresholdValue) { answer = (value1 + value2) / value23; } else { if (isSomething()) { answer = value22 * value17; else { answer = 87.45; } } return answer } And then the original code would be refactored to:\nint thresholdValue = 12; . . . answer = object.calculateSomething(thresholdValue); There\u0026rsquo;s also a good chance that three other things will come out of this refactoring:\nSome or all of the getters like object.getValue22() will no longer be needed and can be deleted. The logic from calculateSomething() is duplicated elsewhere in the system and can be replaced with a call to calculateSomething() calculateSomething() is now testable, all by itself. But most important is that the logic that is now in calculateSomething() is not cluttering up the other code any more. It\u0026rsquo;s just one line answer = object.calculateSomething(thresholdValue). And, of course, it\u0026rsquo;s not called \u0026ldquo;calculateSomething\u0026rdquo;, it\u0026rsquo;s got a meaningful name which describes it properly, so that when you read it you can tell at a glance what it does. That\u0026rsquo;s an important point, because it may not have been clear from that block of code what it was trying to do.\nSo when you read this code, you\u0026rsquo;ll know right away if you care about what object.calculateSomething() is doing, and if you need to look into it to finish my task. Probably, you won\u0026rsquo;t, and you will have saved yourself the effort of opening up what ever class object belongs to and verifying that someValue, someValue1, isSomething and all the other fields mean what you think they mean. You won\u0026rsquo;t have to confirm that there\u0026rsquo;s not some hidden side-effect in that block of code, either.\nAn Exception to the Information Expert Principle # If you have a class which is a JBOD or some kind of a data model that fits into a framework, you probably won\u0026rsquo;t want to have business (or application, if you like) logic baked into that data class. Business logic generally belongs in a specific place where you know to go and find it, without having to hunt around in a slew of data classes.\nEven if you have to create some relationship between elements in a JBOD, you shouldn\u0026rsquo;t define that relationship inside the class if it comes anywhere close to being business logic. For instance you could have a Rectangle class that has height and width fields. Maybe you could have a method called getArea() because that might be considered sufficiently generic to be included in a data model of that type. But if you had some business logic that required determining if the height was 2.8 times the width, then adding a isRectangleHighEnough() method would probably be a bad idea.\nGeneral Readability Issues # Creating Local Variables for Readability # Sometimes you\u0026rsquo;ll come across something like this:\nif (theList.get(listIndex).retrieveAValue(xyz) \u0026lt; someValue) { . . . } It might not be clear from the code what that value means in the context in which it\u0026rsquo;s being used. Worse even\u0026hellip;\nif ((theList.get(listIndex).retrieveAValue(xyz) * 0.754) \u0026lt; otherList.get(index2).retrieveSomething(abc)) { . . . } That\u0026rsquo;s not just ugly, it\u0026rsquo;s hard to understand, too.\nLast example:\nif ((theList.get(listIndex).retrieveAValue(xyz) * 0.754) \u0026lt; otherList.get(index2).retrieveSomething(abc)) { . double something = theList.get(listIndex).retrieveAValue(xyz) / 2; . } Here we\u0026rsquo;ve used that retrieved value twice, which means that you have to check that code twice to make sure that it really is the same thing. What if the second use passed \u0026ldquo;xjz\u0026rdquo; to retrieveAValue()? Would you catch it?\nFar better would be something like this:\ndouble xyzValue = theList.get(listIndex).retrieveAValue(xyz); double somethingRatio = 0.754; double contextualThreshold = otherList.get(index2).retrieveSomething(abc); if ((xyzValue * somethingRatio) \u0026lt; contextualThreshold) { . double something = xyzValue / 2; . } This is a few more lines of code, but the result is way easier to understand, and it\u0026rsquo;s now perfectly clear that xyzValue is used twice. On top of that, the variable names should clearly state what all of those values are and what they mean in this context. No comments are required now because the code has been designed to make it clear what\u0026rsquo;s going on.\nUsing Variables to Explain Conditions # That last example still has one possible issue. What\u0026rsquo;s that xyzValue * somethingRatio? This can become really important when a condition has multiple clauses, especially cases where logical and\u0026rsquo;s and or\u0026rsquo;s are mixed together. It\u0026rsquo;s much clearer to break out the clauses as booleans and give them meaningful names. Something like this:\nboolean hasEnoughMojo = (someValue \u0026gt;= thresholdValue); boolean isThursday = dayOfWeek.equals(CalendarTools.getWeekDay(4)); boolean isQualified = certificateList.contains(courseName); if ((hasEnoughMojo || isThursday) \u0026amp;\u0026amp; isQualified) { . . . } This is helpful for two reasons. First, the if statement is now super simple to read. With the conditions broken out, it\u0026rsquo;s easy to see how the conditions fit together. Secondly, it\u0026rsquo;s clear what the individual conditions mean - because they have names!\nThere\u0026rsquo;s an extra bonus value here, too. Look at that variable isQualified. That name speaks to intent. It asks, \u0026ldquo;Is it qualified\u0026rdquo;? What if, upstream, the API that supplied certificateList is changed to include current courses? Would that lookup function now mean \u0026ldquo;qualified\u0026rdquo;? Without that intent baked into the code, it\u0026rsquo;s impossible to know whether or not there\u0026rsquo;s an issue here. The way it is, when you realize that certificateList now contains certificates in progress, you can go and ask the business whether or not that counts as \u0026ldquo;qualified\u0026rdquo;.\nUnnecessary Local Variables: # Sometimes the opposite is required. I see this all the time:\npublic void start(Stage primaryStage) throws Exception { primaryStage.setTitle(\u0026#34;window name \u0026#34;); BorderPane layout = createLayout(); Scene scene = new Scene(layout, 300, 250); primaryStage.setScene(scene); primaryStage.show(); } This just takes up space on the screen. The variables scene and layout add nothing. This will work just as well:\npublic void start(Stage primaryStage) throws Exception { primaryStage.setTitle(\u0026#34;window name \u0026#34;); primaryStage.setScene(new Scene(createLayout(), 300, 250)); primaryStage.show(); } Generally speaking, if something is boilerplate then you can assume that any programmer looking at it is going to understand what it does and you should feel free to compress it as much as possible without causing confusion. I use Intellij\u0026rsquo;s \u0026ldquo;inline variable\u0026rdquo; function all the time.\nTip It\u0026rsquo;s a good rule of thumb that less code is going to be easier to understand than more code.\nWhen in doubt, write less code. Don\u0026rsquo;t try to be clever about it - that has the opposite effect - but when something can be done clearly in one line of code, don\u0026rsquo;t do it in two lines.\nIn Conclusion # If I open up a program and see that it has just one or two comments, then I\u0026rsquo;ll usually take the time to read those comments and see how they relate to the code. Because, even if the code isn\u0026rsquo;t stylistically what I like, the fact that there\u0026rsquo;s only a couple of comments is big clue that the programmer understood what comments are for and they are, therefore, worth reading.\nComments do have a useful role when they are employed to explain things that can\u0026rsquo;t adequately be made clear through the executable code itself. But they really should be a last resort only.\nImportant Here\u0026rsquo;s the big benefit from avoiding comments: It\u0026rsquo;s harder to write bugs when your first priority is to write clear simple code that is easy to understand without comments.\nIt\u0026rsquo;s true. I don\u0026rsquo;t know how many times I\u0026rsquo;ve discovered bugs in old code just by doing all those refactorings I listed up near the top. Without even getting to the point of even understanding what the code does!\nAnd if you write your code the same way from the start, you\u0026rsquo;ll find those bugs yourself as you are writing the programs.\n","date":"24 August 2021","externalUrl":null,"permalink":"/programming/code-comments/","section":"Programming","summary":"Comments in code are supposed to help other programmers understand your code so that they can build on it, improve it and, sometimes, fix it.  Are the comments that you’re leaving in your code helping anyone to do that?","title":"Are Your Code Comments Helping Anyone?","type":"programming"},{"content":"I decided to try writing a Hangman game that would look up words from the internet, just too see how much code it would actually take. The simplicity of the result surprised even me and it points out just how powerful reactive programming can be.\nThe Inspiration # I came across a question on the Internet from someone trying to create a virtual keyboard (for a Hangman game) that looked a bit like a physical keyboard, with each row of keys a little offset from the one above it. The person asking the question was trying to use a GridPane, which seems like it might be logical, but that became complicated because to create the offsets you needed to have each key span two columns so that you could skip the first column on the second row, and so on.\nIt turns out it\u0026rsquo;s extremely simple to write with just a few lines of code by using a VBox with the keys in three HBoxes. Then you can provide the offsets, by adding some padding to the left of the 2nd and 3rd HBoxes.\nThe code that was posted by the person asking the question was really, really complicated - apart from the GridPane - and I was struck by how really simple the code I had written was in comparison. So I asked myself, \u0026ldquo;Just how little code would it take to write the whole game?\u0026rdquo;\nHangman - The Rules # Hangman is a pretty simple game where you try find a hidden word by guessing at letters that might be in it. There\u0026rsquo;s a picture of a noose, and every time you guess a letter that isn\u0026rsquo;t in the word, a new body part is added to the picture. First, a head, then a body then the legs and so on. When the entire figure has been drawn, if you haven\u0026rsquo;t guessed the word, you lose. Usually, when you\u0026rsquo;re playing the game with paper and pens, the hangman is just a stick figure. Generally speaking, there are 7 body parts, so you get 7 wrong letter guesses before you lose.\nThe Program Structure # I decided to use an MVCI structure (MVC plus an Interactor to hold the game logic), because that\u0026rsquo;s generally best when doing anything non-trivial.\nThe Model # This is the place to start, because the Model really describes how the game logic is going to be translated over to the GUI.\nIf you think about it for a little bit, there\u0026rsquo;s really only two pieces of data required for the game; a word, and a list of letters that have been guessed. Every other aspect of the game play can be derived from just those two things. Furthermore, the word isn\u0026rsquo;t really going to be treated like a word, it\u0026rsquo;s just an ordered list of letters.\nSo the first two elements of the Model need to be ObservableLists; one with the letters of the word, and the other with the letters that have been guessed.\nThe next thing we need for the GUI is the number of wrong letters that have been guessed. This is just a count of all of the letters in guessed list that aren\u0026rsquo;t contained in the word list. Since this is game logic, it can\u0026rsquo;t be done in the View, but needs to be included in the Model so that it can be observed by the View. In the Model, we\u0026rsquo;ll create an IntegerProperty to hold this value.\nFinally, we need a BooleanProperty to hold whether the game has been won, and another to hold whether it has been lost.\nThis is what the fields in the Model look like:\nprivate final ObservableList\u0026lt;String\u0026gt; pickedLetters = FXCollections.observableArrayList(); private final ObservableList\u0026lt;String\u0026gt; word = FXCollections.observableArrayList(); private final IntegerProperty wrongLetterCount = new SimpleIntegerProperty(0); private final BooleanProperty gameWon = new SimpleBooleanProperty(false); private final BooleanProperty gameLost = new SimpleBooleanProperty(false); Of course, the Model will also contain all of JavaFX Bean methods for these fields.\nThe Interactor # The game play for Hangman is pretty simple. There are just two actions; one to reset the hidden word, and another to add a letter to the list of guessed letters. So the Interactor has to support those two actions, and set up the Model with the relationships between those two lists and the three other fields.\nBindings in the Model # Let\u0026rsquo;s look at the Model setup first. It\u0026rsquo;s done in the constructor of the Interactor, which is provided a reference to the Model by the Controller when it\u0026rsquo;s instantiated:\npublic HangmanInteractor(HangmanModel model) { this.model = model; this.model.gameLostProperty().bind(model.wrongLetterCountProperty().greaterThan(5)); this.model.gameWonProperty() .bind(Bindings.createBooleanBinding(() -\u0026gt; hasGameBeenWon() \u0026amp;\u0026amp; !model.isGameLost(), model.getPickedLetters(), model.gameLostProperty(), model.getWord())); this.model.wrongLetterCountProperty().bind(Bindings.createIntegerBinding(this::countWrongLetters, this.model.getPickedLetters())); } private int countWrongLetters() { return Math.toIntExact(model.getPickedLetters().stream().filter(letter -\u0026gt; !model.getWord().contains(letter)).count()); } private boolean hasGameBeenWon() { return (model.getWord().stream().allMatch(model.getPickedLetters()::contains)); } The first element to look at is the wrongLetterCount property. It\u0026rsquo;s created via Bindings.createIntegerBinding(). There\u0026rsquo;s a method called countWrongLetters() which streams the pickedLetters List, filters out the ones that are in the word, and then counts them. The Binding is triggered every time the pickedLetters List changes.\nThe gameLost property is bound to the wrongLetterCount property, checking to see if it is greater than 5.\nThe gameWon property is calculated by streaming through the word List, and checking that each letter in the word is also in the pickedLetters List. Recalculation is triggered when either of the two letter lists is changed.\nGame Play Methods # There\u0026rsquo;s only two actions in the game play; when a letter is selected, and restarting a new word. Here\u0026rsquo;s the code for both:\nvoid processLetter(String letter) { if (!model.isGameWon() \u0026amp;\u0026amp; !model.isGameLost()) { model.getPickedLetters().add(letter); } } void newWord() { model.setWord(wordAsList(words.remove(0).toUpperCase())); model.getPickedLetters().clear(); } private List\u0026lt;String\u0026gt; wordAsList(String word) { return Arrays.asList(word.split(\u0026#34;\u0026#34;)).stream().filter(letter -\u0026gt; !letter.isEmpty()).collect(Collectors.toList()); } That\u0026rsquo;s all pretty simple. The action for selecting a letter is just to add it to the pickedLetters List and then the Bindings will do the rest, updating the other three properties. The if statement around the action just prevents the addition of new selected letters after the game is over.\nThe newWord() method just clears the pickedLetters list and converts a new word string into an array of letters that are used to replace the contents of the word List.\nGetting Words # But where does the array of words come from? A little Googling found a REST API on herokuapp.com that supplies random words. Rather than hit it each time to get a new word, it seemed more efficient to grab 100 words, pull only those with less that 10 letters and put them in a list. The API is hit once when the application is started, and the initial list just has the word \u0026ldquo;HANGMAN\u0026rdquo; in it until the API responds - that way it\u0026rsquo;s not empty if the user starts to play before the API can return a list.\nIf anyone feels the need to play Hangman more than about 100 times in a row, they\u0026rsquo;ll run out of words. It didn\u0026rsquo;t seem worth programming around this, and probably easier just to increase the number of words retrieved from the API to 200 or 1000 if it\u0026rsquo;s a problem.\nHere\u0026rsquo;s the code to retrieve the words from the REST API, note that this code is expected to run on a background thread managed by the Controller:\nvoid fetchWords() { try { HttpRequest request = Unirest.get(\u0026#34;https://random-word-api.herokuapp.com/word\u0026#34;).queryString(\u0026#34;number\u0026#34;, \u0026#34;100\u0026#34;); Type collectionType = new TypeToken\u0026lt;Collection\u0026lt;String\u0026gt;\u0026gt;() {}.getType(); ArrayList\u0026lt;String\u0026gt; wordArray = new Gson().fromJson(request.asString().getBody().toString(), collectionType); words = wordArray.stream().filter(word -\u0026gt; word.length() \u0026lt; 10).collect(Collectors.toList()); } catch (Exception e) { e.printStackTrace(); } } The code uses konghq.com\u0026rsquo;s unirest-java and unirest-object-wrappers-gson libraries to handle the REST and JSON work.\nThe Controller # Here\u0026rsquo;s the entire Controller, since it\u0026rsquo;s pretty simple:\npublic class HangmanController { HangmanInteractor interactor; HangmanModel model; Builder\u0026lt;Region\u0026gt; viewBuilder; public HangmanController() { model = new HangmanModel(); interactor = new HangmanInteractor(model); viewBuilder = new HangmanViewBuilder(model, interactor::processLetter, interactor::newWord); fetchWords(); } private void fetchWords() { Task\u0026lt;Void\u0026gt; fetchTask = new Task\u0026lt;\u0026gt;() { @Override protected Void call() { interactor.fetchWords(); return null; } }; fetchTask.setOnSucceeded(evt -\u0026gt; interactor.newWord()); Thread fetchThread = new Thread(fetchTask); fetchThread.start(); } public Region getView() { return viewBuilder.build(); } } The constructor instantiates the Model, View Builder, and Interactor, then launches a background task to go fetch the words list from the REST API at herokuapp.com. There\u0026rsquo;s a getter for the View which triggers the build() method in the View Builder.\nA Task is used to do the background processing to fetch the words from the internet. When the background task is completed, it calls the Interactor\u0026rsquo;s newWord() method which launches a new game.\nThe View # Now all of the game mechanics have been completed, the Model has been set up, the game play actions are all handled and we can even retrieve a list of words from the Internet. All that remains is the GUI.\nThe GUI needs 4 components:*\nA virtual keyboard A hanged man Win/Lose indicators Puzzle word I chose to use a BorderPane to hold the GUI because it seemed like a natural fit for a screen that has been divided up into logical sections. It should be possible to achieve something similar with just VBoxes and HBoxes, though.\nIt\u0026rsquo;s important to remember that the GUI is just strictly a GUI. It doesn\u0026rsquo;t contain any game logic at all, but simply handles the visual presentation of the game, and the interaction with the user. One other thing that you\u0026rsquo;ll notice is that none of the parts of the GUI have any direct interaction with any of the other parts. I could have written a separate class for each part, just to make the point.\nThe Virtual Keyboard # The virtual keyboard is a separate class, mostly because it has enough code in it that it makes sense to split it out and thereby keep the main GUI code cleaner. Also, this was the part that I started with to see how it could be done, and I like the idea of having it as a separate, reusable part.\nThe layout is super simple. It\u0026rsquo;s a VBox, and the VBox holds a set of HBoxes. Each HBox is a row of keys. The last row has the \u0026ldquo;Restart\u0026rdquo; button, which is a little special. Each row has a little more left padding than the one above it. In the spirit of DRY, the creation of the buttons and the HBox rows are delegated to methods which are called repeatedly. So there\u0026rsquo;s no duplicated code.\nThe letter Buttons and the \u0026ldquo;Restart\u0026rdquo; button are pretty much identical, except for the action an the \u0026ldquo;Restart\u0026rdquo; button. So the instantiation and formatting of the buttons is all done in the createButton() method.\nThe virtual keyboard takes two parameters in its constructor, one is a Consumer\u0026lt;String\u0026gt; which will handle the letter button clicks, and the other is a Runnable which will handle the restart. These are invoked via onAction event of each Button. The virtual keyboard has no knowledge of what these elements do, and it does not need to know. Besides invoking the Consumer and the Runnable, the onAction event handlers on the letter Buttons disables the button, while the \u0026ldquo;Restart\u0026rdquo; button re-enables all the buttons.\nHere\u0026rsquo;s the code for the Virtual Keyboard:\npublic class VirtualKeyboard extends VBox { private static final List\u0026lt;String\u0026gt; row1Keys = List.of(\u0026#34;Q\u0026#34;, \u0026#34;W\u0026#34;, \u0026#34;E\u0026#34;, \u0026#34;R\u0026#34;, \u0026#34;T\u0026#34;, \u0026#34;Y\u0026#34;, \u0026#34;U\u0026#34;, \u0026#34;I\u0026#34;, \u0026#34;O\u0026#34;, \u0026#34;P\u0026#34;); private static final List\u0026lt;String\u0026gt; row2Keys = List.of(\u0026#34;A\u0026#34;, \u0026#34;S\u0026#34;, \u0026#34;D\u0026#34;, \u0026#34;F\u0026#34;, \u0026#34;G\u0026#34;, \u0026#34;H\u0026#34;, \u0026#34;J\u0026#34;, \u0026#34;K\u0026#34;, \u0026#34;L\u0026#34;); private static final String restart = \u0026#34;Restart\u0026#34;; private static final List\u0026lt;String\u0026gt; row3Keys = List.of(\u0026#34;Z\u0026#34;, \u0026#34;X\u0026#34;, \u0026#34;C\u0026#34;, \u0026#34;V\u0026#34;, \u0026#34;B\u0026#34;, \u0026#34;N\u0026#34;, \u0026#34;M\u0026#34;); private final Consumer\u0026lt;String\u0026gt; keystrokeConsumer; private Runnable restartHandler; private List\u0026lt;Button\u0026gt; allButtons = new ArrayList\u0026lt;\u0026gt;(); public VirtualKeyboard(Consumer\u0026lt;String\u0026gt; keystrokeConsumer, Runnable restartHandler) { this.keystrokeConsumer = keystrokeConsumer; this.restartHandler = restartHandler; getChildren().addAll(createRow(row1Keys, 0d, false), createRow(row2Keys, 20d, false), createRow(row3Keys, 40d, true)); } private HBox createRow(List\u0026lt;String\u0026gt; letters, Double leftPadding, boolean includeRestart) { HBox hBox = new HBox(); hBox.getChildren().addAll(letters.stream().map(this::buttonSetup).collect(Collectors.toList())); if (includeRestart) { hBox.getChildren().add(createRestartKey()); } hBox.setPadding(new Insets(0, 0, 0, leftPadding)); return hBox; } private Node buttonSetup(String letter) { Button button = createButton(letter); button.setOnMouseClicked(mouseEvent -\u0026gt; { keystrokeConsumer.accept(letter); button.setDisable(true); }); allButtons.add(button); return button; } private Button createButton(String letter) { Button button = new Button(letter); button.setMinSize(60.0, 60.0); button.setStyle(\u0026#34;-fx-font-size: 22; -fx-font-weight: bold;\u0026#34;); return button; } private Node createRestartKey() { Button button = createButton(\u0026#34;Restart\u0026#34;); button.setOnAction(evt -\u0026gt; { allButtons.forEach(eachButton -\u0026gt; eachButton.setDisable(false)); restartHandler.run(); }); return button; } } The Hanged Man # This was probably the trickiest part of the whole screen. How to cycle through a series of 7 images as the number of wrong letters grows? A little bit of googling came up with the following image:\nI liked the look of this, but I wasn\u0026rsquo;t thrilled with the idea of splitting it up manually to make 7 images out of it. It turns out that you don\u0026rsquo;t need to! The JavaFX ImageView class has a property called viewport, and you can manually manipulate this to show only part of an image. You can set the viewport by creating a Rectangle2D object with the correct size and positioning and use that. In order to handle the whole thing, I created a List of Rectangle2D, each with the correct offset, then bound the Viewport property of the ImageView to it.\nI\u0026rsquo;ve since found out that this image is called a \u0026ldquo;Sprite\u0026rdquo;, and is commonly used in gaming to provide animation to characters that move around the screen.\nThe binding itself is interesting. It\u0026rsquo;s from the Bindings library and it\u0026rsquo;s called valueAt(). You give it an Observable List, and an Observable Integer and it will select the item in the List that corresponds to the Observable Integer. It\u0026rsquo;s simple, and it\u0026rsquo;s clean. I use the wrongLetterCount property from the Model to control the Viewport.\nHere\u0026rsquo;s the code:\npublic Node createHangedMan() { Image image = new Image(\u0026#34;/images/hangman.png\u0026#34;); ImageView imageView = new ImageView(image); double cellWidth = image.getWidth() / 7; List\u0026lt;Rectangle2D\u0026gt; clippingShapes = IntStream.range(0, 7) .mapToObj(i -\u0026gt; new Rectangle2D(i * cellWidth, 0, cellWidth, image.getHeight())) .collect(Collectors.toList()); imageView.viewportProperty().bind(Bindings.valueAt(FXCollections.observableList(clippingShapes), model.wrongLetterCountProperty())); StackPane results = new StackPane(imageView); results.setPadding(new Insets(30)); return results; } It\u0026rsquo;s actually fairly simple. I put the ImageView in a StackPane because the contents of StackPanes are centred by default, with a little padding so that he\u0026rsquo;s not tucked up over to the left or crowded by the Centre panel.\nWin/Lose Indicators # This part is pretty simple. I created a StackPane and put the two ImageViews into it. One with the the \u0026ldquo;Win\u0026rdquo; image, and one with the \u0026ldquo;You Lose\u0026rdquo; image. Both images were scaled the same, at a size that would fit in the area available. The Visible property of each image was bound to the appropriate property in the Model, either gameWon or gameLost.\nPuzzle Word # The Puzzle Word, and the Win/Lose images are in a VBox in the Centre of the BorderPane. The Puzzle Word is actually a set of VBox\u0026rsquo;s held in an HBox. Each VBox has a Text with a letter above a Line. Once again, in order to stick to DRY, there\u0026rsquo;s a separate method to create each box.\npublic void buildWord() { wordBox.getChildren().setAll(model.getWord().stream().map(this::createLetterBox).collect(Collectors.toList())); } private Node createLetterBox(S```tring letter```) { VBox vBox = new VBox(); Node letterText = bigText(letter); letterText.visibleProperty() .bind(Bindings.createBooleanBinding(() -\u0026gt; model.getPickedLetters().contains(letter) || model.isGameLost(), model.getPickedLetters(), model.gameLostProperty())); vBox.getChildren().addAll(letterText, new Line(0, 10, 40, 10)); vBox.setAlignment(Pos.BOTTOM_CENTER); return vBox; } private Node bigText(String letter) { Text results = new Text(letter); results.setStyle(\u0026#34;-fx-font-size: 45; -fx-font-weight: bold\u0026#34;); return results; } That\u0026rsquo;s All it Takes # Really, that\u0026rsquo;s it. On the surface, this seems like a much more difficult thing to build, there\u0026rsquo;s all of the images, the virtual keyboard and keeping track of what letters have been played and which to show. But when you look at it closely, it all becomes very simple. None of the screen elements directly interact, and the virtually every element of the GUI is controlled by two arrays of letters and some bindings which combine them together.\nThe complete code can be found here:\nHangMan on GitHub\n","date":"15 August 2021","externalUrl":null,"permalink":"/javafx/hangman/","section":"JavaFX","summary":"How much code does it take to build a complete Hangman game in JavaFX?\nIt turns out … Not much!","title":"Hangman in JavaFX","type":"javafx"},{"content":"PropertyValueFactory is an obsolete convenience method designed to eliminate boilerplate. We don\u0026rsquo;t don\u0026rsquo;t need it any more now that we have Lambda expressions.\nIn JavaFX, the TableView class is largely customized by adding TableColumns to an instance. TableColumn is where you define both the TableCells that will be used, and manner in which data will be extracted from each row model to populate each the TableCell.\nThe Full Boilerplate Approach # The JavaDoc\u0026rsquo;s for TableColumn document the full boilerplate necessary to define how the data will be populated into the cells:\nObservableList\u0026lt;Person\u0026gt; data = ... TableView\u0026lt;Person\u0026gt; tableView = new TableView\u0026lt;Person\u0026gt;(data); TableColumn\u0026lt;Person,String\u0026gt; firstNameCol = new TableColumn\u0026lt;Person,String\u0026gt;(\u0026#34;First Name\u0026#34;); firstNameCol.setCellValueFactory(new Callback\u0026lt;CellDataFeatures\u0026lt;Person, String\u0026gt;, ObservableValue\u0026lt;String\u0026gt;\u0026gt;() { public ObservableValue\u0026lt;String\u0026gt; call(CellDataFeatures\u0026lt;Person, String\u0026gt; p) { return p.getValue().firstNameProperty(); } }); Yikes! That\u0026rsquo;s a lot of \u0026ldquo;\u0026lt;\u0026gt;\u0026rdquo; to wrap your brain around.\nWhat does this do?\nWell first, a Callback is very similiar to a Function. It takes a value of some specific type and returns a value of some other, specific type. While a Function uses the apply() method, a Callback uses a call() method.\nIn this case, we\u0026rsquo;re going to pass a TableColumn.CellDataFeatures object to the call() method, and it will return an ObservableValue back. Both of these classes are generics, so we need specify the class of the data model for the table, and the class of the TableColumn data. In the example above, the table model is Person and the Observable returned will contain a String.\nIt\u0026rsquo;s not really important to know, but TableColumn.CellDataFeatures is a wrapper class which holds a reference to the TableView, the TableColumn, and has a function to return an instance of the TableView data model (in other words, the data for the row). Remember that this Callback is invoked from deep inside the TableView code, and the purpose of this Callback is to provide a \u0026ldquo;hook\u0026rdquo; to allow that code to extract the data that it needs from the TableView data model to populate the cell.\nTechnically, it might be possible that you\u0026rsquo;d need some properties of the TableView or the TableColumn to determine how to extract the data from the data model. Generally speaking, though, you just need the data model to extract the data.\nThe Original Convenience Method # Even when you do understand what that boilerplate code is doing, it\u0026rsquo;s still a lot of code to write for something that almost always boils down to one line very similar to this:\nreturn p.getValue().firstNameProperty(); Because that\u0026rsquo;s what most CellValueFactories do, they extract a single ObservableValue from an object composed of a number of ObservableValue\u0026rsquo;s. Furthermore, the best practice is to implement the fields in the model as JavaFX Property Beans, like this:\npublic class Model { private StringProperty firstName = new SimpleStringProperty(\u0026#34;\u0026#34;); public String getFirstName() { return firstName.get(); } public void setFirstName(String firstName) { this.firstName.set(firstName); } public StringProperty firstNameProperty() { return firstName; } } For each field in the Model, there\u0026rsquo;s a getter and a setter which delegate to the field\u0026rsquo;s get() and set() methods. Then there\u0026rsquo;s a method called {Field Name}Property(), which returns the property itself.\nThis last piece is the most important for the TableView because, if this pattern is followed, you can use reflection to access the property through this method.\nReturning to the JavaDoc\u0026rsquo;s for TableColumn, we see this:\nIt is hoped that over time there will be convenience cell value factories developed and made available to developers. As of the JavaFX 2.0 release, there is one such convenience class: PropertyValueFactory. This class removes the need to write the code above, instead relying on reflection to look up a given property from a String.\nCuriously, this hasn\u0026rsquo;t been updated, even as of JavaFX 15 - which is a shame.\nThis meant that you could replace the boilerplate described above with this:\nfirstNameCol.setCellValueFactory(new PropertyValueFactory(\u0026#34;firstName\u0026#34;); Which is clearly a lot easier to both write and read than the full boilerplate. In fact, most programmers learn this method without ever understanding the Callback code that lies behind it.\nThe Problem with \u0026ldquo;PropertyValueFactory\u0026rdquo; # The problem is that \u0026ldquo;firstName\u0026rdquo; is just a String. There\u0026rsquo;s no way that your IDE or a compiler is going to detect that \u0026ldquo;firstName\u0026rdquo; means that there needs to be a method called firstNameProperty() in your model. So if you misspell \u0026ldquo;firstName\u0026rdquo;, perhaps as \u0026ldquo;FirstName\u0026rdquo;, your code will fail at runtime and you\u0026rsquo;ll have to chase it down.\nEven worse, should you decide to refactor your model and change the name of the field and the methods associated with it, your IDE won\u0026rsquo;t be able to track down the reference in the String and update it as well. Which, of course, won\u0026rsquo;t generate any compiler errors but will mysteriously cause your TableView to stop working.\nLambda to the Rescue # Since Java 8, there is a better way to reduce the boilerplate which avoids the use of reflection and makes PropertyValueFactory obsolete.\nRemember that the Callback interface is a Functional Interface. Which means that it can be the target of a lambda expression. Also, lambda expressions can infer the class of the input parameters based on the context in which it is declared. So the boilerplate listed above can be replaced with this:\nObservableList\u0026lt;Person\u0026gt; data = ... TableView\u0026lt;Person\u0026gt; tableView = new TableView\u0026lt;Person\u0026gt;(data); TableColumn\u0026lt;Person,String\u0026gt; firstNameCol = new TableColumn\u0026lt;Person,String\u0026gt;(\u0026#34;First Name\u0026#34;); firstNameCol.setCellValueFactory(p -\u0026gt; p.getValue().firstNameProperty()); In this example p is the CellDataFeatures\u0026lt;Person, String\u0026gt; object generated from deep inside the TableView code, and the return value is an ObservableValue\u0026lt;String\u0026gt;. But there\u0026rsquo;s no need to declare these types in the lambda since they are all inferred from the context.\nClearly, this is about the same amount of code as using PropertyValueFactory, but it avoids the use of reflection. If you misspell something, you will get a compiler error and a red squiggly line in your IDE. If you refactor firstNameProperty() to a new name, your IDE will adjust it automatically and the compiler will complain if something goes wrong.\nSo that\u0026rsquo;s it.\nStop using PropertyValueFactory right now. It does nothing for you, and can cause you trouble in the future. Use lambda expressions instead.\n","date":"24 June 2021","externalUrl":null,"permalink":"/javafx/techniques/propertyvaluefactory/","section":"JavaFX","summary":"PropertyValueFactory is an obsolete convenience method designed to eliminate boilerplate.  We don’t don’t need it any more now that we have Lambda expressions.","title":"Stop Using PropertyValueFactory","type":"javafx"},{"content":"","date":"28 May 2021","externalUrl":null,"permalink":"/categories/agile--programming/","section":"Categories","summary":"","title":"Agile,  Programming","type":"categories"},{"content":"This is what happened to us:\n\u0026ldquo;What about the cover page?\u0026rdquo;\n\u0026ldquo;Pardon?\u0026rdquo;\n\u0026ldquo;Yes, we need to be able to print a cover page to send along with the cheque when we mail it out.\u0026rdquo;\nThis was the first we had heard about this - at the final demo of the product. How did this happen?\nThe Situation # The company I worked for as Lead Developer routinely contracted out a lot of legal work to external law firms. A lot. Which meant that we also received, processed and paid a lot of legal bills.\nIn case you don\u0026rsquo;t know, lawyers bill by the hour, and they log their entire day in 6 minute (1/10 of an hour) increments. They have software that tracks it all, and when you get your bill it can have pages and pages of entries like, \u0026ldquo;6 minutes - Phone call with Jones about settlement\u0026rdquo; or, \u0026ldquo;24 minutes - Review documents\u0026rdquo;. Things like that. The legal profession calls them \u0026ldquo;dockets\u0026rdquo;.\nWe had spent years building the software to take our company from receiving our legal bills on paper and scanning them, to having them sent by email, to having them uploaded through a portal. But somebody still had to look at those dockets and figure out how they translated into our coding system to track the expenses. We needed to classify the money spent into things like \u0026ldquo;Investigation\u0026rdquo;, \u0026ldquo;Trials\u0026rdquo;, or \u0026ldquo;Pleadings and Motions\u0026rdquo;.\nThe final step in our evolution from paper invoices was to have the legal firms do the classification for us and enter those amounts in our web portal, then upload a PDF of the invoice and docket as supporting documentation. It would save us a lot of time and take out the guess-work that our in-house staff had been doing.\nOf course, building a web application for outside users is complicated and required that we build different entry screens for different kinds of legal bills - each with its own back end processing. We did the most common types of bills first, then the easiest uncommon things. We left the less common, complicated types of bills to the end.\nFinally we came to \u0026ldquo;Third Party Invoices\u0026rdquo;.\nSometimes our lawyers would need to hire expert witnesses, or investigators and other kinds of professionals to do some work. The law firms really didn\u0026rsquo;t like our existing system where they paid the subcontractors directly and then we reimbursed them for the amount. It meant that they were out the money while they waited for us to pay them. They would rather that we just pay those subcontractors directly.\nThis was very different from all of the other invoices. We needed to provide a way for the administrators at the law firms to enter the name and the address of the payee, and they could only apply the amount to a single billing code in our system - from a smaller list of special codes. We had re-engineer the interface into our legacy billing system to allow us to override the name and address too, which was no small job.\nFinally, having the ability to generate a cheque for an arbitrary name and address is a completely different situation from generating a cheque for someone already set up as a supplier in the system. It presents an entirely new challenge from the perspective of setting up financial and fraud controls. So we needed to make sure we thought of all that as we built it.\nThe Process # We were trying to be as Agile as possible. We organized our work into Sprints, set goals for the Sprints and touched base with the users as much as possible. Unfortunately, we were never able to get this particular business unit to agree to partial implementations as we built anything. They were happy to come and see demos and to discuss requirements and ideas, but they didn\u0026rsquo;t want to deploy anything until it was all done.\nWe spent almost two months building this.\nDuring this time we had numerous meetings with all of the internal stakeholders. We would have meetings with everyone involved in the project in the room at one time. This included our internal administrators and their management; the people managing the work the outside law firms were doing and who approved the legal bills; their managers; and even the VP of the department. We also routinely had all of the Development Team in these meetings; programmers, BA\u0026rsquo;s and testers.\nWe always felt it was important to have the whole Team understand as much of the business aspect of what they were building as possible. So we encouraged the Team to participate in these meetings, make suggestions and ask questions.\nAll through the development I felt like we had a good handle on what we needed to build. I also felt that the users did too. I can vividly remember sitting in meetings while the users tossed ideas around and argued amongst themselves about technical or process details. They would discuss the implications of some approach, and offer alternatives or improvements to the ideas.\nIt seemed liked the users had a solid grasp of how the software was going to work. They had examined the implications and ramifications of deep elements the design and had developed a 360 degree view of what we were building.\nI was fooled.\nWhat About the Cover Page? # Well, it turns out you can\u0026rsquo;t just stuff a cheque in an envelop and put it in the mail. You need to add a cover page that tells that person why you are sending them money.\nIt seems obvious now.\nWe had spent all that time building the software, and we had arrived at our final demo and deployment planning meeting. We were ready to talk about issues around training, coordinating with the Finance department (who print the cheques), and getting the message out to the law firms about the change. We were done!\nWe did the demo to the same people who had been in all of the meetings all along. The same people who had argued about technical details and helped us to design the whole thing.\nVirtually the first question that anyone had after we completed that final demo was, \u0026ldquo;What about cover page?\u0026rdquo;. Immediately, all the other users in the room were nodding their heads. Where\u0026rsquo;s the step where we create the cover page?\nFrom a developer perspective this was no small thing. We had engineered a business process and now we needed to inject a whole new step. This was a brand new document, and we would need to build link to our automated document creation system, not to mention design the document and the automation for it.\nThis wasn\u0026rsquo;t a disaster, but it was a set-back. There would be no discussions about training and deployment that day. We had more work to do.\nWhat Went Wrong? # When I described this to my boss at the time, he asked, \u0026ldquo;Why didn\u0026rsquo;t the BA shadow the users? See how the manual process works\u0026rdquo;. That\u0026rsquo;s a fair question.\nThis was essentially a brand new process. We didn\u0026rsquo;t cut third party cheques for expenses - at least not routinely - before this. So there was no one to shadow.\nBut that\u0026rsquo;s not really the point. There are potentially a variety of techniques that might have revealed the need for cover letters, but we didn\u0026rsquo;t employ them. We didn\u0026rsquo;t use them because we had a fully engaged customer who seemed (collectively) to understand what we were building.\nI can tell you, for a fact, that everyone on the Development Team had a shared vision of what they were building, how it would work and what it would look like when it was completed.\nAnd that\u0026rsquo;s the problem.\nAs developers we had that clear, shared vision for the product. We assumed that the customer did too.\nThe First Incontrovertible Rule of Software Engineering # This was not unique. I\u0026rsquo;ve seen examples before where the customer\u0026rsquo;s reaction upon seeing the finished product was, \u0026ldquo;What\u0026rsquo;s this? This is not what you described in the meeting\u0026rdquo;. Yet every developer that was in that meeting would agree that what they delivered is exactly what was discussed.\nThis leads to the most import rule about building systems:\nCustomers don\u0026rsquo;t engage with and understand your design until you give them working software. {: .notice\u0026ndash;danger}\nThere\u0026rsquo;s something about vapourware that puts up an impenetrable barrier that stops most non-developers from picturing how it will work. But once you give them actual working software, especially when you say, \u0026ldquo;This is it. This is what you get.\u0026rdquo;, that barrier dissolves and they can start really thinking about what that software does and how it does it.\nThis is really, really hard for us developers to anticipate. It\u0026rsquo;s what we do all day, every day. We envision what a good solution would look like, what potential problems there might be and the implications of one approach over another. We assume that the other people on the Team are doing the same things, constantly - because they are.\nIt\u0026rsquo;s easy to forget that this is a skill that you developed. Often over years. As a developer, it may be the most important skill you have. Other people don\u0026rsquo;t have this skill and never will.\nHow Do You Deal With This? # Our biggest mistake was letting the customer talk us into a \u0026ldquo;Big Bang\u0026rdquo; release. We could have found an approach that would have allowed us to deliver a partial implementation that was actually useful itself. For instance, we could have built some internal screens to allow users to enter and code up these invoices that came to us as PDF. This would have allowed us to get useful working software into the users\u0026rsquo; hands a lot faster, before we spent time building pages on the extranet for outside counsel to use.\nThe Waterfall approach would say that we had a failure in our requirements gathering, and that by improving our methodologies there we could ensure success in the future. But experience has shown that this is a fallacy. One of the reasons for this is that your customers can\u0026rsquo;t understand your design before you build it. So they can\u0026rsquo;t anticipate the things you\u0026rsquo;ll need to know to build it correctly. And no matter what you do, the customer is always going to be the subject matter expert on business process and you need their insight to figure out the requirements.\nA core idea of Agile is that everyone\u0026rsquo;s ignorance is greatest at the start of any project, and the best approach is one that allows everyone to learn as the project progresses. For customers, that means they need to get their hands on working software as soon as possible. Because without working software, they won\u0026rsquo;t understand what you\u0026rsquo;re building.\n","date":"28 May 2021","externalUrl":null,"permalink":"/programming/cover-page/","section":"Programming","summary":"There’s a lot of ways that software development projects can fail, or partially fail.  Some of these happen when the users don’t understand as much as you think they do about what you’re building, how it will look and how it will work.","title":"What About the Cover Page?","type":"programming"},{"content":"Model-View-Controller is generally accepted as a good way to structure an application with a user interface. While JavaFX contains all of the classes needed to implement MVC in a natural and seamless way, it doesn\u0026rsquo;t seem to be widely documented and there\u0026rsquo;s little evidence on the Web that many people have figured out how to do it properly. The fact that JavaFX supports Reactive programming makes a big difference to how you should implement MVC.\nModel-View-Controller-Interactor # One of the biggest complications with MVC in JavaFX is the need to handle background threads to perform time intensive work which cannot take place on the Application Thread (FXAT). Coupled with that is a firm rule that GUI elements, including any Observable type variables and fields must only be updated by code running on the FXAT.\nTo make this easier to handle, I prefer a slight modification to MVC which splits the controller into two parts, adding an \u0026ldquo;Interactor\u0026rdquo; which handles the business/application logic for the screen. You don\u0026rsquo;t need to do this split if you don\u0026rsquo;t want to, but it does make the code more organized.\nLet\u0026rsquo;s look at what each part does:\nThe Model # The model is the storage space for all of the data shared throughout the the MVCI structure. It is instantiated by the Controller, which retains a reference to it, and passes that reference on to the View and the Interactor. It is the only source of direct communication between the View and the Interactor.\nIn the world of Reactive Programming, the Model is the home for the elements generally referred to as \u0026ldquo;State\u0026rdquo;.\nIn JavaFX, the building blocks of data storage are \u0026ldquo;observable\u0026rdquo; entities such as Properties and ObservableLists. The Model should be a POJO with the fields all instances of the various observable classes in JavaFX. Each property type field should have a getter and a setter which delegate to the get() and set() methods of the property, and a {Field_Name}Property() method which returns the property itself. Essentially, this is the JavaFX version of a Bean. Normally, an ObservableList type field would have a getter, and then a setter that delegates to the setAll() method of the list. All of the fields should be final.\nThe Controller # The Controller is the central element of the structure, and the connection to the rest of the application\u0026rsquo;s GUI. It\u0026rsquo;s instantiated first, and is responsible for instantiating the other components and handling the communication between them. It\u0026rsquo;s also responsible for handling any threading, which is important in JavaFX as all non-GUI activity needs to happen in a background thread.\nThe View # The View is the actual graphical content of the screen. Typically, the View would be a class which extends one of the Region type Nodes in JavaFX; like a BorderPane, VBox or StackPane. Something which can be inserted into a Scene. The View is restricted to having only code which is directly related to displaying information on the screen and accepting user input.\nThe View normally interacts with the Model in one way only. It binds the properties in the model to the properties in the JavaFX nodes on the screen. User editable fields are bound bidirectionally, and display only fields are bound from the Model to the value properties of display nodes. In this way, the Model always reflects the current state of the information displayed on the screen, and there is no reason to write code that scrapes the data out of the screen nodes to submit it to the Controller when any action button is clicked.\nThe Interactor # The Interactor is the application - or business - logic of the MVCI system. It\u0026rsquo;s the piece which \u0026ldquo;interacts\u0026rdquo; with the non-GUI elements of the application. It can invoke the persistence layer or communication classes to talk to other systems, and it can deal with both domain objects and the Model. It\u0026rsquo;s responsible for establishing interrelationships between elements of the model based on business rules.\nGenerally speaking, the Interactor is the only class which should be allowed to use the getters and setters in the Model. The Controller and the View should only access the fields by the ...Property() methods - and then usually just to bind them or add listeners.\nHow Does the Interactor Help? # While it\u0026rsquo;s really just an organizational thing, it\u0026rsquo;s critical to providing structure to the application by isolating the application logic from screen control logic. Technically, everything that the Interactor does could be handled by the Controller, but at the cost of making the Controller much more difficult to understand.\nThe Interactor is where the domain objects, process data and screen data all exist in the same place. Typically, an Interactor will make calls to a persistence layer or external API and then it will need to store the results from those calls. It might have data structures to handle processing and analysis of the domain objects specific to the functionality of the MVCI component, all of which need to be updated on a background thread. In addition, it will probably need to read the contents of the Model in order to make those API calls and process the resulting data on that background thread.\nKeeping Things in the Right Place # One of the keys to writing clean applications, second only to naming things clearly, is putting code in the right place. The toughest part of MVCI is keeping business logic out of the View, as it is extremely easy for it to sneak in. One of the best ways to avoid this is to follow a strict rule of not creating relationships between screen nodes unless the relationship is completely about display.\nFor instance, if there is a checkbox that makes a region of the screen visible or invisible, then it\u0026rsquo;s OK to bind the visible property of the region to the value property of the checkbox. But if the visibility of that region is based on something in the data, perhaps a threshold value for a particular field, then that linkage can\u0026rsquo;t be established by the View itself, and needs to come from the Model.\nHow do you put that in the Model?\nCreate a boolean property in the Model called something like, \u0026ldquo;yadaYadaThresholdMet\u0026rdquo;, and bind the visibility of the region to that field. The logic to actually decide if the threshold has been met belongs in the Interactor, and there should be some code, generally invoked from the Interactor\u0026rsquo;s constructor, that binds that boolean property to other fields in the Model properly.\nPhilosophically, it\u0026rsquo;s also important not to call that boolean property something like, \u0026ldquo;showXyzRegion\u0026rdquo;, as this has no meaning when someone is looking at the Interactor. Since it\u0026rsquo;s actually expressing some sort of application rule, it should be named in a manner that clearly reflects that rule.\nWhile the View has no code which reflects business logic, no other part of the system can ever assume anything about the contents of the View. Any particular field in the Model may or may not be present in the GUI, and manner in which it\u0026rsquo;s displayed cannot be known. There may or may not be a \u0026ldquo;Save\u0026rdquo; button - maybe the save action is triggered by changing the value in a ComboBox?\nA Simple Example # Here\u0026rsquo;s a simple example to illustrate how this all goes together. This is an MVCI set-up with screen that has a TextField and a button, and the button triggers an action that will modify the value in the TextField in some way - in this case it assumes it is a number and attempts to add 5 to it.\npublic class MvciSample extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Scene scene = new Scene(new MvciController().getView(), 500, 200); primaryStage.setScene(scene); primaryStage.show(); } } You can see here that the application instantiates the Controller, then fetches the View from it and installs it into the Scene. Let\u0026rsquo;s look at how the Controller works:\npublic class MvciController { private final Region view; public MvciController() { MvciModel viewModel = new MvciModel(); MvciInteractor interactor = new MvciInteractor(viewModel); view = new MvciView(viewModel, interactor::addFive); } public Region getView() { return view; } } The Controller instantiates the Model, then passes it to the constructor of the Interactor and then the constructor of the View. Additionally, the Controller passes a Runnable to the View to handle the action when View triggers it.\nNow let\u0026rsquo;s look at the Model:\nclass MvciModel { private final StringProperty number = new SimpleStringProperty(\u0026#34;0\u0026#34;); private final BooleanProperty moreAllowed = new SimpleBooleanProperty(true); String getNumber() { return number.get(); } StringProperty numberProperty() { return number; } void setNumber(String number) { this.number.set(number); } boolean isMoreAllowed() { return moreAllowed.get(); } BooleanProperty moreAllowedProperty() { return moreAllowed; } void setMoreAllowed(boolean moreAllowed) { this.moreAllowed.set(moreAllowed); } } There are just two fields, one is a StringProperty that holds a number, and the other is a BooleanProperty that will control whether the \u0026ldquo;Add 5\u0026rdquo; action is allowed at any time. Both fields are explicitly declared final, but they\u0026rsquo;re also effectively final since the \u0026ldquo;setter\u0026rdquo; for each actually delegates to the set method of the properties.\nNow to look at the View:\nclass MvciView extends VBox { MvciView(MvciModel model, Runnable actionHandler) { TextField numberTF = new TextField(); numberTF.textProperty().bindBidirectional(model.numberProperty()); HBox dataBox = new HBox(4, new Label(\u0026#34;Enter a number: \u0026#34;), numberTF); Button button = new Button(\u0026#34;Add 5\u0026#34;); button.setOnAction(evt -\u0026gt; actionHandler.run()); button.disableProperty().bind(model.moreAllowedProperty().not()); getChildren().addAll(dataBox, button); } } The View is one of the simplest JavaFX container nodes, a VBox. There are two elements in the VBox; an HBox containing a label and a TextField for data entry, and a button that performs an action. The \u0026ldquo;text\u0026rdquo; property of the TextField is bound to the number field in the Model, and the \u0026ldquo;disable\u0026rdquo; property of the button is bound to the moreAllowed field in the Model. The button has been set up so that it\u0026rsquo;s onAction() event handler will invoke the actionHandler Runnable.\nThe only constraints on the structure of the View are defined by the dependencies in the constructor. It must be able to function with the Model it\u0026rsquo;s given, and potentially invoke the action supplied through the Runnable. The TextField doesn\u0026rsquo;t have to be a TextField, potentially it could be a Spinner (although it would have to handle conversions to String in the binding) or some other input method. Presumably, if it was a ComboBox, the Model would have to be able to supply the possible value choices somehow.\nNow, the Interactor:\nclass MvciInteractor { MvciModel viewModel; MvciInteractor(MvciModel viewModel) { this.viewModel = viewModel; viewModel.moreAllowedProperty() .bind(Bindings.createBooleanBinding(() -\u0026gt; checkIfMoreAllowed(), viewModel.numberProperty())); } void addFive() { try { viewModel.setNumber(Integer.toString(Integer.parseInt(viewModel.getNumber()) + 5)); } catch (NumberFormatException e) { viewModel.setNumber(\u0026#34;5\u0026#34;); } } private boolean checkIfMoreAllowed() { try { int numberValue = Integer.parseInt(viewModel.getNumber()); return (numberValue \u0026lt; 21); } catch (Exception e) { return true; } } } This interactor does two important things. First, it provides the logic to perform the \u0026ldquo;Add 5\u0026rdquo; action by converting the string into an integer and adding 5. If the integer conversion fails, then it resets the string to \u0026ldquo;5\u0026rdquo;.\nSecondly, it binds the Model field \u0026ldquo;moreAllowed\u0026rdquo; to the \u0026ldquo;number\u0026rdquo; field through a supplier that invokes the checkIfMoreAllowed() method. This method always returns true if the string in the \u0026ldquo;number\u0026rdquo; field is not a number, so that the addFive() method can reset it when invoked.\nNote that this Interactor has no knowledge of the structure of the View baked into it. It deals with the Model alone, and supplies a single callable method with no constraints or dependencies about when or how it will be invoked. Just like the View, it could be replaced with a different Interactor so long as that new Interactor handles the same model and supplies an addFive() method. For instance, a new Interactor could treat the \u0026ldquo;number\u0026rdquo; string like a string and simply concatenate a \u0026ldquo;5\u0026rdquo; onto it, setting \u0026ldquo;moreAllowed\u0026rdquo; to false if the string had 10 or more characters in it.\nPackaging and Visibility # The best packaging is probably to place all of the MVCI components into the same package so that visibility of the inner parts of the components can be controlled properly. Any other custom view elements, such as extensions of TableView, can be added to the same package.\nNote that the only methods with \u0026ldquo;public\u0026rdquo; visibility are the constructor and the getView() method of the controller. All of the other classes are \u0026ldquo;package-private\u0026rdquo;, and have no exposure from classes outside the package. Although this example is too simple to see it, it should also be noted that the View has no methods which are not \u0026ldquo;private\u0026rdquo; except its constructor. In other words, nothing of the inner structure of the View is exposed to any other classes.\nIn order to ensure that the \u0026ldquo;set\u0026rdquo; and \u0026ldquo;get\u0026rdquo; methods of the Model are only callable from the Interactor, it\u0026rsquo;s tempting to put the Model and the Interactor in a separate sub-package and leave those methods as \u0026ldquo;package-private\u0026rdquo;. But this would mean that the constructors and all of the other externally callable methods in those two classes would need to be public, which would expose them outside of the MVCI structure. So it\u0026rsquo;s probably best to leave everything in one package and use programmer discipline to ensure that methods are only called in the appropriate manner. This is made a little bit easier by the fact that the only the Controller has references to all three of the other components, while the View and the Interactor only have a reference to the Model. The Model, of course, has no references to any other components of the MVCI.\nEnabling \u0026ldquo;Save\u0026rdquo; Only When Data Has Been Changed # One situation which is fairly common and appears to be difficult with this approach is when you have a \u0026ldquo;Save\u0026rdquo; button that you only want enabled when the user has changed something. When using the value properties of the screen nodes to store the user data separate from the data model, in seems easier to be able to compare the original values (in the model) to the values on screen. However, Thomas Nields has written an excellent library called DirtyFX which handles this wonderfully.\nNormally, most models have ObjectProperty's which are instantiated using the SimpleObjectProperty implementation. Thomas has created a set of implementations which also allow the initial state of the property - a baseline - to be set and then it maintains an internal boolean property which indicates whether or not the current value of the property is different from the baseline - if it\u0026rsquo;s \u0026ldquo;dirty\u0026rdquo;.\nImplementing this in the example model is straight-forward:\npublic class MvciModel { private final DirtyStringProperty number = new DirtyStringProperty(\u0026#34;0\u0026#34;); private final BooleanProperty moreAllowed = new SimpleBooleanProperty(true); public String getNumber() { return number.get(); } public StringProperty numberProperty() { return number; } public void setNumber(String number) { this.number.set(number); this.number.rebaseline(); } public BooleanProperty moreAllowedProperty() { return moreAllowed; } public ObservableValue\u0026lt;Boolean\u0026gt; dataChanged() { return number.isDirtyProperty(); } } I\u0026rsquo;ve removed the setter for the moreAllowed field, since the property is bound in the constructor of the Interactor, and a bound property cannot be set. I\u0026rsquo;ve also removed the getter, since the Interactor doesn\u0026rsquo;t use it. The dataChanged() method is only there to expose the isDirty property of \u0026ldquo;number\u0026rdquo; to the interactor, while keeping the return value of numberProperty() to be StringProperty - in other words, not exposing the DirtyStringProperty implementation of the field to the rest of the system.\nNote that the setter for the \u0026ldquo;number\u0026rdquo; field now calls rebaseline(), which resets the baseline value to whatever the current value of the property is, after calling the set() method of the property. This works nicely with the idea that the setter is only called from the Interactor, and the View interacts with the property only through a binding, and therefore won\u0026rsquo;t invoke the rebaseline() method when the value in the TextField is changed.\nThe constructor of the Interactor now looks like this:\npublic MvciInteractor(MvciModel viewModel) { this.viewModel = viewModel; viewModel.moreAllowedProperty() .bind(Bindings.createBooleanBinding(() -\u0026gt; checkIfMoreAllowed() \u0026amp;\u0026amp; viewModel.dataChanged().getValue(), viewModel.numberProperty(), viewModel.dataChanged())); } Now the \u0026ldquo;moreAllowed\u0026rdquo; property will only be true if the number in the field is less than 21 or not actually a number, and has been changed since the the last time the addFive() method was invoked. DirtyFX also has a composite dirty property, which allows you to register other dirty properties with it, and will be true if any of the component properties are dirty.\nIn Summary # This example is simple because it lacks any deeper application behind it, and therefore no need to perform background processing to handle communication with a database or external API. If it did, then the simple Runnable to invoke the addFive() method would need to be changed to something more complex to allow the GUI to be modified prior to invoking the background job, perhaps showing some kind of progress indicator and disabling the button, and then to return the GUI back to it\u0026rsquo;s original state when the job was completed. The Controller would need to be enhanced to create a Task to run the background job, and then to invoke a second Interactor method to update the Model on the FXAT after the background job had been completed.\n","date":"11 May 2021","externalUrl":null,"permalink":"/javafx/MVC_In_JavaFX/","section":"JavaFX","summary":"Model-View-Controller is generally accepted as a good way to structure an application with a user interface.  Since JavaFX implements Reactive programming there’s a natural way to incorporate MVC into a JavaFX application.  However, it doesn’t seem to be widely documented and there’s little evidence on the Web that many people have figured out how to do it properly.","title":"Implementing MVC in JavaFX","type":"javafx"},{"content":"","date":"11 May 2021","externalUrl":null,"permalink":"/series/textformatter/","section":"Series","summary":"","title":"TextFormatter","type":"series"},{"content":" Introduction # Model - View - Controller is a design pattern that has become the defacto standard for building web sites and business applications worldwide. It can be used effectively with JavaFX, but how to do so properly is not very well understood or very well documented. MineSweeper is a simple game that can be used to illustrate how to implement MVC in JavaFX, and also to show how a lot of functionality can be packed into very simple code with JavaFX.\nThe structure of this application uses the Reactive nature of JavaFX in virtually all of its design aspects.\nRecently, I saw a question on StackOverflow from someone attempting to build a MineSweeper game in JavaFX. The question itself doesn\u0026rsquo;t matter - and it was later deleted by the person asking the question - but the sample code that was posted was extremely complicated and did not work very well.\nI wondered just how simple a MineSweeper program could be if it was written with a strict MVC approach. The nice thing about using MineSweeper as an example is that it has no outside API to interact with and no persistence layer, so that everything can be done on the FXAT. This way, we can concentrate on the MVC structure and how to implement it without have to complicate things with background processing and threading issues.\nMineSweeper - The Rules # Everybody probably already knows MineSweeper - it\u0026rsquo;s a game with a grid of grey squares, and when you click on a square it turns white to reveal:\nNothing - No mines are in any adjacent squares A Number - How many adjacent squares have mines in them A Mine - Boom! You lose The goal is to click on all of the squares that don\u0026rsquo;t contain a mine, leaving only the squares with mines in them unclicked.\nAnother important idea about the game-play is that it shouldn\u0026rsquo;t ever be left to chance alone at the beginning. So the first square you click cannot be a mine. Nor can it have any adjacent mines, as you wouldn\u0026rsquo;t yet have enough information to make a non-random choice about which square to click second. The main impact this has on designing the game is that the contents of the squares cannot be determined until the user clicks on the first square.\nA Quick Review of Model-View-Controller # Model-View-Controller is a design pattern that divides a system - or a component of a system - into three parts:\nThe Model This holds the data which is used by the View. In JavaFX, the model is composed of Observable objects and lists. The View The View is strictly the component which handles the display and user interaction elements of system. It has no business logic, and binds screen components to the Observable objects in the model. The Controller The Controller is responsible for instantiating the Model and the View, and communicating with the rest of the system. It is the only MVC component which has methods visible outside of the MVC structure. The Controller also contains all of the application logic. The idea is that the screen components and UI are a black box and could, theoretically at least, be swapped out for any number of designs without modifying the Controller or the Model. At the same time, the View does not contain any application logic, it\u0026rsquo;s strictly a user interface that delegates any application decisions back to the Controller.\nThe Components # This implementation of the game is built with two MVC constructs, the game board and the cells. A great deal of care is taken to ensure that all of the functionality is placed in the element where it belongs. This means that the Cell MVC is responsible for everything that goes on inside a cell, and the game board MVC is responsible for the game board activities and anything that involves multiple cells.\nThis design is especially interesting because it illustrates how a more complicated application with many layered and interdependent components could be constructed. Think of each MVC as a component, with the entirety of its API exposed through the Controller.\nThe Cell # The Cell is an autonomous, self-contained unit. Other than having a public method which will tell whether or not an (x,y) cell co-ordinate is one of its neighbours it isn\u0026rsquo;t particularly aware of existing in the context of a grid. It doesn\u0026rsquo;t have any functionality which is dependant on any other Cells, or the Game Board itself.\nThe CellModel # The CellModel is fairly simple, mostly a collection of boolean properties:\nclass CellModel { private final BooleanProperty clicked = new SimpleBooleanProperty(false); private final BooleanProperty isMine = new SimpleBooleanProperty(false); private final BooleanProperty isFlag = new SimpleBooleanProperty(false); private final StringProperty mineCount = new SimpleStringProperty(\u0026#34;\u0026#34;); public BooleanProperty clickedProperty() { return clicked; } public BooleanProperty isMineProperty() { return isMine; } public BooleanProperty isFlagProperty() { return isFlag; } public StringProperty mineCountProperty() { return mineCount; } } We need to know if the Cell contains a mine, if it\u0026rsquo;s been clicked, what number to display when it\u0026rsquo;s clicked (assuming it\u0026rsquo;s not a mine), and whether we should show the flag to indicate that it\u0026rsquo;s been right clicked. That\u0026rsquo;s all.\nI chose not to use delegate getters or setters for these properties because it wouldn\u0026rsquo;t add any extra control or make anything easier to understand given how simple the Controller is.\nThe CellView # The CellView needs to display some of 4 possible things:\nA gray or white rectangle as a background. And one of the following: A Flag A Mine A Number The best layout for the Cell is a StackPane, with the everything loaded up in it at the beginning, and the visibility of the components bound to the properties in the CellModel. The Rectangle needs to go at the bottom, but the other three components can go in any order as only one of them will be visible at a time:\nclass CellView extends StackPane { private static final Image mineImage = new Image(\u0026#34;/images/MineSmall.png\u0026#34;); private static final Image flagImage = new Image(\u0026#34;/images/Flag.png\u0026#34;); CellView(CellModel model, Consumer\u0026lt;MouseButton\u0026gt; clickConsumer) { Text mineCountText = new Text(\u0026#34;\u0026#34;); ImageView mineImageView = new ImageView(mineImage); ImageView flagImageView = new ImageView(flagImage); flagImageView.setFitHeight(24); flagImageView.setFitWidth(24); getChildren().addAll(createRectangle(model.clickedProperty()), mineCountText, mineImageView, flagImageView); mineCountText.textProperty().bind(model.mineCountProperty()); mineCountText.visibleProperty().bind(model.clickedProperty().and(model.isMineProperty().not())); mineImageView.visibleProperty().bind(model.clickedProperty().and(model.isMineProperty())); flagImageView.visibleProperty().bind(model.isFlagProperty().and(model.clickedProperty().not())); setOnMouseClicked(evt -\u0026gt; clickConsumer.accept(evt.getButton())); } private Rectangle createRectangle(ObservableBooleanValue clickedProperty) { Rectangle rectangle = new Rectangle(30, 30); rectangle.fillProperty() .bind(Bindings.createObjectBinding(() -\u0026gt; clickedProperty.get() ? Color.WHITE : Color.GRAY, clickedProperty)); rectangle.setStroke(Color.BLACK); return rectangle; } } The mine and the flag are just images. I wasn\u0026rsquo;t able to quickly find a small enough flag image, so I scaled it in the code when it was loaded into its ImageView. StackPane's centre everything in them by default, so there\u0026rsquo;s no need to fuss about with alignment either.\nThe Rectangle starts out gray, and then flips to white when the cell has been clicked, so the binding for that converts the boolean property into a Color via the Bindings library createObjectBinding(). For the other bindings, I\u0026rsquo;ve just used the Fluent Binding API since they are direct bindings of the model properties joined with and() and possibly modified with not().\nThe Cell Controller # The Controller for each Cell is just the Cell object itself. It instantiates the CellModel as a field, and then passes it to the constructor of the CellView:\npublic class Cell { private static final Random RANDOM = new Random(); private final int x; private final int y; private final CellModel model = new CellModel(); private final Region cellView; public Cell(int x, int y, BiConsumer\u0026lt;Integer, Integer\u0026gt; clickConsumer) { this.x = x; this.y = y; Consumer\u0026lt;MouseButton\u0026gt; viewClickConsumer = mouseButton -\u0026gt; { if (mouseButton.equals(MouseButton.SECONDARY)) { model.isFlagProperty().set(!model.isFlagProperty().get()); } else { model.clickedProperty().set(true); clickConsumer.accept(x, y); } }; cellView = new CellView(model, viewClickConsumer); } public Region getView() { return cellView; } public void setNum(long num) { model.mineCountProperty().set(num \u0026gt; 0 ? Long.toString(num) : \u0026#34;\u0026#34;); } public int getX() { return x; } public int getY() { return y; } public boolean isMine() { return model.isMineProperty().get(); } public boolean notMine() { return !isMine(); } public boolean isNeighbour(int testX, int testY) { if ((Math.abs(getX() - testX) \u0026lt;= 1) \u0026amp;\u0026amp; (Math.abs(getY() - testY) \u0026lt;= 1)) { return !((getX() == testX) \u0026amp;\u0026amp; (getY() == testY)); } return false; } public BooleanProperty clickedProperty() { return model.clickedProperty(); } public void setMineRandomly() { model.isMineProperty().set(RANDOM.nextInt(10) \u0026lt;= 1); } } One thing you\u0026rsquo;ll notice right away is the the \u0026ldquo;x\u0026rdquo; and \u0026ldquo;y\u0026rdquo; location of the cell aren\u0026rsquo;t part of the model! That\u0026rsquo;s because they have nothing to do with the CellView at all and they act more like domain objects in a business application. So they don\u0026rsquo;t belong in the View Model for the Cell.\nThe other thing to notice is the viewClickConsumer which is passed to the CellView. It would seem to be easier to just directly attach an EventHandler to the CellView with view.setOnMouseClicked(), so why not do that? This would violate the idea that the View is a black box by assuming that the design of the CellView makes it clickable itself. While the MineSweeper game generally assumes that mouse clicks are going to be the user interaction, it\u0026rsquo;s not cast in stone, and the implementation doesn\u0026rsquo;t have to make the whole CellView clickable. It\u0026rsquo;s possible it could be implemented with a pair of ToggleButtons, one for \u0026ldquo;flag\u0026rdquo; and one for \u0026ldquo;reveal\u0026rdquo;. We break that dependency by creating a Consumer, and passing it to the CellView via its constructor.\nThe viewClickConsumer still assumes that MouseButton is going to be sent back, but any implementation of CellView that doesn\u0026rsquo;t use mouse clicks can still convert its functionality to pass either MouseButton.PRIMARY or MouseButton.SECONDARY. This is maybe a bit of a kludge, and if I was making a large business application I\u0026rsquo;d probably create an Enum called CellAction and pass that back instead.\nThere\u0026rsquo;s surprisingly little logic inside the Cell. It really has only the following functions that aren\u0026rsquo;t getters or setters:\nIt defines a click consumer that handles both left and right clicks. It can determine if any given x,y combination is one of its neighbours. It can use a random function to set it\u0026rsquo;s isMine Property The notMine() method was added so that it could be access as a Method Reference in a Stream in the game Controller. It just makes that code look a little cleaner.\nThe MineSweeper Game Board # This is essentially the entire window for the game. There are three buttons to start (or restart) a game of various board sizes, the board itself, and some graphics to indicate win or lose.\nThe Model # The View Model for the game board is very simple, just 2 boolean properties:\nprivate final BooleanProperty loseProperty = new SimpleBooleanProperty(false); private final BooleanProperty winProperty = new SimpleBooleanProperty(false); Rather than create a separate Model class for this, these have been left as fields in the Controller. This was really just to make a point; they still comprise the Model but they don\u0026rsquo;t *have * to be put into a separate class if it doesn\u0026rsquo;t make sense. In this case, they can be passed to the View in its constructor without resulting in a ridiculously long parameter list.\nThe View # The main screen View is a VBox with two compound elements. The first is a StackPane which contains the main GridPane at the bottom and two other StackPanes, each containing an image for win or lose on top of it. These two StackPane\u0026rsquo;s are only partially opaque, and have a different background colour.\nThe second part of the VBox is an HBox with a set of Buttons which start new games at different board sizes. The entirety of the action of these buttons is handled by the View Controller via the Consumer passed to the View in its constructor.\nclass MineSweeperView extends VBox { private GridPane grid = new GridPane(); private Consumer\u0026lt;Integer\u0026gt; boardInitializer; MineSweeperView(Consumer\u0026lt;Integer\u0026gt; boardInitializer, ObservableBooleanValue gameLostProperty, ObservableBooleanValue gameWonProperty) { this.boardInitializer = boardInitializer; initialize(gameLostProperty, gameWonProperty); } private void initialize(ObservableBooleanValue mineClickedProperty, ObservableBooleanValue unclickEmptyProperty) { setSpacing(10); getChildren().addAll(setUpMainGrid(mineClickedProperty, unclickEmptyProperty), setUpButtonBox()); setPadding(new Insets(10)); } private Region setUpButtonBox() { return new HBox(10, createTypeButton(\u0026#34;Easy\u0026#34;, 10), createTypeButton(\u0026#34;Medium\u0026#34;, 16), createTypeButton(\u0026#34;Hard\u0026#34;, 25)); } private Region setUpMainGrid(ObservableBooleanValue gameLostProperty, ObservableBooleanValue gameWonProperty) { return new StackPane(grid, setUpWinLosePane(new ImageView(new Image(\u0026#34;/images/MineBig.png\u0026#34;)), Color.PINK, gameLostProperty), setUpWinLosePane(new ImageView(new Image(\u0026#34;/images/Win.png\u0026#34;)), Color.BLUEVIOLET, gameWonProperty)); } void addCell(int x, int y, Region cell) { grid.add(cell, x, y); } void clearView() { grid.getChildren().clear(); } private Pane setUpWinLosePane(ImageView imageView, Color colour, ObservableBooleanValue revealProperty) { Pane pane = new StackPane(); pane.setBackground(new Background(new BackgroundFill(colour, CornerRadii.EMPTY, Insets.EMPTY))); pane.setOpacity(0.6); pane.getChildren().add(imageView); pane.visibleProperty().bind(revealProperty); return pane; } private Node createTypeButton(String label, int newBoardSize) { Button button = new Button(label); button.setOnAction(evt -\u0026gt; boardInitializer.accept(newBoardSize)); return button; } } Virtually all of the code in this View is involved with setting up the layout. There are only two methods not related to this; one which clears the GridPane of all of its children, and one which adds a Node to the GridPane at specified row and column. These are also the only two non-private methods in the View, aside from the constructor. It is possible to avoid having these two public methods by expanding the Model to have enough information to populate or clear the GridPane, but it wouldn\u0026rsquo;t result in a cleaner design.\nThe Game Controller # The Game Controller has the code which launches everything, and the code which needs to look at more than one cell at a time, which as it turns out, isn\u0026rsquo;t much:\nclass MineSweeperController { private final BooleanProperty loseProperty = new SimpleBooleanProperty(false); private final BooleanProperty winProperty = new SimpleBooleanProperty(false); private final MineSweeperView view; private boolean initialized = false; private ArrayList\u0026lt;Cell\u0026gt; cells = new ArrayList\u0026lt;\u0026gt;(); private final BiConsumer\u0026lt;Integer, Integer\u0026gt; screenSizeHandler; MineSweeperController(BiConsumer\u0026lt;Integer, Integer\u0026gt; screenSizeHandler) { this.screenSizeHandler = screenSizeHandler; view = new MineSweeperView(this::setBoardSize, loseProperty, winProperty); } private void setBoardSize(int newBoardSize) { screenSizeHandler.accept((newBoardSize * 31) + 100, (newBoardSize * 31) + 32); reset(); loadBoard(newBoardSize); } public Region getView() { return view; } public void reset() { winProperty.unbind(); winProperty.set(false); loseProperty.unbind(); loseProperty.set(false); cells.clear(); initialized = false; } public void loadBoard(int boardSize) { view.clearView(); for (int i = 0; i \u0026lt; boardSize; i++) { for (int j = 0; j \u0026lt; boardSize; j++) { Cell cell = new Cell(i, j, this::consumeBoardClick); view.addCell(i, j, cell.getView()); cells.add(cell); } } } private void consumeBoardClick(int x, int y) { if (!initialized) { initializeBoard(x, y); initialized = true; } System.out.println(\u0026#34;***** (\u0026#34; + x + \u0026#34;, \u0026#34; + y + \u0026#34;)\u0026#34;); cells.stream() .filter(Cell::notMine) .filter(cell -\u0026gt; !cell.clickedProperty().get()) .forEach(cell -\u0026gt; System.out.println(\u0026#34;\\t\u0026#34; + \u0026#34;(\u0026#34; + cell.getX() + \u0026#34;, \u0026#34; + cell.getY() + \u0026#34;)\u0026#34;)); } public void initializeBoard(int pressedI, int pressedJ) { setMines(pressedI, pressedJ); cells.forEach(cell -\u0026gt; cell.setNum(getNeighbors(cell.getX(), cell.getY()).stream().filter(Cell::isMine).count())); winProperty.bind(createWinLoseBinding(Cell::notMine, Boolean::logicalAnd)); loseProperty.bind(createWinLoseBinding(Cell::isMine, Boolean::logicalOr)); } private BooleanBinding createWinLoseBinding(Predicate\u0026lt;Cell\u0026gt; predicate, BinaryOperator\u0026lt;Boolean\u0026gt; reductionOperator) { List\u0026lt;BooleanProperty\u0026gt; clickProperties = cells.stream().filter(predicate).map(Cell::clickedProperty).collect(Collectors.toList()); return Bindings.createBooleanBinding(() -\u0026gt; clickProperties.stream() .map(BooleanProperty::get) .reduce(clickProperties.get(0).get(), reductionOperator), clickProperties.toArray(new BooleanProperty[0])); } private void setMines(int pressedI, int pressedJ) { cells.stream() .filter(cell -\u0026gt; !cell.isNeighbour(pressedI, pressedJ)) .filter(cell -\u0026gt; !((cell.getX() == pressedI) \u0026amp;\u0026amp; (cell.getY() == pressedJ))) .forEach(Cell::setMineRandomly); } public List\u0026lt;Cell\u0026gt; getNeighbors(int x, int y) { return cells.stream().filter(cell -\u0026gt; cell.isNeighbour(x, y)).collect(Collectors.toList()); } } Setting Up the Board # This is really simple, just use two nested loops for the (x,y) coordinates of the board, instantiate a Cell for each iteration and pass it to the View to do something with it. What\u0026rsquo;s really interesting is that you don\u0026rsquo;t need a logical grid structure to store the Cell elements for doing any later processing. A simple List\u0026lt;Cell\u0026gt; will do. Since each Cell knows its own (x,y) coordinates, it can supply a method which will tell if any given (x,y) coordinate is one of its neighbours.\nThe original sample code in the StackOverflow question had a matrix: Cell[][] board that held all of cells in a grid. Then the board was traversed with two nested loops any time all of the cells needed to be checked. Replacing this with a List means that the nested loops can be simplified to simple Stream operations.\nThe First Click # The biggest complication in the game is that the first cell clicked has to be empty white. This means that none of the cell contents can be determined until that first cell has been clicked. For this reason, the game board controller passes a BiConsumer to the Cell constructors to handle the (x,y) location of the click. That BiConsumer calls consumeBoardClick() which will do nothing if it isn\u0026rsquo;t the first click.\nInitializing the board is by necessity a two-pass operation. Pass 1 visits each cell, and if it isn\u0026rsquo;t the first cell clicked or one of its neighbours, then tell it to randomly set its isMine property. Pass 2 visits each cell, gets all of its neighbours and Streams them, filtering for the ones with isMine set to true. Then it counts them up and sets the cell\u0026rsquo;s number based on that.\nThe Win and Lose Properties # The last step in initializing the board is to bind the two win/lose properties to the cells. Let\u0026rsquo;s have a look a the conditions for \u0026ldquo;win\u0026rdquo; and \u0026ldquo;lose\u0026rdquo;\nWin The player wins when **all ** of the cells with the isMine property set to false have been clicked. Lose The player loses when any of the cells with the isMine property set to true have been clicked. Determining win or lose at any given time is simple; Stream a list of cells and check to see if any or all of them have been clicked. So we need two lists of cells, one with isMine set to true, and the other with false. However, we\u0026rsquo;re not really interested in the cells, just the isMine property contained in each one. We can use a map() function to transform the Stream of Cell into a Stream of BooleanProperty, and then collect them into a list.\nWe\u0026rsquo;re going to use the Bindings library to create this binding:\npublic static BooleanBinding createBooleanBinding(Callable\u0026lt;Boolean\u0026gt; func, Observable... dependencies) That\u0026rsquo;s straight from the JavaDocs for Bindings. The first parameter is just a Supplier, which is a functional interface that doesn\u0026rsquo;t take any parameters but returns a value of the type specified - in this case a Boolean. We just need to write a supplier that determine if the player has won or lost at the time that it is called. It\u0026rsquo;s important to note that this Supplier isn\u0026rsquo;t necessarily involved with properties and observables, it\u0026rsquo;s just ordinary Java that calculates a boolean value somehow.\nThe second parameter is an array of Observable objects that will trigger a recalculation of the Boolean whenever any one of them changes. For these cases, we\u0026rsquo;ll have a List of BooleanProperty that we can convert into an array.\nLet\u0026rsquo;s see how this works:\nprivate BooleanBinding createWinLoseBinding(Predicate\u0026lt;Cell\u0026gt; predicate, BinaryOperator\u0026lt;Boolean\u0026gt; reductionOperator) { List\u0026lt;BooleanProperty\u0026gt; clickProperties = cells.stream().filter(predicate).map(Cell::clickedProperty).collect(Collectors.toList()); return Bindings.createBooleanBinding(() -\u0026gt; clickProperties.stream() .map(BooleanProperty::get) .reduce(clickProperties.get(0).get(), reductionOperator), clickProperties.toArray(new BooleanProperty[0])); } The first step is to create a List\u0026lt;BooleanProperty\u0026gt; by streaming the entire list of cells, filtering them based on isMine() or notMine() and then extracting the clickedProperty() in a map function.\nTo determine the win/lose condition, the list of BooleanProperty will be Streamed, then the current value will be determined by calling the get() method of the property, and the Stream will be reduced to a single Boolean value by applying a Boolean operator between all of the elements. For this, \u0026ldquo;Any\u0026rdquo; means that we need to use OR as the operator, and \u0026ldquo;All\u0026rdquo; means that we need to use AND.\nEven if you have trouble following exactly how it works, the calls to this method are pretty clear in their intent:\nwinProperty.bind(createWinLoseBinding(Cell::notMine, Boolean::logicalAnd)); loseProperty.bind(createWinLoseBinding(Cell::isMine, Boolean::logicalOr)); One Ugly Part # The only problem with the win/lose logic is that it isn\u0026rsquo;t relevant in the time between the reset of the grid for a new game and the first cell click of that game. It needs to be disabled for that time span so we have the following code:\npublic void reset() { winProperty.unbind(); winProperty.set(false); loseProperty.unbind(); loseProperty.set(false); cells.clear(); initialized = false; } This seems a bit clumsy, but the only alternative is to turn the initialized field into a BooleanProperty and then tie it into the BooleanBinding built for the winProperty and loseProperty. But doing this wouldn\u0026rsquo;t make anything clearer, and the bindings still need to be rebuilt and bound to those properties after the first click.\nThe Main Class # The Main class is only of interest here because has some code which resizes the window according to the size of the board the game is being played on:\npublic class MineSweeper extends Application { public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) throws Exception { primaryStage.setTitle(\u0026#34;Minesweeper\u0026#34;); BiConsumer\u0026lt;Integer, Integer\u0026gt; screenSizeHandler = (height, width) -\u0026gt; { primaryStage.setHeight(height); primaryStage.setWidth(width); }; Scene value = new Scene(new MineSweeperController(screenSizeHandler).getView()); primaryStage.setScene(value); primaryStage.show(); } } You can see that the Main class passes a BiConsumer to the constructor of the Controller to allow it to change the size of the window. In this way, the main screen Controller needs to have no knowledge of anything outside it\u0026rsquo;s scope. It has a dependency declared in its constructor that requires that any parent class instantiating the Controller has to provide some sort of screen size handling\nThe images # If you want to copy this code and run it yourself with changing things, you\u0026rsquo;ll need the images:\nSummary # Once the game is underway, there\u0026rsquo;s only really two lines of code that are directly executed when a cell is clicked:\nif (evt.getButton().equals(MouseButton.SECONDARY)) { model.isFlagProperty().set(!model.isFlagProperty().get()); } else { model.clickedProperty().set(true); } Everything else is either layout or setup, which largely consists of setting model values and creating bindings between the view and the model. This the essence of programming with JavaFX: virtually all of the JavaFX code is setup - creating the layout, the bindings and the listeners - and it only amounts to a small proportion of the overall code.\nLook at where the bulk of the code in this application resides. It\u0026rsquo;s in the Controllers, after the getView() method. Sure, a lot of it deals with observables and properties, but it deals with them as data, and the way that they are tied back to the GUI is not considered in that code at all.\nAnother thing to note is just how small the entire application is. The Cell MVC has about 60 lines of code, and the Game MVC has about 100 lines of code. To me, that doesn\u0026rsquo;t seem like much code at all considering what it does.\nUsing the MVC design pattern here really allows the code to become easier to understand and to develop. Having a clear picture of exactly what information is needed to make the GUI work is a key step to creating a clean design for the GUI, and the need to define a Model helps to understand that. Having a complete separation between layout and application logic is critical to implementing MVC cleanly, and the result is that a of complexity that you\u0026rsquo;d have without it just drops out of the application.\nIn terms of having a multi-layered MVC construction, note how the Game Board MVC has no knowledge of or dependencies on the structure of the the CellView. To the Game Board MVC, and to the MineSweeperView, it\u0026rsquo;s just a Region. Once the Cells have been instantiated and the CellViews have been passed on to the MineSweeperView, the MineSweeperController only deals with the Cells as gameplay objects. The visual aspect of a Cell can be ignored by the MineSweeperController gameplay logic.\n","date":"3 May 2021","externalUrl":null,"permalink":"/javafx/projects/minesweeper/","section":"JavaFX","summary":"MineSweeper!  Everyone’s favourite time-waster Windows game.  Implemented in JavaFX showing how the reactive nature of JavaFX can be used with MVC to create a game.","title":"MineSweeper in JavaFX","type":"javafx"},{"content":"Many programmers look for on-line tutorials and examples to find out how to code common JavaFX structures and techniques. Unfortunately, not all the code you\u0026rsquo;ll find on the web is high quality and there are a number of bad techniques that have been replicated over and over again on numerous sites. One of these is the structure that I call the \u0026ldquo;Dispatch EventHandler\u0026rdquo;.\nA Real Example From the Web # There was an answer to a question on StackOverflow recently about adding actions to Buttons in JavaFX.\nThe solution was posted is in the listing below. To be fair, the person posting the answer was just solving the problem as asked, and kept the same overall approach that the person who posted the question had taken. I\u0026rsquo;ve used the code from the answer instead of from the question because this code actually runs and produces a partially correct result:\npublic class LatinTranslator extends Application { private Label myLabel; public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { myLabel = new Label(); Button leftButton = new Button(\u0026#34;Sinister\u0026#34;); Button centerButton = new Button(\u0026#34;Medium\u0026#34;); Button rightButton = new Button(\u0026#34;Dexter\u0026#34;); centerButton.setAlignment(Pos.CENTER); leftButton.setOnAction(new ButttonClickHandler()); centerButton.setOnAction(new ButttonClickHandler()); rightButton.setOnAction(new ButttonClickHandler()); VBox Vbox = new VBox(20, myLabel, leftButton, centerButton, rightButton); Scene scenebox = new Scene(Vbox, 300, 500); Vbox.setAlignment(Pos.CENTER); primaryStage.setScene(scenebox); primaryStage.setTitle(\u0026#34;Latin Translator\u0026#34;); primaryStage.show(); } class ButttonClickHandler implements EventHandler\u0026lt;ActionEvent\u0026gt; { @Override public void handle(ActionEvent event) { Object source = event.getSource(); if (source instanceof Button) { Button button = (Button) source; String text = button.getText(); if (\u0026#34;Sinister\u0026#34;.equals(text)) { myLabel.setText(\u0026#34;left\u0026#34;); } } } } } I\u0026rsquo;ve seen this approach a lot. I wrote code like this when I started out with JavaFX.\nThis is probably copypasta from the early days of JavaFX 2, most likely starting with a tutorial on creating a menu. The same type of structure was used, with the menu text being used to decide which action to take in the ButtonClickHandler code. It\u0026rsquo;s possible that this was a hold-over from Swing, where this approach was standard for building JMenu\u0026rsquo;s.\nSearching for examples today, I couldn\u0026rsquo;t find any online tutorials for JavaFX menus that used this technique, which is good. However, the fact that it would pop up in a recent StackOverflow question posted by someone who was probably new to JavaFX seems to indicate that there are examples out there for beginners to find.\nThe Problem # It\u0026rsquo;s the ButtonClickHandler class that is the problem. It\u0026rsquo;s a \u0026ldquo;Dispatch EventHandler\u0026rdquo;. The idea is that you create a single EventHandler that will called by a number of controls, and it will somehow figure out which one called it, and what it should do for each one.\nIt\u0026rsquo;s a very round-about way to program this. It\u0026rsquo;s worse than going around three sides of a square - more like going around seven sides of an octagon - and it introduces a level of complexity that simply doesn\u0026rsquo;t serve any purpose at all. The person writing the answer in this example didn\u0026rsquo;t even bother to put in the actions for two out of the three buttons; probably because it was too much work and didn\u0026rsquo;t contribute to the solution.\nUsing this approach treats the Buttons like data, and ignores the fact that Button is a Control with the ability to perform actions on its own. This, even though the program invokes the Buttons\u0026rsquo; OnAction() method!\nAnother big issue with this code is that it tightly couples the display elements to the action elements. What happens if you discover that you\u0026rsquo;ve spelled one of the Latin words on the buttons incorrectly? Now you have to correct both the button and the action code. Furthermore, if you\u0026rsquo;re coming at this as a maintenance programmer to fix the spelling error, you need to understand that the button text isn\u0026rsquo;t just display but is actually used as data to control the logic flow somewhere else. That\u0026rsquo;s an easy thing to miss!\nA Better Approach # The program could be much simpler:\npublic class Translator extends Application { private final Label myLabel = new Label(); public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { VBox vBox = new VBox(20, myLabel, createButton(\u0026#34;Sinister\u0026#34;, \u0026#34;Left\u0026#34;), createButton(\u0026#34;Medium\u0026#34;, \u0026#34;Middle\u0026#34;), createButton(\u0026#34;Dexter\u0026#34;, \u0026#34;Right\u0026#34;)); vBox.setAlignment(Pos.CENTER); primaryStage.setScene(new Scene(vBox, 300, 500)); primaryStage.setTitle(\u0026#34;Latin Translator\u0026#34;); primaryStage.show(); } private Node createButton(String buttonText, String labelText) { Button button = new Button(buttonText); button.setOnAction(evt -\u0026gt; myLabel.setText(labelText)); return button; } } First of all, this new version is much smaller. There are roughly half as many lines of code compared to the original version - especially considering that this version handles the translations for all of the Buttons, not just the first one. Generally speaking, less code is going to be easier to read and to understand; and that certainly is the case here.\nI\u0026rsquo;ve also stripped out some unneeded variables and their initialization to streamline and simplify the code. This contributes to the reduction in lines of code.\nI\u0026rsquo;ve put the actual instantiation of the Buttons into its own method here for a couple of reasons:\nFirst, the code in that method would otherwise be repeated over three times, and you\u0026rsquo;d need to create a variable for each one. If you had more than three buttons, the situation would just get worse.\nSecondly, note that the method returns \u0026ldquo;Node\u0026rdquo;, not button. This is an important distinction because it really emphasizes that to the layout it\u0026rsquo;s just a generic screen element. Everything that makes it different and unique is contained within it, and the layout code has no need to even know that this is a button with an action. The nature of the button is now completely decoupled from the layout of the GUI, as it should be.\nThere are Even More Benefits # Given that the context for this code seems to be an application for showing translations of words from one language to another, it seems unlikely that you\u0026rsquo;d want to hand code each word and translation on screen after screen after screen. In that case, you\u0026rsquo;d probably have some database of translations, and you\u0026rsquo;d pass some subset of your database to the screen for display.\nTo simulate that, we\u0026rsquo;ll add a method that will create a Map\u0026lt;String, String\u0026gt; of words and translation to provide our database results. Then we\u0026rsquo;ll stream that Map and turn it into a list of Buttons that can be put in the VBox:\npublic class Translator extends Application { private final Label myLabel = new Label(); public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { VBox vBox = new VBox(20, myLabel); vBox.getChildren() .addAll(initializeData().entrySet() .stream() .map(entry -\u0026gt; createButton(entry.getKey(), entry.getValue())) .collect(Collectors.toList())); vBox.setAlignment(Pos.CENTER); primaryStage.setScene(new Scene(vBox, 300, 500)); primaryStage.setTitle(\u0026#34;Latin Translator\u0026#34;); primaryStage.show(); } private Node createButton(String buttonText, String labelText) { Button button = new Button(buttonText); button.setOnAction(evt -\u0026gt; myLabel.setText(labelText)); return button; } private Map\u0026lt;String, String\u0026gt; initializeData() { Map\u0026lt;String, String\u0026gt; results = new HashMap\u0026lt;\u0026gt;(); results.put(\u0026#34;Sinister\u0026#34;, \u0026#34;Left\u0026#34;); results.put(\u0026#34;Medium\u0026#34;, \u0026#34;Middle\u0026#34;); results.put(\u0026#34;Dexter\u0026#34;, \u0026#34;Right\u0026#34;); return results; } } Philosophically, what\u0026rsquo;s important here is that the key-value pairs are turned into independent Button controls work on their own. These can then be placed anywhere on the scene without requiring any modification of anything else.\nYou can also see how the createButton() method does not need any modification to work as a translation routine to turn each key-value pair into a JavaFX Node.\nOne Last Fix # There\u0026rsquo;s one dependency on the screen that is still hanging around, and that\u0026rsquo;s the direct update of myLabel.setText(). This is forcing the myLabel element to be a field in the class. It also forces each Button to be aware of the presence of the myLabel screen element, which is more needless coupling at the GUI layout level.\nThis can be fixed by creating a StringProperty to hold the translation, and then binding the Text property of myLabel to that StringProperty. This may seem like nit-picking, but if you were building this screen with an MVC structure, then both the Map of words and translations and the translation for the word associated with a clicked button would be defined in the Model. This would meant that the buttons and the translation label are coupled to the Model, as they should be, and not to each other:\npublic class Translator extends Application { private final StringProperty translation = new SimpleStringProperty(\u0026#34;\u0026#34;); public static void main(String[] args) { launch(args); } @Override public void start(Stage primaryStage) { Label myLabel = new Label(); myLabel.textProperty().bind(translation); VBox vBox = new VBox(20, myLabel); vBox.getChildren() .addAll(initializeData().entrySet() .stream() .map(entry -\u0026gt; createButton(entry.getKey(), entry.getValue())) .collect(Collectors.toList())); vBox.setAlignment(Pos.CENTER); primaryStage.setScene(new Scene(vBox, 300, 500)); primaryStage.setTitle(\u0026#34;Latin Translator\u0026#34;); primaryStage.show(); } private Node createButton(String buttonText, String translationText) { Button button = new Button(buttonText); button.setOnAction(evt -\u0026gt; translation.set(translationText)); return button; } private Map\u0026lt;String, String\u0026gt; initializeData() { Map\u0026lt;String, String\u0026gt; results = new HashMap\u0026lt;\u0026gt;(); results.put(\u0026#34;Sinister\u0026#34;, \u0026#34;Left\u0026#34;); results.put(\u0026#34;Medium\u0026#34;, \u0026#34;Middle\u0026#34;); results.put(\u0026#34;Dexter\u0026#34;, \u0026#34;Right\u0026#34;); return results; } } And that\u0026rsquo;s all. This has gone from something clumsy enough that the original author couldn\u0026rsquo;t be bothered to complete the coding for all of the Buttons in the sample to something so simple that it was trivial to simulate how it would work integrated with a database. Most importantly, by making the Buttons stand-alone controls, the layout of the screen is now completely decoupled from the actions of the screen. Those Buttons don\u0026rsquo;t even have to be Buttons anymore, and myLabel could change to some other kind of Node.\n","date":"19 April 2021","externalUrl":null,"permalink":"/javafx/techniques/dispatchEventHandler/","section":"JavaFX","summary":"The “Dispatch EventHandler”. For some reason I see this in examples on the web all over the place, I think because it’s a technique that seems to integrate with FXML. The idea is that you create a single EventHandler and you invoke it from all of the Buttons (or whatever) on your GUI. In that EventHandler, you check the passed Event and run different code depending on which Button triggered it.\nIt’s a bad approach that adds complexity to your code and returns you nothing. In this article you’ll see how this approach fails, and how to properly write EventHandlers in a clear and simple way.\n","title":"JavaFX Anti-Pattern: The Dispatch EventHandler","type":"javafx"},{"content":"","externalUrl":null,"permalink":"/series/absolute-beginners-guide/","section":"Series","summary":"","title":"Absolute Beginner's Guide","type":"series"},{"content":" Introduction # So far we\u0026rsquo;ve built an application that does little, but demonstrates a lot. Now we\u0026rsquo;re going to write one of the most common types of application out there - CRUD (Create, Retrieve, Update and Delete).\nOur example is going to be something super common, some sort of a customer database where we can add new customers, look them up in a number of ways, and then modify or delete them.\nSoftware Architecture # Most software applications have a structure that looks something like this:\nThe top three horizontal boxes (in green hues) represent the GUI elements of the application. Individual Labels, TextFields, ComboBoxes and such are at the top, then actual layouts that contain them. The bottom layer of the GUI is the part that contains the mechanics. These are the things that control actions from the GUI, instantiate the GUI and, of course, the State.\nThe blue layer is the business logic layer. This is the part that contains the logic which is specific to whatever function the GUI supports.\nUnder that, in pink colours are the \u0026ldquo;external\u0026rdquo; elements that connect your application to other applications, the persistence layer and calls to external API\u0026rsquo;s. The \u0026ldquo;Brokers\u0026rdquo; layer is responsible for fetching data from these other services and packaging the results into \u0026ldquo;domain objects\u0026rdquo;, which can then be dealt with by the Business Logic layer.\nFeatures and Scope # The most important thing, is that when you develop an application you implement one feature at a time, and feature always cuts a vertical slice through the application structure - from screen widget right through to external API call. When you expand the scope of a feature, you are cutting a wider slice through the entire application structure.\nWhat this means is that you can\u0026rsquo;t just add a TextField to a screen and not create the rest of the structure to handle the data in that TextField right down to your database. On on the other end, if you\u0026rsquo;re also creating the database, you can\u0026rsquo;t add fields to it without creating the Brokers and DAO\u0026rsquo;s and business logic to pass it to the GUI and put it on the screen.\nYou build your application by starting with a single feature, no matter how small, and implement it from top to bottom in the application structure. And all the way, you stick to YAGNI (You Ain\u0026rsquo;t Gonna Need It) as your guiding principle. This means that you ONLY build things directly required to implement the feature that you\u0026rsquo;re implementing. No peeking ahead.\nAs you continue to build your application, you can expand the scope of a feature, or add new features (very often this is just a matter of semantics). All the while, making sure that you build out each feature from top to bottom of the application structure, and only building the things you need.\nThis is the process we\u0026rsquo;re going to follow to create our Customer Database application.\nTesting # The unfortunate truth is that GUI\u0026rsquo;s are problematic to test. There are automated tools, but they are complicated and difficult to set up. The best thing to do is test by hand.\nHowever, the rest of your system can, and should, be tested, and if your architecture allows for a clean separation between the GUI and everything else (and ours will), you can do unit testing easily.\n","externalUrl":null,"permalink":"/javafx/beginners/crud/crud/","section":"JavaFX","summary":"Introduction # So far we’ve built an application that does little, but demonstrates a lot. Now we’re going to write one of the most common types of application out there - CRUD (Create, Retrieve, Update and Delete).\n","title":"Moving On - Building A CRUD Application","type":"javafx"}]